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, []);});
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, []);
});