Something went wrong. Try again.
presents omp as codex for use with chatgpt remote
Something went wrong. Try again.
6.4 kB · 140 lines
JavaScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141import test from "node:test";import assert from "node:assert/strict";import { spawn } from "node:child_process";import { createInterface } from "node:readline";import { fileURLToPath } from "node:url";
const executable = fileURLToPath(new URL("../bin/codex.mjs", import.meta.url));
test("codex --version is parseable", async () => { const child = spawn(process.execPath, [executable, "--version"], { stdio: ["ignore", "pipe", "pipe"] }); let stdout = ""; child.stdout.on("data", (chunk) => { stdout += chunk; }); const code = await new Promise((resolve, reject) => { child.once("error", reject); child.once("exit", resolve); }); assert.equal(code, 0); assert.match(stdout, /^codex-cli \S+\n$/);});
test("stdio app-server performs an end-to-end mock turn", async () => { const fs = await import("node:fs/promises"); const os = await import("node:os"); const path = await import("node:path"); const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-test-")); const child = spawn(process.execPath, [executable, "app-server", "--backend", "mock"], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, OMP_CODEX_QUIET: "1", OMP_CODEX_STATE_FILE: path.join(tempDir, "state.json"), OMP_CODEX_RUNTIME_DIR: tempDir }, }); const messages = []; const waiters = []; const lines = createInterface({ input: child.stdout, crlfDelay: Infinity }); lines.on("line", (line) => { const message = JSON.parse(line); messages.push(message); for (const waiter of [...waiters]) waiter(message); if (message.id !== undefined && message.method?.includes("requestApproval")) { child.stdin.write(`${JSON.stringify({ id: message.id, result: { decision: "accept" } })}\n`); } }); const waitFor = (predicate, timeoutMs = 4000) => { const existing = messages.find(predicate); if (existing) return Promise.resolve(existing); return new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(`timeout; messages=${JSON.stringify(messages)}`)), timeoutMs); const waiter = (message) => { if (!predicate(message)) return; clearTimeout(timer); waiters.splice(waiters.indexOf(waiter), 1); resolve(message); }; waiters.push(waiter); }); }; child.stdin.write(`${JSON.stringify({ id: 1, method: "initialize", params: { clientInfo: { name: "test" } } })}\n`); await waitFor((message) => message.id === 1); child.stdin.write(`${JSON.stringify({ method: "initialized", params: {} })}\n`); child.stdin.write(`${JSON.stringify({ id: 2, method: "thread/start", params: { cwd: process.cwd() } })}\n`); const started = await waitFor((message) => message.id === 2); child.stdin.write(`${JSON.stringify({ id: 3, method: "turn/start", params: { threadId: started.result.thread.id, input: [{ type: "text", text: "cli" }] } })}\n`); await waitFor((message) => message.id === 3); const completed = await waitFor((message) => message.method === "turn/completed"); assert.equal(completed.params.turn.status, "completed"); child.stdin.end(); const code = await new Promise((resolve, reject) => { child.once("error", reject); child.once("exit", resolve); }); assert.equal(code, 0); await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});});
test("a stdio app-server shares the daemon's state instead of opening its own", async () => { const fs = await import("node:fs/promises"); const os = await import("node:os"); const path = await import("node:path"); const { startDaemon, stopDaemon, runtimePaths } = await import("../src/daemon.mjs"); const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "codex-shared-")); const env = { OMP_CODEX_QUIET: "1", OMP_CODEX_BACKEND: "mock", OMP_CODEX_STATE_FILE: path.join(tempDir, "state.json"), OMP_CODEX_RUNTIME_DIR: tempDir, }; const previous = Object.fromEntries(Object.keys(env).map((key) => [key, process.env[key]])); Object.assign(process.env, env);
const open = (args, name) => { const child = spawn(process.execPath, [executable, ...args], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, ...env }, }); const pending = new Map(); createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", (line) => { let message = null; try { message = JSON.parse(line); } catch { return; } const settle = pending.get(message.id); if (!settle) return; pending.delete(message.id); settle(message); }); let nextId = 0; const call = (method, params = {}) => new Promise((resolve, reject) => { const id = (nextId += 1); const timer = setTimeout(() => reject(new Error(`${name} timed out on ${method}`)), 8000); pending.set(id, (message) => { clearTimeout(timer); resolve(message); }); child.stdin.write(`${JSON.stringify({ id, method, params })}\n`); }); return { call, close: () => { child.stdin.end(); child.kill("SIGTERM"); }, ready: call("initialize", { clientInfo: { name } }), }; };
let daemonStarted = false; try { await startDaemon({ executable }); daemonStarted = true;
// Make the daemon load state first: only a shared server can report a // thread created afterwards, since a private one keeps it in its own // snapshot and the daemon never re-reads the file. const overSocket = open(["app-server", "proxy", "--socket", runtimePaths().socket], "ios-like"); const overStdio = open(["app-server"], "desktop-like"); try { await overSocket.ready; await overSocket.call("thread/list", { limit: 5 }); await overStdio.ready; const started = await overStdio.call("thread/start", { cwd: tempDir }); const threadId = started.result.thread.id; const read = await overSocket.call("thread/read", { threadId, includeTurns: false }); assert.equal(read.result?.thread?.id, threadId, `daemon did not see the stdio client's thread: ${JSON.stringify(read.error || read.result)}`); } finally { overStdio.close(); overSocket.close(); } } finally { if (daemonStarted) await stopDaemon().catch(() => {}); for (const [key, value] of Object.entries(previous)) { if (value === undefined) delete process.env[key]; else process.env[key] = value; } await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); }});