fix(scripts): clamp memory fd repro timers

This commit is contained in:
Vincent Koc
2026-06-22 03:24:51 +02:00
parent 2800ce4e28
commit 4113982fa8
2 changed files with 90 additions and 9 deletions

View File

@@ -27,6 +27,7 @@ const ISSUE_FILE_COUNTS = [
const ISSUE_MEMORY_FILE_COUNT = ISSUE_FILE_COUNTS.reduce((sum, [, count]) => sum + count, 0);
const DEFAULT_FILE_COUNT = 512;
const DEFAULT_MAX_WORKSPACE_REG_FDS = process.platform === "darwin" ? 8 : 64;
const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
/**
* Maximum gateway-ready output tail retained while waiting for startup.
*/
@@ -134,6 +135,24 @@ function readPositiveNumberEnv(name, fallback) {
return raw == null || raw.trim() === "" ? fallback : readPositiveNumber(raw, name);
}
function clampTimerTimeoutMs(valueMs, minMs = 1) {
const min = Math.max(0, Math.floor(minMs));
const value = Number.isFinite(valueMs) ? valueMs : min;
return Math.min(Math.max(Math.floor(value), min), MAX_TIMER_TIMEOUT_MS);
}
function readTimerTimeoutNumber(value, label, minMs = 1) {
const parsed = minMs > 0 ? readPositiveNumber(value, label) : readNumber(value, label);
return clampTimerTimeoutMs(parsed, minMs);
}
function readTimerTimeoutNumberEnv(name, fallback, minMs = 1) {
const raw = process.env[name];
return raw == null || raw.trim() === ""
? clampTimerTimeoutMs(fallback, minMs)
: readTimerTimeoutNumber(raw, name, minMs);
}
/**
* Parses memory FD repro CLI arguments and environment fallbacks.
*/
@@ -192,13 +211,13 @@ export function parseArgs(argv) {
options.minLeakedFds = readPositiveNumber(readValue(), "--min-leaked-fds");
break;
case "--invoke-timeout-ms":
options.invokeTimeoutMs = readPositiveNumber(readValue(), "--invoke-timeout-ms");
options.invokeTimeoutMs = readTimerTimeoutNumber(readValue(), "--invoke-timeout-ms");
break;
case "--sample-delay-ms":
options.sampleDelayMs = readNumber(readValue(), "--sample-delay-ms");
options.sampleDelayMs = readTimerTimeoutNumber(readValue(), "--sample-delay-ms", 0);
break;
case "--settle-delay-ms":
options.settleDelayMs = readNumber(readValue(), "--settle-delay-ms");
options.settleDelayMs = readTimerTimeoutNumber(readValue(), "--settle-delay-ms", 0);
break;
case "--output-dir":
options.outputDir = path.resolve(readValue());
@@ -222,9 +241,17 @@ export function parseArgs(argv) {
"OPENCLAW_MEMORY_FD_REPRO_MAX_WORKSPACE_REG_FDS",
DEFAULT_MAX_WORKSPACE_REG_FDS,
);
options.invokeTimeoutMs ??= readPositiveNumberEnv("OPENCLAW_MEMORY_FD_REPRO_TIMEOUT_MS", 30_000);
options.sampleDelayMs ??= readNumberEnv("OPENCLAW_MEMORY_FD_REPRO_SAMPLE_DELAY_MS", 1_000);
options.settleDelayMs ??= readNumberEnv("OPENCLAW_MEMORY_FD_REPRO_SETTLE_DELAY_MS", 5_000);
options.invokeTimeoutMs ??= readTimerTimeoutNumberEnv("OPENCLAW_MEMORY_FD_REPRO_TIMEOUT_MS", 30_000);
options.sampleDelayMs ??= readTimerTimeoutNumberEnv(
"OPENCLAW_MEMORY_FD_REPRO_SAMPLE_DELAY_MS",
1_000,
0,
);
options.settleDelayMs ??= readTimerTimeoutNumberEnv(
"OPENCLAW_MEMORY_FD_REPRO_SETTLE_DELAY_MS",
5_000,
0,
);
if (!Number.isFinite(options.fileCount) || options.fileCount <= 0) {
throw new Error("file count must be greater than 0");
}
@@ -243,7 +270,7 @@ function logStep(message) {
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
setTimeout(resolve, clampTimerTimeoutMs(ms, 0));
});
}
@@ -676,9 +703,10 @@ export function classifyMemorySearchInvokeResponse({ httpOk, status, bodyText })
};
}
async function invokeMemorySearch({ port, token, timeoutMs }) {
export async function invokeMemorySearch({ port, token, timeoutMs }) {
const resolvedTimeoutMs = clampTimerTimeoutMs(timeoutMs);
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
const timer = setTimeout(() => controller.abort(), resolvedTimeoutMs);
const startedAt = Date.now();
try {
const res = await fetch(`http://127.0.0.1:${port}/tools/invoke`, {