#!/usr/bin/env node import { spawn } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createCodexAppServerClient } from './live/codex-app-server-client.mjs'; import { CODEX_CLI_SETUP_URL, CODEX_WORKER_OWNER, codexWorkerProcessStateIsOwned, codexWorkerStateIsOwned, resolveCodexExecutable, resolveCodexWorkerConfig, } from './live/codex-worker.mjs'; import { CodexLiveWorkerSupervisor } from './live/codex-worker-supervisor.mjs'; import { getLiveCodexWorkerStatePath, readLiveServerInfo, resolveLiveConfigPath, } from './lib/impeccable-paths.mjs'; const args = process.argv.slice(2); const cwd = process.cwd(); const scriptPath = fileURLToPath(import.meta.url); const scriptsDir = path.dirname(scriptPath); const statePath = getLiveCodexWorkerStatePath(cwd); if (args.includes('--help') || args.includes('-h')) { console.log(`Usage: node live-codex-worker.mjs [--background [--no-wait] | --status | --stop] Codex Live generation supervisor. It owns a separate app-server process and dedicated worker thread; it never attaches to the foreground desktop task. It is enabled by default when a Codex runtime signal is present. Set IMPECCABLE_LIVE_CODEX_WORKER=0 to use the portable foreground path. Project config may tune the worker but cannot activate it across harnesses. Optional environment: IMPECCABLE_LIVE_CODEX_PROFILE quality (default) or fast IMPECCABLE_LIVE_CODEX_MODEL Model override; otherwise a quality model is selected dynamically IMPECCABLE_LIVE_CODEX_EFFORT Reasoning effort override (default: medium) IMPECCABLE_CODEX_PATH Codex binary path (default: codex) Outside Codex this command exits without polling, leaving the portable foreground Live path unchanged.`); process.exit(0); } if (args.includes('--status')) { const state = readJson(statePath); console.log(JSON.stringify(state ? { ...state, reachable: pidReachable(state.pid) } : { ok: false, status: 'not_started' })); process.exit(0); } if (args.includes('--stop')) { const state = readJson(statePath); if (state?.pid && !codexWorkerProcessStateIsOwned(state, cwd)) { console.log(JSON.stringify({ ok: false, status: 'not_stopped', error: 'codex_worker_state_unowned', })); process.exitCode = 2; process.exit(); } if (!state?.pid || !pidReachable(state.pid)) { console.log(JSON.stringify({ ok: true, status: 'not_running' })); process.exit(0); } process.kill(state.pid, 'SIGTERM'); const stopped = await waitFor( () => !pidReachable(state.pid), positiveInteger(process.env.IMPECCABLE_LIVE_CODEX_STOP_TIMEOUT_MS, 5_000), ); if (!stopped) { console.log(JSON.stringify({ ok: false, status: 'stop_timeout', pid: state.pid })); process.exitCode = 2; process.exit(); } console.log(JSON.stringify({ ok: true, status: 'stopped', pid: state.pid })); process.exit(0); } const liveConfig = readLiveConfig(cwd); const config = resolveCodexWorkerConfig({ env: process.env, liveConfig }); if (!config.enabled) { console.log(JSON.stringify({ ok: false, error: 'codex_worker_disabled', fallback: 'foreground', })); process.exit(0); } if (args.includes('--background')) { const existing = readJson(statePath); if (codexWorkerProcessStateIsOwned(existing, cwd) && existing?.pid && pidReachable(existing.pid) && ['starting', 'ready', 'working'].includes(existing.status)) { console.log(JSON.stringify({ ...existing, ok: true, reused: true })); process.exit(0); } } const executable = resolveCodexExecutable(config.codexPath, { cwd, env: process.env }); if (!executable.available) { const unavailable = writeState({ ok: false, owner: CODEX_WORKER_OWNER, pid: null, status: 'unavailable', mode: 'foreground', error: executable.error, command: executable.command, message: 'Codex CLI not found. Live is using the main agent for generation.', setup: { docsUrl: CODEX_CLI_SETUP_URL, afterInstall: 'codex login', }, }); console.log(JSON.stringify({ ...unavailable, fallback: 'foreground' })); process.exit(0); } config.codexPath = executable.resolvedPath; if (args.includes('--background')) { fs.mkdirSync(path.dirname(statePath), { recursive: true }); const logPath = path.join(path.dirname(statePath), 'codex-worker.log'); const logFd = fs.openSync(logPath, 'a'); const child = spawn(process.execPath, [scriptPath, '--foreground'], { cwd, env: process.env, detached: true, stdio: ['ignore', logFd, logFd], }); child.unref(); fs.closeSync(logFd); const observed = readJson(statePath); const starting = observed?.pid === child.pid && ['ready', 'working'].includes(observed.status) ? observed : writeState({ ok: true, owner: CODEX_WORKER_OWNER, pid: child.pid, status: 'starting', threadId: null, model: config.model, effort: config.effort, profile: config.profile, delivery: config.delivery, }); if (args.includes('--no-wait')) { console.log(JSON.stringify({ ...starting, ok: true, starting: true, logPath })); process.exit(0); } const ready = await waitFor(() => { const state = readJson(statePath); if (state?.pid !== child.pid) return null; if (state.status === 'error') return state; return ['ready', 'working'].includes(state.status) ? state : null; }, positiveInteger(process.env.IMPECCABLE_LIVE_CODEX_START_TIMEOUT_MS, 12_000)); if (!ready || ready.status === 'error') { let terminated = true; if (pidReachable(child.pid)) { process.kill(child.pid, 'SIGTERM'); terminated = Boolean(await waitFor( () => !pidReachable(child.pid), positiveInteger(process.env.IMPECCABLE_LIVE_CODEX_STOP_TIMEOUT_MS, 2_000), )); } console.log(JSON.stringify({ ok: false, error: ready?.error || 'codex_worker_start_timeout', fallback: terminated ? 'foreground' : null, terminated, childPid: child.pid, logPath, })); process.exitCode = 2; } else { console.log(JSON.stringify({ ...ready, ok: true, logPath })); } process.exit(); } await runForeground(); async function runForeground() { const server = readLiveServerInfo(cwd)?.info; if (!server?.port || !server?.token) { writeState({ ok: false, status: 'error', error: 'live_server_not_running' }); process.exitCode = 1; return; } const client = createCodexAppServerClient({ command: config.codexPath, cwd, requestTimeoutMs: 30_000, turnTimeoutMs: 240_000, clientInfo: { name: 'impeccable_live', title: 'Impeccable Live dedicated worker', version: '0.1.0', }, }); const supervisor = new CodexLiveWorkerSupervisor({ cwd, base: `http://localhost:${server.port}`, token: server.token, client, config, statePath, scriptsDir, log: (message) => process.stderr.write(`[impeccable-codex-worker] ${message}\n`), }); let shuttingDown = false; const shutdown = async () => { if (shuttingDown) return; shuttingDown = true; await supervisor.shutdown({ archive: true }).catch(() => {}); process.exit(0); }; process.once('SIGINT', shutdown); process.once('SIGTERM', shutdown); try { await supervisor.initialize(); await supervisor.run(); } catch (error) { writeState({ ok: false, status: 'error', error: error.message, stack: error.stack, }); await supervisor.shutdown().catch(() => {}); process.exitCode = 1; } } function readLiveConfig(projectCwd) { const configPath = resolveLiveConfigPath({ cwd: projectCwd, scriptsDir }); return readJson(configPath) || {}; } function writeState(value) { fs.mkdirSync(path.dirname(statePath), { recursive: true }); const state = { cwd: path.resolve(cwd), pid: process.pid, updatedAt: new Date().toISOString(), ...value, }; const temporary = `${statePath}.${process.pid}.${Date.now()}.tmp`; fs.writeFileSync(temporary, JSON.stringify(state, null, 2) + '\n', 'utf-8'); fs.renameSync(temporary, statePath); return state; } function readJson(file) { try { return JSON.parse(fs.readFileSync(file, 'utf-8')); } catch { return null; } } function pidReachable(pid) { if (!Number.isInteger(pid) || pid < 1) return false; try { process.kill(pid, 0); return true; } catch (error) { return error?.code === 'EPERM'; } } async function waitFor(check, timeoutMs) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const result = check(); if (result) return result; await new Promise((resolve) => setTimeout(resolve, 25)); } return null; } function positiveInteger(value, fallback) { const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; }