feat: make each plugin an npm workspace built by a termix-plugin CLI

This commit is contained in:
LukeGus committed 2026-09-22 15:09:50 -05:00
1 parent 4758016f41
commit 5796186a1c
448 files changed
+3087 -2003

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
# Changelog
## 1.0.0
Bundled with Termix 2.9.0.
-83
View File
@@ -1,83 +0,0 @@
/**
* 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;
}
+1
View File
@@ -0,0 +1 @@
{}
-2
View File
@@ -37,8 +37,6 @@
}
]
},
"backend": "backend/index.mjs",
"frontend": "frontend/index.mjs",
"locales": "locales",
"platforms": ["linux", "win32", "darwin"]
}
+15
View File
@@ -0,0 +1,15 @@
{
"name": "@termix-plugin/ssh-terminal",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "termix-plugin build",
"test": "termix-plugin test",
"typecheck": "tsc -p tsconfig.json",
"validate": "termix-plugin validate"
},
"devDependencies": {
"@termix/plugin-sdk": "*"
}
}
+21
View File
@@ -0,0 +1,21 @@
import type { PluginContext } from "@termix/plugin-sdk/backend";
import {
TERMINAL_WS_PORT,
sessionManager,
startTerminalServer,
stopTerminalServer,
} from "../../../../src/backend/hosts/terminal/index.js";
export async function activate(ctx: PluginContext) {
await 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", sessionManager ?? null);
ctx.log.info(`SSH terminal listening on ${TERMINAL_WS_PORT ?? 30002}`);
}
export async function deactivate() {
await stopTerminalServer();
}
+10
View File
@@ -0,0 +1,10 @@
{
"extends": "@termix/plugin-sdk/tsconfig.plugin.json",
"compilerOptions": {
"paths": {
"@/types/*": ["../../src/types/*"],
"@/*": ["../../src/ui/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx"]
}
+3
View File
@@ -0,0 +1,3 @@
import { pluginVitestConfig } from "@termix/plugin-sdk/vitest-preset";
export default pluginVitestConfig(import.meta.url);