fix(dev): forward run-node wrapper signals

This commit is contained in:
Peter Steinberger
2026-04-05 17:05:12 +01:00
parent 9e8151f347
commit 17521116db
3 changed files with 138 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ export function runNodeMain(params?: {
args: string[],
options: unknown,
) => {
kill?: (signal?: string) => boolean | void;
on: (
event: "exit",
cb: (code: number | null, signal: string | null) => void,
@@ -27,6 +28,7 @@ export function runNodeMain(params?: {
spawnSync?: unknown;
fs?: unknown;
stderr?: { write: (value: string) => void };
process?: NodeJS.Process;
execPath?: string;
cwd?: string;
args?: string[];

View File

@@ -250,6 +250,15 @@ const BUILD_REASON_LABELS = {
const formatBuildReason = (reason) => BUILD_REASON_LABELS[reason] ?? reason;
const SIGNAL_EXIT_CODES = {
SIGINT: 130,
SIGTERM: 143,
};
const isSignalKey = (signal) => Object.hasOwn(SIGNAL_EXIT_CODES, signal);
const getSignalExitCode = (signal) => (isSignalKey(signal) ? SIGNAL_EXIT_CODES[signal] : 1);
const logRunner = (message, deps) => {
if (deps.env.OPENCLAW_RUNNER_LOG === "0") {
return;
@@ -257,19 +266,65 @@ const logRunner = (message, deps) => {
deps.stderr.write(`[openclaw] ${message}\n`);
};
const waitForSpawnedProcess = async (childProcess, deps) => {
let forwardedSignal = null;
let onSigInt;
let onSigTerm;
const cleanupSignals = () => {
if (onSigInt) {
deps.process.off("SIGINT", onSigInt);
}
if (onSigTerm) {
deps.process.off("SIGTERM", onSigTerm);
}
};
const forwardSignal = (signal) => {
if (forwardedSignal) {
return;
}
forwardedSignal = signal;
try {
childProcess.kill?.(signal);
} catch {
// Best-effort only. Exit handling still happens via the child "exit" event.
}
};
onSigInt = () => {
forwardSignal("SIGINT");
};
onSigTerm = () => {
forwardSignal("SIGTERM");
};
deps.process.on("SIGINT", onSigInt);
deps.process.on("SIGTERM", onSigTerm);
try {
return await new Promise((resolve) => {
childProcess.on("exit", (exitCode, exitSignal) => {
resolve({ exitCode, exitSignal, forwardedSignal });
});
});
} finally {
cleanupSignals();
}
};
const runOpenClaw = async (deps) => {
const nodeProcess = deps.spawn(deps.execPath, ["openclaw.mjs", ...deps.args], {
cwd: deps.cwd,
env: deps.env,
stdio: "inherit",
});
const res = await new Promise((resolve) => {
nodeProcess.on("exit", (exitCode, exitSignal) => {
resolve({ exitCode, exitSignal });
});
});
const res = await waitForSpawnedProcess(nodeProcess, deps);
if (res.exitSignal) {
return 1;
return getSignalExitCode(res.exitSignal);
}
if (res.forwardedSignal) {
return getSignalExitCode(res.forwardedSignal);
}
return res.exitCode ?? 1;
};
@@ -306,6 +361,7 @@ export async function runNodeMain(params = {}) {
spawnSync: params.spawnSync ?? spawnSync,
fs: params.fs ?? fs,
stderr: params.stderr ?? process.stderr,
process: params.process ?? process,
execPath: params.execPath ?? process.execPath,
cwd: params.cwd ?? process.cwd(),
args: params.args ?? process.argv.slice(2),
@@ -341,11 +397,12 @@ export async function runNodeMain(params = {}) {
stdio: "inherit",
});
const buildRes = await new Promise((resolve) => {
build.on("exit", (exitCode, exitSignal) => resolve({ exitCode, exitSignal }));
});
const buildRes = await waitForSpawnedProcess(build, deps);
if (buildRes.exitSignal) {
return 1;
return getSignalExitCode(buildRes.exitSignal);
}
if (buildRes.forwardedSignal) {
return getSignalExitCode(buildRes.forwardedSignal);
}
if (buildRes.exitCode !== 0 && buildRes.exitCode !== null) {
return buildRes.exitCode;