






















@@ -4,6 +4,10 @@ import { createWriteStream } from "node:fs";
44import path from "node:path";
55import { finished } from "node:stream/promises";
66import { fileURLToPath } from "node:url";
7+import {
8+addTimerTimeoutGraceMs,
9+clampTimerTimeoutMs,
10+} from "@openclaw/normalization-core/number-coercion";
711import { resolveNpmRunner } from "../../npm-runner.mjs";
812import { resolvePnpmRunner } from "../../pnpm-runner.mjs";
913import { buildCmdExeCommandLine, resolveWindowsCmdExePath } from "../../windows-cmd-helpers.mjs";
@@ -338,6 +342,14 @@ function isBareCommand(command: string, name: "npm" | "pnpm"): boolean {
338342return portableBasename(command) === command && command.toLowerCase() === name;
339343}
340344345+function resolveHostCommandTimeoutMs(timeoutMs: number): number {
346+return clampTimerTimeoutMs(timeoutMs) ?? 1;
347+}
348+349+function resolveOptionalHostCommandTimeoutMs(timeoutMs: number | undefined): number | undefined {
350+return timeoutMs === undefined ? undefined : resolveHostCommandTimeoutMs(timeoutMs);
351+}
352+341353export function resolveHostCommandInvocation(
342354command: string,
343355args: string[],
@@ -387,9 +399,10 @@ export function resolveHostCommandInvocation(
387399export function run(command: string, args: string[], options: RunOptions = {}): CommandResult {
388400const env = { ...process.env, ...options.env };
389401const invocation = resolveHostCommandInvocation(command, args, { env });
390-const usesPosixTimedWrapper = process.platform !== "win32" && options.timeoutMs !== undefined;
402+const timeoutMs = resolveOptionalHostCommandTimeoutMs(options.timeoutMs);
403+const usesPosixTimedWrapper = process.platform !== "win32" && timeoutMs !== undefined;
391404const result = usesPosixTimedWrapper
392- ? runPosixTimedCommandSync(invocation, env, options)
405+ ? runPosixTimedCommandSync(invocation, env, options, timeoutMs)
393406 : spawnSync(invocation.command, invocation.args, {
394407cwd: options.cwd ?? repoRoot,
395408encoding: "utf8",
@@ -399,7 +412,7 @@ export function run(command: string, args: string[], options: RunOptions = {}):
399412maxBuffer: HOST_COMMAND_MAX_BUFFER_BYTES,
400413stdio: options.quiet ? ["pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
401414shell: invocation.shell,
402-timeout: options.timeoutMs,
415+timeout: timeoutMs,
403416windowsVerbatimArguments: invocation.windowsVerbatimArguments,
404417});
405418@@ -421,7 +434,7 @@ export function run(command: string, args: string[], options: RunOptions = {}):
421434wrapperTimedOut || (result.error as NodeJS.ErrnoException | undefined)?.code === "ETIMEDOUT";
422435if (wrapperTimedOut && options.check !== false) {
423436const error = new Error(
424-`${command} ${args.join(" ")} timed out after ${options.timeoutMs}ms`,
437+`${command} ${args.join(" ")} timed out after ${timeoutMs}ms`,
425438) as NodeJS.ErrnoException;
426439error.code = "ETIMEDOUT";
427440throw error;
@@ -495,7 +508,9 @@ function runPosixTimedCommandSync(
495508invocation: HostCommandInvocation,
496509env: NodeJS.ProcessEnv,
497510options: RunOptions,
511+timeoutMs: number,
498512): SpawnSyncReturns<string> {
513+const wrapperTimeoutMs = addTimerTimeoutGraceMs(timeoutMs, HOST_COMMAND_WRAPPER_BACKSTOP_MS) ?? 1;
499514const payload = JSON.stringify({
500515args: invocation.args,
501516command: invocation.command,
@@ -505,7 +520,7 @@ function runPosixTimedCommandSync(
505520maxBufferBytes: HOST_COMMAND_MAX_BUFFER_BYTES,
506521shell: invocation.shell,
507522timeoutKillGraceMs: HOST_COMMAND_TIMEOUT_KILL_GRACE_MS,
508-timeoutMs: options.timeoutMs,
523+ timeoutMs,
509524});
510525return spawnSync(process.execPath, ["-e", POSIX_TIMEOUT_WRAPPER], {
511526cwd: options.cwd ?? repoRoot,
@@ -515,7 +530,7 @@ function runPosixTimedCommandSync(
515530killSignal: "SIGKILL",
516531maxBuffer: HOST_COMMAND_MAX_BUFFER_BYTES * 2 + HOST_COMMAND_WRAPPER_EXTRA_BUFFER_BYTES,
517532stdio: ["pipe", "pipe", "pipe", "pipe"],
518-timeout: (options.timeoutMs ?? 0) + HOST_COMMAND_WRAPPER_BACKSTOP_MS,
533+timeout: wrapperTimeoutMs,
519534});
520535}
521536@@ -531,11 +546,12 @@ export async function runStreaming(
531546return await new Promise((resolve, reject) => {
532547const env = { ...process.env, ...options.env };
533548const invocation = resolveHostCommandInvocation(command, args, { env });
549+const timeoutMs = resolveOptionalHostCommandTimeoutMs(options.timeoutMs);
534550const logStream = options.logPath
535551 ? createWriteStream(options.logPath, { encoding: "utf8", flags: "w" })
536552 : undefined;
537553let logStreamError: Error | undefined;
538-const detached = process.platform !== "win32" && options.timeoutMs != null;
554+const detached = process.platform !== "win32" && timeoutMs !== undefined;
539555const child = spawn(invocation.command, invocation.args, {
540556cwd: options.cwd ?? repoRoot,
541557 detached,
@@ -570,8 +586,8 @@ export async function runStreaming(
570586return (error as NodeJS.ErrnoException).code === "EPERM";
571587}
572588};
573-const waitForStreamingProcessGroupExit = async (timeoutMs: number): Promise<boolean> => {
574-const deadlineAt = Date.now() + timeoutMs;
589+const waitForStreamingProcessGroupExit = async (timeoutBudgetMs: number): Promise<boolean> => {
590+const deadlineAt = Date.now() + timeoutBudgetMs;
575591while (Date.now() < deadlineAt) {
576592if (!streamingProcessGroupAlive()) {
577593return true;
@@ -637,7 +653,7 @@ export async function runStreaming(
637653}
638654}, HOST_COMMAND_TIMEOUT_KILL_GRACE_MS);
639655};
640-if (process.platform !== "win32" && options.timeoutMs != null) {
656+if (process.platform !== "win32" && timeoutMs !== undefined) {
641657for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"] as const) {
642658const handler = (): void => {
643659forwardedParentSignal ??= signal;
@@ -702,7 +718,7 @@ export async function runStreaming(
702718}
703719};
704720const timer =
705-options.timeoutMs == null
721+timeoutMs === undefined
706722 ? undefined
707723 : setTimeout(() => {
708724timedOut = true;
@@ -713,7 +729,7 @@ export async function runStreaming(
713729HOST_COMMAND_STREAMING_TIMEOUT_KILL_GRACE_MS,
714730);
715731killTimer.unref();
716-}, options.timeoutMs);
732+}, timeoutMs);
717733718734child.on("error", (error) => {
719735if (timer) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。