chore: test deploy snapshot

This commit is contained in:
2026-05-27 19:32:28 +09:00
parent 10805d242e
commit e195ac8088
5 changed files with 1226 additions and 274 deletions

View File

@@ -58,6 +58,48 @@ const CODEX_LIVE_FINISHED_RETENTION_MS = Math.max(
);
const activeCodexExecutions = new Map();
const recentCodexExecutions = new Map();
let runnerShutdownSignal = null;
function logRunner(message) {
process.stdout.write("[server-command-runner] " + new Date().toISOString() + " " + message + "\n");
}
function summarizeActiveExecutionIds(limit = 8) {
const requestIds = Array.from(activeCodexExecutions.keys()).slice(0, limit);
const suffix = activeCodexExecutions.size > requestIds.length ? " +" + (activeCodexExecutions.size - requestIds.length) + " more" : "";
return requestIds.length > 0 ? requestIds.join(", ") + suffix : "none";
}
function resolveSignalExitCode(signal) {
switch (signal) {
case "SIGINT":
return 130;
case "SIGHUP":
return 129;
case "SIGTERM":
return 143;
default:
return 1;
}
}
function shutdownRunnerFromSignal(signal) {
if (runnerShutdownSignal) {
return;
}
runnerShutdownSignal = signal;
logRunner("received " + signal + "; activeExecutions=" + activeCodexExecutions.size + "; requestIds=" + summarizeActiveExecutionIds());
process.exit(resolveSignalExitCode(signal));
}
process.once("SIGTERM", () => shutdownRunnerFromSignal("SIGTERM"));
process.once("SIGINT", () => shutdownRunnerFromSignal("SIGINT"));
process.once("SIGHUP", () => shutdownRunnerFromSignal("SIGHUP"));
process.on("exit", (code) => {
const shutdownLabel = runnerShutdownSignal === null ? "none" : runnerShutdownSignal;
logRunner("exiting with code " + code + "; shutdownSignal=" + shutdownLabel + "; activeExecutions=" + activeCodexExecutions.size);
});
function resolveCodexLiveModel(value) {
const normalized = String(value ?? '').trim();
@@ -798,6 +840,7 @@ async function runCodexLiveExecution(payload, response) {
child,
tempDir,
});
logRunner("spawned Codex child pid=" + (child.pid ?? "unknown") + " requestId=" + requestId + " sessionId=" + sessionId + " model=" + codexModel + " idleTimeoutMs=" + configuredIdleTimeoutMs + " maxExecutionMs=" + configuredMaxExecutionMs);
activeCodexExecutions.set(requestId, executionRecord);
attachCodexExecutionSubscriber(executionRecord, response);
broadcastCodexExecutionEvent(executionRecord, {
@@ -820,7 +863,7 @@ async function runCodexLiveExecution(payload, response) {
}
};
const requestTermination = (message) => {
const requestTermination = (message, reason = 'runner-termination') => {
if (terminationRequested) {
return;
}
@@ -833,6 +876,7 @@ async function runCodexLiveExecution(payload, response) {
message,
});
logRunner("terminating Codex child pid=" + (child.pid ?? "unknown") + " requestId=" + requestId + " reason=" + reason + " message=" + message);
child.kill('SIGTERM');
setTimeout(() => {
if (!child.killed) {
@@ -853,6 +897,7 @@ async function runCodexLiveExecution(payload, response) {
idleTimer = setTimeout(() => {
requestTermination(
`Codex Live 실행이 ${Math.round(configuredIdleTimeoutMs / 1000)}초 동안 출력이 없어 중단되었습니다.`,
'idle-timeout',
);
}, configuredIdleTimeoutMs);
idleTimer.unref?.();
@@ -861,6 +906,7 @@ async function runCodexLiveExecution(payload, response) {
executionTimer = setTimeout(() => {
requestTermination(
`Codex Live 실행이 ${Math.round(configuredMaxExecutionMs / 1000)}초를 넘어 중단되었습니다.`,
'max-execution-timeout',
);
}, configuredMaxExecutionMs);
executionTimer.unref?.();
@@ -968,6 +1014,7 @@ async function runCodexLiveExecution(payload, response) {
child.on('error', async (error) => {
clearExecutionTimers();
logRunner("Codex child process error requestId=" + requestId + " pid=" + (child.pid ?? "unknown") + " message=" + (error instanceof Error ? error.message : String(error)));
broadcastCodexExecutionEvent(executionRecord, {
type: 'error',
message: error instanceof Error ? error.message : String(error),
@@ -979,8 +1026,9 @@ async function runCodexLiveExecution(payload, response) {
finalizeCodexExecution(executionRecord);
});
child.on('close', async (code) => {
child.on('close', async (code, signal) => {
clearExecutionTimers();
logRunner("Codex child closed requestId=" + requestId + " pid=" + (child.pid ?? "unknown") + " exitCode=" + (code ?? "null") + " signal=" + (signal ?? "none") + " terminationRequested=" + terminationRequested);
const trailingLine = jsonLineBuffer.trim();
if (trailingLine) {
handleCodexJsonLine(trailingLine);
@@ -1204,6 +1252,7 @@ const server = createServer(async (request, response) => {
}
try {
logRunner("received cancel request for requestId=" + requestId + "; forwarding SIGTERM to child pid=" + (activeExecution.child.pid ?? "unknown"));
activeExecution.child.kill('SIGTERM');
setTimeout(() => {
const current = activeCodexExecutions.get(requestId);
@@ -1239,5 +1288,5 @@ server.listen(port, host, () => {
});
}, 10_000);
heartbeatTimer.unref();
process.stdout.write(`server-command-runner listening on http://${host}:${port}\n`);
logRunner("listening on http://" + host + ":" + port + "; pid=" + process.pid + "; ppid=" + process.ppid + "; startedAt=" + startedAt + "; logFile=" + runnerLogFile);
});

0
scripts/server-command-runner-supervisor.sh Normal file → Executable file
View File