@@ -36,6 +36,7 @@ const DEFAULT_HOT_WALL_WARN_MS = 30_000;
|
36 | 36 | const DEFAULT_MAX_RSS_WARN_MB = 1536; |
37 | 37 | const DEFAULT_QA_PLUGIN_CHUNK_SIZE = 12; |
38 | 38 | const COMMAND_OUTPUT_MAX_BUFFER_BYTES = 16 * 1024 * 1024; |
| 39 | +const MAX_TIMER_TIMEOUT_MS = 2_147_000_000; |
39 | 40 | const ANSI_PATTERN = new RegExp(String.raw`\u001B\[[0-9;]*m`, "gu"); |
40 | 41 | |
41 | 42 | /** |
@@ -439,6 +440,15 @@ function stripAnsi(value) {
|
439 | 440 | return value.replace(ANSI_PATTERN, ""); |
440 | 441 | } |
441 | 442 | |
| 443 | +function resolveTimerTimeoutMs(valueMs) { |
| 444 | +const value = Number.isFinite(valueMs) ? Math.floor(valueMs) : MAX_TIMER_TIMEOUT_MS; |
| 445 | +return Math.min(Math.max(value, 1), MAX_TIMER_TIMEOUT_MS); |
| 446 | +} |
| 447 | + |
| 448 | +function resolveOptionalTimerTimeoutMs(valueMs) { |
| 449 | +return valueMs === undefined || valueMs <= 0 ? null : resolveTimerTimeoutMs(valueMs); |
| 450 | +} |
| 451 | + |
442 | 452 | function writeCommandLog(params) { |
443 | 453 | const { logDir, label, stdout, stderr } = params; |
444 | 454 | fs.mkdirSync(logDir, { recursive: true }); |
@@ -487,7 +497,8 @@ export function runMeasuredCommandLive(params) {
|
487 | 497 | let parentTerminationSignal = null; |
488 | 498 | const maxBufferBytes = params.maxBufferBytes ?? COMMAND_OUTPUT_MAX_BUFFER_BYTES; |
489 | 499 | const maxRelayBytes = params.consoleOutputMaxBytes ?? maxBufferBytes; |
490 | | -const timeoutKillGraceMs = params.timeoutKillGraceMs ?? 5_000; |
| 500 | +const timeoutMs = resolveOptionalTimerTimeoutMs(params.timeoutMs); |
| 501 | +const timeoutKillGraceMs = resolveTimerTimeoutMs(params.timeoutKillGraceMs ?? 5_000); |
491 | 502 | const spawnOptions = mode === "none" ? (params.spawnOptions ?? {}) : {}; |
492 | 503 | const useProcessGroup = |
493 | 504 | process.platform !== "win32" && |
@@ -519,8 +530,8 @@ export function runMeasuredCommandLive(params) {
|
519 | 530 | return Boolean(error && error.code === "EPERM"); |
520 | 531 | } |
521 | 532 | }; |
522 | | -const waitForProcessGroupExit = async (timeoutMs) => { |
523 | | -const deadlineAt = Date.now() + timeoutMs; |
| 533 | +const waitForProcessGroupExit = async (timeoutBudgetMs) => { |
| 534 | +const deadlineAt = Date.now() + timeoutBudgetMs; |
524 | 535 | while (Date.now() < deadlineAt) { |
525 | 536 | if (!processGroupAlive()) { |
526 | 537 | return true; |
@@ -641,16 +652,16 @@ export function runMeasuredCommandLive(params) {
|
641 | 652 | child.stdout?.on("data", (chunk) => appendOutput("stdout", chunk)); |
642 | 653 | child.stderr?.on("data", (chunk) => appendOutput("stderr", chunk)); |
643 | 654 | const timeout = |
644 | | -params.timeoutMs > 0 |
| 655 | +timeoutMs !== null |
645 | 656 | ? setTimeout(() => { |
646 | 657 | timedOut = true; |
647 | 658 | spawnError = { |
648 | 659 | code: "ETIMEDOUT", |
649 | | -message: `Command timed out after ${params.timeoutMs}ms`, |
| 660 | +message: `Command timed out after ${timeoutMs}ms`, |
650 | 661 | }; |
651 | 662 | killMeasuredProcess(); |
652 | 663 | scheduleForceKill(); |
653 | | -}, params.timeoutMs) |
| 664 | +}, timeoutMs) |
654 | 665 | : null; |
655 | 666 | timeout?.unref?.(); |
656 | 667 | const finish = (status, signal) => { |
|