fix(scripts): clamp boundary artifact timers

This commit is contained in:
Vincent Koc
2026-06-22 03:05:11 +02:00
parent c310f8cfa4
commit 4f7d1f4977
2 changed files with 23 additions and 2 deletions
@@ -18,6 +18,7 @@ const ROOT_SHIMS_MAX_OLD_SPACE_SIZE =
const ROOT_SHIMS_NODE_OPTIONS =
`${process.env.NODE_OPTIONS ?? ""} --max-old-space-size=${ROOT_SHIMS_MAX_OLD_SPACE_SIZE}`.trim();
const NODE_STEP_ABORT_KILL_GRACE_MS = 1_000;
const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
const NODE_STEP_PARENT_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"];
const NODE_STEP_PARENT_SIGNAL_EXIT_CODES = new Map([
["SIGHUP", 129],
@@ -469,10 +470,19 @@ function installNodeStepParentSignalForwarders() {
});
}
function resolveNodeStepTimerTimeoutMs(valueMs) {
const value = Number(valueMs);
if (!Number.isFinite(value)) {
return MAX_TIMER_TIMEOUT_MS;
}
return Math.min(Math.max(Math.floor(value), 1), MAX_TIMER_TIMEOUT_MS);
}
/**
* Runs one artifact step with timeout, abort propagation, and prefixed output.
*/
export function runNodeStep(label, args, timeoutMs, params = {}) {
const resolvedTimeoutMs = resolveNodeStepTimerTimeoutMs(timeoutMs);
const abortController = params.abortController;
const spawnImpl = params.spawnImpl ?? spawn;
installNodeStepParentSignalForwarders();
@@ -555,8 +565,8 @@ export function runNodeStep(label, args, timeoutMs, params = {}) {
stdoutWriter.flush();
stderrWriter.flush();
abortSiblingSteps(abortController);
rejectPromise(new Error(`${label} timed out after ${timeoutMs}ms`));
}, timeoutMs);
rejectPromise(new Error(`${label} timed out after ${resolvedTimeoutMs}ms`));
}, resolvedTimeoutMs);
abortController?.signal.addEventListener("abort", abortStep, { once: true });
child.stdout.setEncoding("utf8");
@@ -7,6 +7,7 @@ import os from "node:os";
import path from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { pathToFileURL } from "node:url";
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
import { afterEach, describe, expect, it, vi } from "vitest";
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
import {
@@ -317,6 +318,16 @@ describe("prepare-extension-package-boundary-artifacts", () => {
expect(signals).toEqual(["SIGKILL"]);
});
it("clamps oversized prep step timers before scheduling", async () => {
await expect(
runNodeStep(
"slow-success",
["--eval", "setTimeout(() => process.exit(0), 25);"],
MAX_TIMER_TIMEOUT_MS + 1,
),
).resolves.toBeUndefined();
});
it.runIf(process.platform !== "win32")("kills timed-out prep step process groups", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-timeout-group-"));
tempRoots.add(rootDir);