mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-10 03:14:32 +08:00
179 lines
3.6 KiB
JavaScript
Executable File
179 lines
3.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { spawn } from 'node:child_process';
|
|
|
|
const DEFAULT_MODEL = 'gpt-5-codex';
|
|
const DEFAULT_WORKDIR = '.';
|
|
const DEFAULT_TIMEOUT_MS = 7_200_000; // 2 hours
|
|
const FORCE_KILL_DELAY_MS = 5_000;
|
|
|
|
const args = process.argv.slice(2);
|
|
const [task, modelArg, workdirArg] = args;
|
|
|
|
const logError = (message) => {
|
|
process.stderr.write(`ERROR: ${message}\n`);
|
|
};
|
|
|
|
const logWarn = (message) => {
|
|
process.stderr.write(`WARN: ${message}\n`);
|
|
};
|
|
|
|
if (!task) {
|
|
logError('Task required');
|
|
process.exit(1);
|
|
}
|
|
|
|
const model = modelArg || DEFAULT_MODEL;
|
|
const workdir = workdirArg || DEFAULT_WORKDIR;
|
|
|
|
const resolveTimeout = () => {
|
|
const raw = process.env.CODEX_TIMEOUT;
|
|
if (raw == null || raw === '') {
|
|
return DEFAULT_TIMEOUT_MS;
|
|
}
|
|
|
|
const parsed = Number(raw);
|
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
logWarn(`Invalid CODEX_TIMEOUT "${raw}", falling back to ${DEFAULT_TIMEOUT_MS}ms`);
|
|
return DEFAULT_TIMEOUT_MS;
|
|
}
|
|
|
|
return parsed;
|
|
};
|
|
|
|
const codexArgs = [
|
|
'e',
|
|
'-m',
|
|
model,
|
|
'--dangerously-bypass-approvals-and-sandbox',
|
|
'--skip-git-repo-check',
|
|
'-C',
|
|
workdir,
|
|
'--json',
|
|
task,
|
|
];
|
|
|
|
const child = spawn('codex', codexArgs, {
|
|
stdio: ['ignore', 'pipe', 'inherit'],
|
|
});
|
|
|
|
let timedOut = false;
|
|
let lastAgentMessage = null;
|
|
let stdoutBuffer = '';
|
|
let forceKillTimer = null;
|
|
|
|
const timeoutMs = resolveTimeout();
|
|
|
|
const forceTerminate = () => {
|
|
if (!child.killed) {
|
|
child.kill('SIGTERM');
|
|
forceKillTimer = setTimeout(() => {
|
|
if (!child.killed) {
|
|
child.kill('SIGKILL');
|
|
}
|
|
}, FORCE_KILL_DELAY_MS);
|
|
}
|
|
};
|
|
|
|
const timeoutHandle = setTimeout(() => {
|
|
timedOut = true;
|
|
logError('Codex execution timeout');
|
|
forceTerminate();
|
|
}, timeoutMs);
|
|
|
|
const normalizeText = (text) => {
|
|
if (typeof text === 'string') {
|
|
return text;
|
|
}
|
|
if (Array.isArray(text)) {
|
|
return text.join('');
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const handleJsonLine = (line) => {
|
|
const trimmed = line.trim();
|
|
if (!trimmed) {
|
|
return;
|
|
}
|
|
|
|
let event;
|
|
try {
|
|
event = JSON.parse(trimmed);
|
|
} catch (err) {
|
|
logWarn(`Failed to parse Codex output line: ${trimmed}`);
|
|
return;
|
|
}
|
|
|
|
if (
|
|
event &&
|
|
event.type === 'item.completed' &&
|
|
event.item &&
|
|
event.item.type === 'agent_message'
|
|
) {
|
|
const text = normalizeText(event.item.text);
|
|
if (text != null) {
|
|
lastAgentMessage = text;
|
|
}
|
|
}
|
|
};
|
|
|
|
child.stdout.on('data', (chunk) => {
|
|
stdoutBuffer += chunk.toString('utf8');
|
|
let newlineIndex = stdoutBuffer.indexOf('\n');
|
|
|
|
while (newlineIndex !== -1) {
|
|
const line = stdoutBuffer.slice(0, newlineIndex);
|
|
stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
|
|
handleJsonLine(line);
|
|
newlineIndex = stdoutBuffer.indexOf('\n');
|
|
}
|
|
});
|
|
|
|
child.stdout.on('end', () => {
|
|
if (stdoutBuffer) {
|
|
handleJsonLine(stdoutBuffer);
|
|
stdoutBuffer = '';
|
|
}
|
|
});
|
|
|
|
child.on('error', (err) => {
|
|
clearTimeout(timeoutHandle);
|
|
if (forceKillTimer) {
|
|
clearTimeout(forceKillTimer);
|
|
}
|
|
logError(`Failed to start Codex CLI: ${err.message}`);
|
|
process.exit(1);
|
|
});
|
|
|
|
child.on('close', (code, signal) => {
|
|
clearTimeout(timeoutHandle);
|
|
if (forceKillTimer) {
|
|
clearTimeout(forceKillTimer);
|
|
}
|
|
|
|
if (timedOut) {
|
|
process.exit(124);
|
|
return;
|
|
}
|
|
|
|
if (code === 0) {
|
|
if (lastAgentMessage != null) {
|
|
process.stdout.write(`${lastAgentMessage}\n`);
|
|
process.exit(0);
|
|
} else {
|
|
logError('Codex completed without an agent_message output');
|
|
process.exit(1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (signal) {
|
|
logError(`Codex terminated with signal ${signal}`);
|
|
process.exit(code ?? 1);
|
|
return;
|
|
}
|
|
|
|
logError(`Codex exited with status ${code}`);
|
|
process.exit(code ?? 1);
|
|
});
|