203 lines · 7.4 KB · TypeScript
RawDownload
import assert from "node:assert/strict";import { mkdir, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";import { tmpdir } from "node:os";import path from "node:path";import test from "node:test";import { Effect } from "effect";import {  callConnectorTool,  closePooledConnection,  ConnectorToolDeniedError,  listConnectorTools,} from "../../services/agent-runtime/src/connector-pool";import {  connectorsRevisionSync,  listConnectors,  upsertConnector,} from "../../services/agent-runtime/src/connectors-service";import {  listPluginRuntimeViews,  setPluginEnabled,} from "../../services/agent-runtime/src/plugin-runtime"; const fakeServer = `import readline from "node:readline";const input = readline.createInterface({ input: process.stdin });for await (const line of input) {  const message = JSON.parse(line);  if (typeof message.id !== "number") continue;  let result = {};  if (message.method === "initialize") {    result = { protocolVersion: "2025-03-26", capabilities: { tools: {} }, serverInfo: { name: "fake", version: "1" } };  }  if (message.method === "tools/list") {    result = { tools: [      { name: "inspect", inputSchema: { type: "object" }, annotations: { readOnlyHint: true } },      { name: "mutate", inputSchema: { type: "object" }, annotations: { readOnlyHint: false } }    ] };  }  if (message.method === "tools/call") {    result = { content: [{ type: "text", text: JSON.stringify({ tool: message.params.name, cwd: process.cwd() }) }] };  }  process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: message.id, result }) + "\\n");}`; async function createPlugin(root: string, name: string, mcpPath = "./.mcp.json") {  const bundle = path.join(root, name, "1.0.0");  await mkdir(path.join(bundle, ".codex-plugin"), { recursive: true });  await writeFile(    path.join(bundle, ".codex-plugin", "plugin.json"),    JSON.stringify({      name,      version: "1.0.0",      mcpServers: mcpPath,      interface: { displayName: name === "computer-use" ? "Computer Use" : name },    }),  );  return bundle;} test("plugin runtime activates only declared read-only tools and refreshes connector state", async (context) => {  const root = await mkdtemp(path.join(tmpdir(), "local-studio-plugin-runtime-"));  const dataDir = path.join(root, "data");  const pluginRoot = path.join(root, "plugins");  const previousDataDir = process.env.LOCAL_STUDIO_DATA_DIR;  process.env.LOCAL_STUDIO_DATA_DIR = dataDir;  context.after(async () => {    closePooledConnection("plugin-computer-use-computer-use");    if (previousDataDir === undefined) delete process.env.LOCAL_STUDIO_DATA_DIR;    else process.env.LOCAL_STUDIO_DATA_DIR = previousDataDir;    await rm(root, { recursive: true, force: true });  });   const bundle = await createPlugin(pluginRoot, "computer-use");  await writeFile(path.join(bundle, "server.mjs"), fakeServer);  await writeFile(    path.join(bundle, ".mcp.json"),    JSON.stringify({      mcpServers: {        "computer-use": {          command: process.execPath,          args: ["./server.mjs"],          cwd: ".",        },      },    }),  );  const sources = [{ label: "Test", dir: pluginRoot, priority: 1 }];   const initial = await Effect.runPromise(listPluginRuntimeViews(sources));  assert.equal(initial[0]?.tools.state, "available");  const initialRevision = connectorsRevisionSync();   const activated = await Effect.runPromise(setPluginEnabled("computer-use", true, sources));  assert.equal(activated.plugins[0]?.tools.state, "enabled");  assert.equal(activated.plugins[0]?.tools.allowedToolCount, 1);  assert.notEqual(connectorsRevisionSync(), initialRevision);   const connector = (await listConnectors())[0];  assert.equal(connector?.cwd, await realpath(bundle));  assert.deepEqual(connector?.allowTools, ["inspect"]);  assert.deepEqual(connector?.origin, {    kind: "plugin",    id: "computer-use",    version: "1.0.0",    binding: "computer-use",  });   await upsertConnector({    id: "plugin-computer-use-computer-use",    name: "Computer Use",    transport: "stdio",    command: process.execPath,    args: [path.join(bundle, "server.mjs")],    enabled: true,  });  const roundTripped = (await listConnectors())[0];  assert.equal(roundTripped?.cwd, await realpath(bundle));  assert.deepEqual(roundTripped?.allowTools, ["inspect"]);  assert.equal(roundTripped?.origin?.id, "computer-use");   const tools = await listConnectorTools("plugin-computer-use-computer-use");  assert.deepEqual(    tools.map((tool) => tool.name),    ["inspect"],  );  await assert.rejects(    () => callConnectorTool("plugin-computer-use-computer-use", "mutate", {}),    ConnectorToolDeniedError,  );  const result = await callConnectorTool("plugin-computer-use-computer-use", "inspect", {});  assert.match(JSON.stringify(result), /tool.*inspect/);   const deactivated = await Effect.runPromise(setPluginEnabled("computer-use", false, sources));  assert.equal(deactivated.plugins[0]?.tools.state, "disabled");  assert.equal((await listConnectors())[0]?.enabled, false);}); test("plugin runtime rejects manifest paths that escape the bundle", async (context) => {  const root = await mkdtemp(path.join(tmpdir(), "local-studio-plugin-escape-"));  context.after(() => rm(root, { recursive: true, force: true }));  const bundle = await createPlugin(root, "unsafe", "../escape.json");  await writeFile(    path.join(path.dirname(bundle), "escape.json"),    JSON.stringify({ mcpServers: {} }),  );  const plugins = await Effect.runPromise(    listPluginRuntimeViews([{ label: "Test", dir: root, priority: 1 }]),  );  assert.equal(plugins[0]?.tools.state, "invalid");  assert.match(plugins[0]?.tools.reason ?? "", /escapes its bundle/);}); test("only the bundled Chatterbox plugin receives the local speech capability", async (context) => {  const root = await mkdtemp(path.join(tmpdir(), "local-studio-speech-plugin-"));  context.after(() => rm(root, { recursive: true, force: true }));  const bundle = await createPlugin(root, "chatterbox-voice");  await writeFile(    path.join(bundle, ".codex-plugin", "plugin.json"),    JSON.stringify({      name: "chatterbox-voice",      version: "99.0.0",      apps: "./.app.json",    }),  );  await writeFile(    path.join(bundle, ".app.json"),    JSON.stringify({      apps: {        "chatterbox-voice": {          adapter: "local-studio-controller",          capability: "speech",          actions: ["synthesize"],        },      },    }),  );   const untrusted = await Effect.runPromise(    listPluginRuntimeViews([{ label: "Test", dir: root, priority: 99 }]),  );  assert.equal(untrusted[0]?.hostCapability, undefined);  assert.equal(untrusted[0]?.tools.state, "none");   const bundledRoot = path.resolve(import.meta.dirname, "../desktop/resources/plugins");  const bundled = await Effect.runPromise(    listPluginRuntimeViews([{ label: "Local Studio", dir: bundledRoot, priority: 1 }]),  );  const chatterbox = bundled.find((plugin) => plugin.id === "chatterbox-voice");  assert.deepEqual(chatterbox?.hostCapability, {    adapter: "local-studio-controller",    capability: "speech",    actions: ["synthesize"],  });  assert.equal(chatterbox?.tools.state, "none");   const activation = await Effect.runPromise(    setPluginEnabled("chatterbox-voice", true, [      { label: "Local Studio", dir: bundledRoot, priority: 1 },    ]),  );  assert.deepEqual(activation.connectorIds, []);});