mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
Adds a hardcoded process:transport-owner tier so the terminal can own its WebSocket server and ssh2 clients, which cannot cross the worker boundary.
84 lines
3.1 KiB
JavaScript
84 lines
3.1 KiB
JavaScript
/**
|
|
* SSH Terminal - first-party, in-process plugin.
|
|
*
|
|
* This plugin runs on the main thread rather than in a worker, because it IS
|
|
* the SSH transport rather than a consumer of one: it owns a WebSocket server
|
|
* and long-lived ssh2 clients, and neither a live socket nor an ssh2 Client can
|
|
* cross the structured-clone postMessage boundary a worker plugin talks over.
|
|
* See src/backend/plugins/first-party.ts for why that tier is a hardcoded
|
|
* allowlist and not something a manifest can ask for.
|
|
*
|
|
* The terminal's implementation stays in core (src/backend/hosts/terminal/)
|
|
* rather than being copied in here. Two reasons:
|
|
* - session-manager has six consumers outside the terminal (terminal routes,
|
|
* open-tabs, user-image-storage, collab, and session-sharing twice), so it
|
|
* is shared infrastructure, not plugin-private code.
|
|
* - the terminal needs plaintext host credentials to connect, which ctx.hosts
|
|
* deliberately never returns (PluginHostView is a 13-field allowlist that
|
|
* excludes password, key and passphrase).
|
|
*
|
|
* What this plugin owns is the lifecycle: enabling it starts the WS server,
|
|
* disabling it closes live sessions and frees the port.
|
|
*/
|
|
|
|
import { createRequire } from "node:module";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
/**
|
|
* Resolves the compiled core terminal module.
|
|
*
|
|
* The plugin ships beside dist/backend, so in a built server the module is at
|
|
* dist/backend/backend/hosts/terminal/index.js. Under vitest and dev the
|
|
* plugin is loaded from the repo instead, where the source .ts is the thing
|
|
* that exists and tsx is already handling it.
|
|
*/
|
|
async function loadTerminalModule() {
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const require = createRequire(import.meta.url);
|
|
|
|
const candidates = [
|
|
// Built: plugins/ssh-terminal/backend -> dist/backend/backend/...
|
|
path.resolve(here, "../../../backend/backend/hosts/terminal/index.js"),
|
|
// Repo layout, compiled output present.
|
|
path.resolve(here, "../../../dist/backend/backend/hosts/terminal/index.js"),
|
|
// Repo layout, running from source under tsx.
|
|
path.resolve(here, "../../../src/backend/hosts/terminal/index.ts"),
|
|
];
|
|
|
|
for (const candidate of candidates) {
|
|
try {
|
|
require.resolve(candidate);
|
|
} catch {
|
|
continue;
|
|
}
|
|
return import(new URL(`file://${candidate.replace(/\\/g, "/")}`).href);
|
|
}
|
|
|
|
throw new Error(
|
|
`ssh-terminal could not locate the terminal module. Looked in:\n ${candidates.join("\n ")}`,
|
|
);
|
|
}
|
|
|
|
let terminal = null;
|
|
|
|
export async function activate(ctx) {
|
|
terminal = await loadTerminalModule();
|
|
|
|
await terminal.startTerminalServer();
|
|
|
|
// Published so collab and session-sharing can reach live sessions without
|
|
// importing a plugin. Revoked automatically when this plugin is disabled.
|
|
ctx.registry.provide("terminal.sessions", terminal.sessionManager ?? null);
|
|
|
|
ctx.log.info(
|
|
`SSH terminal listening on ${terminal.TERMINAL_WS_PORT ?? 30002}`,
|
|
);
|
|
}
|
|
|
|
export async function deactivate() {
|
|
if (!terminal) return;
|
|
await terminal.stopTerminalServer();
|
|
terminal = null;
|
|
}
|