Files
NetMesh/electron/plugins/runtimePeer.test.cjs
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

619 lines
23 KiB
JavaScript

"use strict";
const assert = require("node:assert/strict");
const test = require("node:test");
const { MessageChannel } = require("node:worker_threads");
const { PluginRpcError, PluginRpcRouter, RPC_ERRORS } = require("./rpcRouter.cjs");
test("runtime peer exposes secure host capabilities over the canonical RPC transport", async () => {
const { startPluginRuntime } = await import("./runtime/runtimePeer.mjs");
const { port1, port2 } = new MessageChannel();
const values = new Map();
const calls = [];
const deadlines = [];
const host = new PluginRpcRouter({
pluginId: "com.example.peer",
send(message) { port1.postMessage(message); },
handlers: {
"storage.get": async () => {
throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Invalid storage key");
},
"storage.set": async ({ key, value }) => { values.set(key, value); return null; },
"secrets.set": async ({ key, value }) => {
calls.push(["secret", key, value]);
return {
secret: { kind: "secret", id: "secret-reference-0000000000000000", key },
};
},
"secrets.get": async () => ({
found: true,
secret: { kind: "secret", id: "secret-reference-0000000000000000", key: "api-key" },
}),
"credentials.createLease": async ({ operationId }) => ({
kind: "secret-lease",
id: "l".repeat(32),
operationId,
expiresAt: 10_000,
}),
"network.request": async ({ url, timeoutMs }, requestContext) => {
deadlines.push(["network", requestContext.deadlineMs, timeoutMs]);
return {
url,
status: 200,
headers: {},
body: { encoding: "base64", data: "b2s=" },
};
},
"filesystem.readFile": async ({ path }) => ({ data: `read:${path}` }),
"filesystem.writeFile": async ({ path, data }) => { calls.push(["write", path, data]); return null; },
"filesystem.stat": async () => ({ kind: "file", size: 2, modifiedAt: 1 }),
"filesystem.readDirectory": async () => ({ entries: [{ name: "file", kind: "file" }] }),
"companion.start": async () => ({ handleId: "companion-handle-000000000000" }),
"companion.request": async ({ method, params, timeoutMs }, requestContext) => {
deadlines.push(["companion", requestContext.deadlineMs, timeoutMs]);
return { method, params };
},
"companion.stop": async ({ handleId }) => { calls.push(["stop", handleId]); return null; },
"log.write": async () => null,
},
});
let resolveUnhandledStreamCancellation;
let rejectUnhandledStreamCancellation;
const unhandledStreamCancellation = new Promise((resolve, reject) => {
resolveUnhandledStreamCancellation = resolve;
rejectUnhandledStreamCancellation = reject;
});
port1.on("message", (message) => {
const accepted = host.accept(message);
if (message?.frame?.streamId === "unhandled" && message.frame.kind === "cancel") {
accepted.then(resolveUnhandledStreamCancellation, rejectUnhandledStreamCancellation);
}
});
const lifecycle = [];
const peer = await startPluginRuntime({
port: port2,
config: {
pluginId: "com.example.peer",
pluginVersion: "1.0.0",
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
enabledFeatures: [],
},
async loadPlugin() {
return {
default: {
async activate(context) {
lifecycle.push("activate");
await assert.rejects(
context.storage.get("invalid"),
(error) => error?.code === "invalid_argument"
&& error?.message === "Invalid storage key",
);
await context.storage.set("answer", 42);
const secret = await context.secrets.set("api-key", "value");
const lease = await context.credentials.createLease(secret, {
operationId: "network:login",
purpose: "Authenticate",
});
assert.equal(lease.operationId, "network:login");
const vaultLease = await context.credentials.createLease({
kind: "credential",
id: "vault-credential-1",
}, {
operationId: "connection:login",
purpose: "Authenticate connection",
});
assert.equal(vaultLease.operationId, "connection:login");
assert.equal((await context.network.request({
url: "https://example.com",
timeoutMs: 45_000,
})).status, 200);
assert.equal(await context.filesystem.readFile("/tmp/file"), "read:/tmp/file");
await context.filesystem.writeFile("/tmp/file", "ok", { overwrite: true });
assert.equal((await context.filesystem.stat("/tmp/file")).kind, "file");
assert.equal((await context.filesystem.readDirectory("/tmp")).length, 1);
const companion = await context.companions.start("com.example.peer.helper");
assert.deepEqual(await companion.request("echo", { value: 1 }, { timeoutMs: 50_000 }), {
method: "echo",
params: { value: 1 },
});
await companion.stop();
},
async deactivate() { lifecycle.push("deactivate"); },
},
};
},
});
const initialized = await host.request("plugin.initialize", {
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
supportedFeatures: [],
});
assert.equal(initialized.pluginId, "com.example.peer");
const unhandledStream = await host.streams.openOutgoing("unhandled", 1024);
await unhandledStreamCancellation;
await assert.rejects(unhandledStream.write(new Uint8Array([1])), /closed/);
await assert.rejects(
host.request("plugin.unknown", {}),
(error) => error?.code === -32012 && error?.data?.pluginCode === "unsupported",
);
await host.request("plugin.activate", {});
assert.equal(values.get("answer"), 42);
assert.deepEqual(calls, [
["secret", "api-key", "value"],
["write", "/tmp/file", "ok"],
["stop", "companion-handle-000000000000"],
]);
assert.deepEqual(deadlines, [
["network", 45_000, 45_000],
["companion", 50_000, 50_000],
]);
await host.request("plugin.deactivate", {});
await peer.dispose();
host.close();
assert.deepEqual(lifecycle, ["activate", "deactivate"]);
});
test("runtime companion handles retry a failed stop request", async () => {
const { startPluginRuntime } = await import("./runtime/runtimePeer.mjs");
const { port1, port2 } = new MessageChannel();
let stopCalls = 0;
const host = new PluginRpcRouter({
pluginId: "com.example.retry-stop",
send(message) { port1.postMessage(message); },
handlers: {
"companion.start": async () => ({ handleId: "companion-handle-000000000001" }),
"companion.stop": async () => {
stopCalls += 1;
if (stopCalls === 1) throw new Error("transient stop failure");
return null;
},
},
});
port1.on("message", (message) => host.accept(message));
const peer = await startPluginRuntime({
port: port2,
config: {
pluginId: "com.example.retry-stop",
pluginVersion: "1.0.0",
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
enabledFeatures: [],
},
async loadPlugin() {
return {
default: {
async activate(context) {
const companion = await context.companions.start("com.example.retry-stop.helper");
await assert.rejects(companion.stop());
await companion.stop();
},
},
};
},
});
await host.request("plugin.initialize", {
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
supportedFeatures: [],
});
await host.request("plugin.activate", {});
assert.equal(stopCalls, 2);
await peer.dispose();
host.close();
});
test("runtime peer exposes contribution APIs and routes host UI events", async () => {
const { startPluginRuntime } = await import("./runtime/runtimePeer.mjs");
const { port1, port2 } = new MessageChannel();
const calls = [];
const host = new PluginRpcRouter({
pluginId: "com.example.ui",
send(message) { port1.postMessage(message); },
handlers: {
"settings.get": async ({ settingId }) => ({ found: true, value: `value:${settingId}` }),
"settings.update": async ({ settingId, value }) => {
calls.push(["setting", settingId, value]);
return { restartRequired: false };
},
"commands.execute": async ({ command, args }) => {
calls.push(["execute", command, args]);
return "host-result";
},
"contextKeys.set": async ({ key, value }) => { calls.push(["context", key, value]); return null; },
"views.getState": async ({ viewId, scopeId }) => ({ state: { viewId, scopeId } }),
"views.setState": async ({ viewId, scopeId, state }) => {
calls.push(["view-state", viewId, scopeId, state]);
return null;
},
"views.postMessage": async ({ viewId, message }) => { calls.push(["view-message", viewId, message]); },
},
});
port1.on("message", (message) => host.accept(message));
const events = [];
let pluginContext;
const peer = await startPluginRuntime({
port: port2,
config: {
pluginId: "com.example.ui",
pluginVersion: "1.0.0",
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
enabledFeatures: [],
environment: {
locale: "en-GB",
theme: "light",
reducedMotion: false,
highContrast: true,
themeTokens: { "--background": "initial" },
},
},
async loadPlugin() {
return {
default: {
async activate(context) {
pluginContext = context;
assert.equal(context.environment.locale, "fr-FR");
assert.equal(context.environment.theme, "dark");
assert.equal(context.environment.highContrast, true);
assert.deepEqual(context.environment.themeTokens, { "--background": "active" });
assert.throws(() => {
context.environment.themeTokens["--background"] = "mutated";
}, /read only/u);
context.settings.onDidChange((event) => events.push(["settings", event.settingId]));
context.environment.onDidChange((event) => events.push([
"environment",
event.locale,
event.theme,
event.themeTokens["--background"],
]));
context.views.onDidReceiveMessage("com.example.ui.view", (message) => events.push(["view", message]));
context.commands.registerCommand("com.example.ui.hello", async (args, invocation) => ({ args, source: invocation.source }));
context.commands.registerCommand("com.example.ui.void", async () => undefined);
const staleDisposable = context.commands.registerCommand("com.example.ui.replaceable", async () => "old");
staleDisposable.dispose();
context.commands.registerCommand("com.example.ui.replaceable", async () => "new");
staleDisposable.dispose();
assert.equal(await context.settings.get("com.example.ui.greeting"), "value:com.example.ui.greeting");
assert.deepEqual(await context.settings.update("com.example.ui.greeting", "hello"), { restartRequired: false });
assert.equal(await context.commands.executeCommand("com.example.ui.hello", { nested: true }), "host-result");
await context.contextKeys.set("com.example.ui.ready", true);
assert.deepEqual(await context.views.getState("com.example.ui.view", "window-1"), {
viewId: "com.example.ui.view",
scopeId: "window-1",
});
await context.views.setState("com.example.ui.view", "window-1", { selected: 2 });
context.views.postMessage("com.example.ui.view", { ready: true });
},
},
};
},
});
await host.request("plugin.initialize", {
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
supportedFeatures: [],
});
await host.request("plugin.activate", {
environment: {
locale: "fr-FR",
theme: "dark",
reducedMotion: true,
highContrast: true,
themeTokens: { "--background": "active" },
},
});
assert.deepEqual(await host.request("plugin.command.execute", {
command: "com.example.ui.hello",
args: { name: "Catty" },
invocation: { source: "palette" },
}), { args: { name: "Catty" }, source: "palette" });
assert.equal(await host.request("plugin.command.execute", {
command: "com.example.ui.void",
invocation: { source: "palette" },
}), null);
assert.equal(await host.request("plugin.command.execute", {
command: "com.example.ui.replaceable",
invocation: { source: "palette" },
}), "new");
host.notify("plugin.settings.changed", { settingId: "com.example.ui.greeting", scope: "application", scopeId: "application", source: "host" });
host.notify("plugin.environment.changed", {
locale: "zh-CN",
theme: "dark",
reducedMotion: true,
highContrast: false,
themeTokens: { "--background": "updated" },
});
host.notify("plugin.view.message", { viewId: "com.example.ui.view", message: { ping: true } });
await new Promise((resolve) => setImmediate(resolve));
assert.equal(pluginContext.environment.locale, "zh-CN");
assert.equal(pluginContext.environment.reducedMotion, true);
assert.deepEqual(pluginContext.environment.themeTokens, { "--background": "updated" });
assert.deepEqual(events, [
["settings", "com.example.ui.greeting"],
["environment", "zh-CN", "dark", "updated"],
["view", { ping: true }],
]);
assert.deepEqual(calls, [
["setting", "com.example.ui.greeting", "hello"],
["execute", "com.example.ui.hello", { nested: true }],
["context", "com.example.ui.ready", true],
["view-state", "com.example.ui.view", "window-1", { selected: 2 }],
["view-message", "com.example.ui.view", { ready: true }],
]);
await peer.dispose();
host.close();
});
test("runtime peer registers provider handlers and routes immutable terminal lifecycle events", async () => {
const { startPluginRuntime } = await import("./runtime/runtimePeer.mjs");
const { port1, port2 } = new MessageChannel();
const host = new PluginRpcRouter({
pluginId: "com.example.provider",
send(message) { port1.postMessage(message); },
});
port1.on("message", (message) => host.accept(message));
const invocations = [];
const terminalEvents = [];
let cancellationObservedResolve;
const cancellationObserved = new Promise((resolve) => {
cancellationObservedResolve = resolve;
});
const peer = await startPluginRuntime({
port: port2,
config: {
pluginId: "com.example.provider",
pluginVersion: "1.0.0",
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
enabledFeatures: [],
},
async loadPlugin() {
return {
default: {
async activate(context) {
context.terminals.onDidChange((event) => terminalEvents.push(event));
const staleDisposable = context.providers.register(
"com.example.provider.completion",
"terminal.completion",
async () => "stale",
);
staleDisposable.dispose();
context.providers.register(
"com.example.provider.completion",
"terminal.completion",
async (invocation) => {
invocations.push(invocation);
if (invocation.operation === "wait") {
await new Promise((resolve) => {
invocation.cancellationToken.onCancellationRequested(() => {
cancellationObservedResolve();
resolve();
});
});
}
return { items: [{ label: "status" }] };
},
);
staleDisposable.dispose();
},
},
};
},
});
await host.request("plugin.initialize", {
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
supportedFeatures: [],
});
await host.request("plugin.activate", {});
assert.deepEqual(await host.request("provider.invoke", {
providerId: "com.example.provider.completion",
kind: "terminal.completion",
operation: "provide",
requestId: "request-1",
payload: { input: "sta" },
deadlineMs: 1_000,
}), {
requestId: "request-1",
status: "ok",
result: { items: [{ label: "status" }] },
});
assert.equal(invocations.length, 1);
assert.equal(invocations[0].providerId, "com.example.provider.completion");
assert.equal(invocations[0].kind, "terminal.completion");
assert.equal(invocations[0].operation, "provide");
assert.equal(invocations[0].deadlineMs, 1_000);
assert.equal(invocations[0].cancellationToken.isCancellationRequested, false);
assert.equal(Object.isFrozen(invocations[0].payload), true);
await assert.rejects(
host.request("provider.invoke", {
providerId: "com.example.provider.completion",
kind: "terminal.hover",
operation: "provide",
requestId: "request-kind-mismatch",
}),
(error) => error?.code === RPC_ERRORS.failedPrecondition,
);
const event = {
type: "cwdChanged",
session: {
sessionId: "session-1",
protocol: "ssh",
status: "connected",
cwd: "/srv/app",
},
};
host.notify("plugin.terminal.event", event);
await new Promise((resolve) => setImmediate(resolve));
assert.deepEqual(terminalEvents, [event]);
assert.equal(Object.isFrozen(terminalEvents[0]), true);
assert.equal(Object.isFrozen(terminalEvents[0].session), true);
const controller = new AbortController();
const cancelledRequest = host.request("provider.invoke", {
providerId: "com.example.provider.completion",
kind: "terminal.completion",
operation: "wait",
requestId: "request-cancelled",
}, { signal: controller.signal });
await new Promise((resolve) => setImmediate(resolve));
controller.abort();
await assert.rejects(cancelledRequest, (error) => error?.code === RPC_ERRORS.cancelled);
await cancellationObserved;
await peer.dispose();
host.close();
});
test("runtime peer routes connection and importer Provider operation maps", async () => {
const { startPluginRuntime } = await import("./runtime/runtimePeer.mjs");
const { port1, port2 } = new MessageChannel();
const host = new PluginRpcRouter({
pluginId: "com.example.connection",
send(message) { port1.postMessage(message); },
});
port1.on("message", (message) => host.accept(message));
const invocations = [];
let legacyError;
let legacyImporterError;
const peer = await startPluginRuntime({
port: port2,
config: {
pluginId: "com.example.connection",
pluginVersion: "1.0.0",
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
enabledFeatures: [],
},
async loadPlugin() {
return {
default: {
async activate(context) {
try {
context.providers.register("com.example.connection.legacy", "connection", async () => null);
} catch (error) {
legacyError = { code: error?.code, message: error?.message };
}
context.providers.register("com.example.connection.transport", "connection", {
validateConfiguration(invocation) {
invocations.push([invocation.operation, invocation.payload.configuration]);
return { valid: true, issues: [] };
},
probe(invocation) {
invocations.push([invocation.operation, invocation.payload.configuration]);
return { available: true };
},
open() {
throw new Error("open is not invoked by this test");
},
resize(invocation) {
invocations.push([invocation.operation, invocation.payload.columns, invocation.payload.rows]);
return null;
},
signal(invocation) {
invocations.push([invocation.operation, invocation.payload.signal]);
return null;
},
reconnect(invocation) {
invocations.push([invocation.operation, invocation.payload.connectionId]);
return null;
},
close(invocation) {
invocations.push([invocation.operation, invocation.payload.connectionId]);
return null;
},
getStatus(invocation) {
invocations.push([invocation.operation, invocation.payload.connectionId]);
return { status: "connected" };
},
});
try {
context.providers.register("com.example.connection.legacyImporter", "importer", async () => null);
} catch (error) {
legacyImporterError = { code: error?.code, message: error?.message };
}
context.providers.register("com.example.connection.importer", "importer", {
detect(invocation) {
invocations.push([invocation.operation, invocation.payload.fileName]);
return { confidence: 0.75, format: "example" };
},
parse() {
throw new Error("parse is not invoked by this test");
},
});
},
},
};
},
});
await host.request("plugin.initialize", {
netcattyVersion: "1.0.0",
apiVersion: "0.1.0-internal",
supportedFeatures: [],
});
await host.request("plugin.activate", {});
assert.deepEqual(legacyError, {
code: "invalid_argument",
message: "Connection Provider handler must be an operation map",
});
assert.deepEqual(legacyImporterError, {
code: "invalid_argument",
message: "Importer Provider handler must be an operation map",
});
assert.deepEqual(await host.request("provider.invoke", {
providerId: "com.example.connection.transport",
kind: "connection",
operation: "resize",
requestId: "connection-resize-1",
payload: { connectionId: "connection-1", operationId: "operation-1", columns: 132, rows: 44 },
deadlineMs: 1_000,
}), {
requestId: "connection-resize-1",
status: "ok",
result: null,
});
assert.deepEqual(await host.request("provider.invoke", {
providerId: "com.example.connection.importer",
kind: "importer",
operation: "detect",
requestId: "importer-detect-1",
payload: {
fileName: "hosts.json",
sample: { encoding: "base64", data: "e30=" },
},
deadlineMs: 1_000,
}), {
requestId: "importer-detect-1",
status: "ok",
result: { confidence: 0.75, format: "example" },
});
assert.deepEqual(await host.request("provider.invoke", {
providerId: "com.example.connection.transport",
kind: "connection",
operation: "getStatus",
requestId: "connection-status-1",
payload: { connectionId: "connection-1", operationId: "operation-2" },
deadlineMs: 1_000,
}), {
requestId: "connection-status-1",
status: "ok",
result: { status: "connected" },
});
assert.deepEqual(invocations, [
["resize", 132, 44],
["detect", "hosts.json"],
["getStatus", "connection-1"],
]);
await peer.dispose();
host.close();
});