| | @@ -344,39 +344,63 @@ |
| 344 | 344 | }); |
| 345 | 345 | afterEach(() => srv.close()); |
| 346 | 346 | |
| 347 | | - /** A codex that records its calls and answers `mcp get` with `server` (none: exit status 1, as codex does). */ |
| 347 | + /** |
| 348 | + * A codex that records its calls. `mcp get` answers `server` (none: exit status 1, as codex does) until |
| 349 | + * config.toml names a URL, then that URL; `mcp remove` drops the config's diffinery table. |
| 350 | + */ |
| 348 | 351 | function fakeCodex(server: unknown): { dir: string; calls: () => string[] } { |
| 349 | 352 | const dir = tempDir("diffinery-fake-codex-"); |
| 350 | 353 | const log = path.join(dir, "calls"); |
| 351 | 354 | writeFileSync(path.join(dir, "answer.json"), JSON.stringify(server ?? null)); |
| 352 | 355 | writeFileSync( |
| 353 | 356 | path.join(dir, "codex"), |
| 354 | | - `#!/bin/sh\nprintf '%s\\n' "$*" >> "${log}"\nif [ "$1 $2" = "mcp get" ]; then ${server ? `cat "${dir}/answer.json"` : "echo 'Error: No MCP server named' >&2; exit 1"}; fi\nexit 0\n`, |
| 357 | + [ |
| 358 | + "#!/bin/sh", |
| 359 | + `printf '%s\\n' "$*" >> "${log}"`, |
| 360 | + 'cfg="$CODEX_HOME/config.toml"', |
| 361 | + 'if [ "$1 $2" = "mcp remove" ]; then sed -i.bak "/mcp_servers.diffinery/,\\$d" "$cfg"; exit 0; fi', |
| 362 | + 'if [ "$1 $2" = "mcp get" ]; then', |
| 363 | + ' u=$(sed -n \'s/^url = "\\(.*\\)"$/\\1/p\' "$cfg" 2>/dev/null | tail -n 1)', |
| 364 | + ' b=$(sed -n \'s/^bearer_token_env_var = "\\(.*\\)"$/\\1/p\' "$cfg" 2>/dev/null | tail -n 1)', |
| 365 | + ' if [ -n "$u" ]; then printf \'{"transport":{"url":"%s","bearer_token_env_var":"%s"}}\' "$u" "$b"; exit 0; fi', |
| 366 | + server ? ` cat "${dir}/answer.json"` : " echo 'Error: No MCP server named' >&2; exit 1", |
| 367 | + "fi", |
| 368 | + "exit 0", |
| 369 | + "", |
| 370 | + ].join("\n"), |
| 355 | 371 | ); |
| 356 | 372 | chmodSync(path.join(dir, "codex"), 0o755); |
| 357 | 373 | return { dir, calls: () => (existsSync(log) ? readFileSync(log, "utf8").trim().split("\n") : []) }; |
| 358 | 374 | } |
| 359 | | - const machine = async (codexDir: string) => { |
| 375 | + const machine = async (codexDir: string, config?: string) => { |
| 360 | 376 | const env = isolatedEnv({ DIFFINERY_NO_CODEX: "", PATH: `${codexDir}${path.delimiter}${process.env.PATH}` }); |
| 377 | + env.CODEX_HOME = path.join(env.HOME!, ".codex"); |
| 378 | + if (config !== undefined) { |
| 379 | + mkdirSync(env.CODEX_HOME); |
| 380 | + writeFileSync(path.join(env.CODEX_HOME, "config.toml"), config); |
| 381 | + } |
| 361 | 382 | await writeConfig(env, { url: srv.url, token: "dfy_good" }); |
| 362 | 383 | return env; |
| 363 | 384 | }; |
| 385 | + const tomlOf = (env: NodeJS.ProcessEnv) => readFileSync(path.join(env.CODEX_HOME!, "config.toml"), "utf8"); |
| 364 | 386 | |
| 365 | 387 | it("points Codex's diffinery MCP server at a self-hosted server, keeping a headless token setting", async () => { |
| 366 | 388 | const plugin = fakeCodex({ name: "diffinery", transport: { type: "streamable_http", url: "https://diffinery.com/mcp", bearer_token_env_var: null } }); |
| 367 | | - const r = await run(["setup", "--no-auto-merge"], { env: await machine(plugin.dir) }); |
| 389 | + const env = await machine(plugin.dir, 'model = "o3"'); |
| 390 | + const r = await run(["setup", "--no-auto-merge"], { env }); |
| 368 | 391 | expect(r.code).toBe(0); |
| 369 | 392 | expect(r.stdout).toContain(`✓ Codex: the diffinery MCP server is now ${srv.url}/mcp`); |
| 370 | | - expect(plugin.calls()).toEqual(["mcp get diffinery --json", `mcp add diffinery --url ${srv.url}/mcp`]); |
| 393 | + expect(plugin.calls()).toEqual(["mcp get diffinery --json", "mcp get diffinery --json"]); |
| 394 | + expect(tomlOf(env)).toBe(`model = "o3"\n\n[mcp_servers.diffinery]\nurl = "${srv.url}/mcp"\n`); |
| 395 | + // Again: nothing to change. |
| 396 | + expect((await run(["setup", "--no-auto-merge"], { env })).stdout).toContain(`✓ Codex: the diffinery MCP server is ${srv.url}/mcp`); |
| 371 | 397 | |
| 398 | + // A server of the user's own, sending DIFFINERY_TOKEN: replaced, and still sending it. |
| 372 | 399 | const headless = fakeCodex({ name: "diffinery", transport: { url: "https://old.example/mcp", bearer_token_env_var: "DIFFINERY_TOKEN" } }); |
| 373 | | - await run(["setup", "--no-auto-merge"], { env: await machine(headless.dir) }); |
| 374 | | - expect(headless.calls()[1]).toBe(`mcp add diffinery --url ${srv.url}/mcp --bearer-token-env-var DIFFINERY_TOKEN`); |
| 375 | | - |
| 376 | | - const done = fakeCodex({ name: "diffinery", transport: { url: `${srv.url}/mcp` } }); |
| 377 | | - const again = await run(["setup", "--no-auto-merge"], { env: await machine(done.dir) }); |
| 378 | | - expect(again.stdout).toContain(`✓ Codex: the diffinery MCP server is ${srv.url}/mcp`); |
| 379 | | - expect(done.calls()).toEqual(["mcp get diffinery --json"]); |
| 400 | + const henv = await machine(headless.dir, '[mcp_servers.diffinery]\nurl = "https://old.example/mcp"\nbearer_token_env_var = "DIFFINERY_TOKEN"\n'); |
| 401 | + await run(["setup", "--no-auto-merge"], { env: henv }); |
| 402 | + expect(headless.calls()).toContain("mcp remove diffinery"); |
| 403 | + expect(tomlOf(henv)).toBe(`[mcp_servers.diffinery]\nurl = "${srv.url}/mcp"\nbearer_token_env_var = "DIFFINERY_TOKEN"\n`); |
| 380 | 404 | }); |
| 381 | 405 | |
| 382 | 406 | it("leaves Codex alone for diffinery.com", async () => { |