diff --git a/src/process/exec.ts b/src/process/exec.ts index 140582d541a..9af3c909a8b 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -246,6 +246,8 @@ export async function runCommandWithTimeout( let settled = false; let timedOut = false; let noOutputTimedOut = false; + let childExitState: { code: number | null; signal: NodeJS.Signals | null } | null = null; + let closeFallbackTimer: NodeJS.Timeout | null = null; let noOutputTimer: NodeJS.Timeout | null = null; const shouldTrackOutputTimeout = typeof noOutputTimeoutMs === "number" && @@ -260,6 +262,14 @@ export async function runCommandWithTimeout( noOutputTimer = null; }; + const clearCloseFallbackTimer = () => { + if (!closeFallbackTimer) { + return; + } + clearTimeout(closeFallbackTimer); + closeFallbackTimer = null; + }; + const armNoOutputTimer = () => { if (!shouldTrackOutputTimeout || settled) { return; @@ -304,8 +314,22 @@ export async function runCommandWithTimeout( settled = true; clearTimeout(timer); clearNoOutputTimer(); + clearCloseFallbackTimer(); reject(err); }); + child.on("exit", (code, signal) => { + childExitState = { code, signal }; + if (settled || closeFallbackTimer) { + return; + } + closeFallbackTimer = setTimeout(() => { + if (settled) { + return; + } + child.stdout?.destroy(); + child.stderr?.destroy(); + }, 250); + }); child.on("close", (code, signal) => { if (settled) { return; @@ -313,25 +337,28 @@ export async function runCommandWithTimeout( settled = true; clearTimeout(timer); clearNoOutputTimer(); + clearCloseFallbackTimer(); + const resolvedCode = childExitState?.code ?? code; + const resolvedSignal = childExitState?.signal ?? signal; const termination = noOutputTimedOut ? "no-output-timeout" : timedOut ? "timeout" - : signal != null + : resolvedSignal != null ? "signal" : "exit"; const normalizedCode = termination === "timeout" || termination === "no-output-timeout" - ? code === 0 + ? resolvedCode === 0 ? 124 - : code - : code; + : resolvedCode + : resolvedCode; resolve({ pid: child.pid ?? undefined, stdout, stderr, code: normalizedCode, - signal, + signal: resolvedSignal, killed: child.killed, termination, noOutputTimedOut,