(options: {
args: readonly string[];
cwd: string;
env: NodeJS.ProcessEnv;
input: string;
timeoutMs: number;
tailBytes?: number;
outputFileBytes?: number;
stdoutPath?: string;
stderrPath?: string;
appServer?: CodexAppServerProcessOptions;
})
| 69 | } |
| 70 | |
| 71 | export function runCodexProcess(options: { |
| 72 | args: readonly string[]; |
| 73 | cwd: string; |
| 74 | env: NodeJS.ProcessEnv; |
| 75 | input: string; |
| 76 | timeoutMs: number; |
| 77 | tailBytes?: number; |
| 78 | outputFileBytes?: number; |
| 79 | stdoutPath?: string; |
| 80 | stderrPath?: string; |
| 81 | appServer?: CodexAppServerProcessOptions; |
| 82 | }): CodexProcessResult { |
| 83 | const workDir = mkdtempSync(join(tmpdir(), "clawsweeper-codex-process-")); |
| 84 | const optionsPath = join(workDir, "options.json"); |
| 85 | const resultPath = join(workDir, "result.json"); |
| 86 | const stdoutPath = options.stdoutPath ?? join(workDir, "stdout.log"); |
| 87 | const stderrPath = options.stderrPath ?? join(workDir, "stderr.log"); |
| 88 | try { |
| 89 | writeFileSync( |
| 90 | optionsPath, |
| 91 | JSON.stringify({ |
| 92 | args: [...options.args], |
| 93 | command: codexProcessCommand(options.env), |
| 94 | timeoutMs: options.timeoutMs, |
| 95 | resultPath, |
| 96 | stdoutPath, |
| 97 | stderrPath, |
| 98 | tailBytes: normalizedTailBytes(options.tailBytes), |
| 99 | maxOutputFileBytes: normalizedOutputFileBytes(options.outputFileBytes), |
| 100 | ...(options.appServer ? { appServer: options.appServer } : {}), |
| 101 | }), |
| 102 | { encoding: "utf8", mode: 0o600 }, |
| 103 | ); |
| 104 | const workerPath = options.appServer ? CODEX_APP_SERVER_WORKER_PATH : CODEX_PROCESS_WORKER_PATH; |
| 105 | const worker = spawnSync(process.execPath, [workerPath, optionsPath], { |
| 106 | cwd: options.cwd, |
| 107 | env: options.env, |
| 108 | input: options.input, |
| 109 | stdio: ["pipe", "ignore", "ignore"], |
| 110 | timeout: options.timeoutMs + 10_000, |
| 111 | }); |
| 112 | if (existsSync(resultPath)) { |
| 113 | const result = deserializeProcessResult(JSON.parse(readFileSync(resultPath, "utf8"))); |
| 114 | return worker.error ? { ...result, error: worker.error } : result; |
| 115 | } |
| 116 | if (worker.error) return failedProcessResult(worker.error, worker.status, worker.signal); |
| 117 | return failedProcessResult( |
| 118 | new Error( |
| 119 | `Codex process worker failed with exit ${worker.status ?? "unknown"} and did not write a result.`, |
| 120 | ), |
| 121 | worker.status, |
| 122 | worker.signal, |
| 123 | ); |
| 124 | } catch (error) { |
| 125 | return failedProcessResult(error instanceof Error ? error : new Error(String(error))); |
| 126 | } finally { |
| 127 | rmSync(workDir, { recursive: true, force: true }); |
| 128 | } |
no test coverage detected