Files
openclaw/src/process/exec-result.ts
Peter Steinberger db02a96c4c refactor(process): route bounded commands through Execa (#106495)
* refactor(process): centralize bounded command execution

* refactor(process): migrate core one-shot commands

* refactor(plugins): migrate one-shot commands

* fix(process): await Windows tree termination

* chore(plugin-sdk): refresh process runtime surface

* refactor(process): migrate remaining bounded commands

* refactor(process): normalize command result handling

* refactor(process): split execution responsibilities

* chore(plugin-sdk): refresh API baseline

* chore(process): remove release-owned changelog entry

* fix(process): narrow binary command input checks

* fix(process): cap sandbox command output

* fix(qa-lab): preserve exact node probe env

* chore(ci): refresh dead export baseline

* fix(process): preserve force-kill command deadlines

* fix(process): avoid post-exit timeout reclassification

* test(process): update scp staging wrapper mock

* test(process): update remaining wrapper mocks

* refactor(qa-lab): preserve Execa tar execution
2026-07-13 11:07:35 -07:00

118 lines
3.2 KiB
TypeScript

export type SpawnResult = {
pid?: number;
stdout: string;
stderr: string;
stdoutTruncatedBytes?: number;
stderrTruncatedBytes?: number;
preservedStdoutLines?: string[];
preservedStderrLines?: string[];
code: number | null;
signal: NodeJS.Signals | null;
killed: boolean;
termination: "exit" | "timeout" | "no-output-timeout" | "signal";
noOutputTimedOut?: boolean;
outputLimitExceeded?: boolean;
outputErrorStream?: "stdout" | "stderr";
};
export const TIMEOUT_EXIT_CODE = 124;
export function createSanitizedCommandError(result: {
code?: unknown;
exitCode?: unknown;
signal?: unknown;
timedOut?: boolean;
isCanceled?: boolean;
isMaxBuffer?: boolean;
isTerminated?: boolean;
}): Error {
const code = typeof result.code === "string" ? result.code : undefined;
const exitCode = typeof result.exitCode === "number" ? result.exitCode : undefined;
const signal = typeof result.signal === "string" ? result.signal : undefined;
const message = result.timedOut
? "Command timed out"
: result.isMaxBuffer
? "Command output exceeded its capture limit"
: result.isCanceled
? "Command was canceled"
: result.isTerminated
? `Command was terminated${signal ? ` by ${signal}` : ""}`
: exitCode !== undefined && exitCode !== 0
? `Command exited with code ${exitCode}`
: `Command failed during launch or output capture${code ? ` (${code})` : ""}`;
return Object.assign(new Error(message), {
...(code ? { code } : {}),
...(exitCode !== undefined ? { exitCode } : {}),
...(signal ? { signal } : {}),
});
}
export function isPlainCommandExitFailure(result: {
failed: boolean;
exitCode?: unknown;
signal?: unknown;
cause?: unknown;
timedOut?: boolean;
isCanceled?: boolean;
isMaxBuffer?: boolean;
isTerminated?: boolean;
}): boolean {
return (
result.failed &&
typeof result.exitCode === "number" &&
result.exitCode !== 0 &&
result.signal === undefined &&
result.cause === undefined &&
!result.timedOut &&
!result.isCanceled &&
!result.isMaxBuffer &&
!result.isTerminated
);
}
export function isPlainCommandSignalFailure(result: {
failed: boolean;
exitCode?: unknown;
signal?: unknown;
cause?: unknown;
timedOut?: boolean;
isCanceled?: boolean;
isMaxBuffer?: boolean;
isTerminated?: boolean;
}): boolean {
return (
result.failed &&
result.exitCode === undefined &&
typeof result.signal === "string" &&
result.cause === undefined &&
!result.timedOut &&
!result.isCanceled &&
!result.isMaxBuffer &&
result.isTerminated === true
);
}
export function resolveProcessExitCode(params: {
explicitCode: number | null | undefined;
childExitCode: number | null | undefined;
resolvedSignal: NodeJS.Signals | null;
usesWindowsExitCodeShim: boolean;
timedOut: boolean;
noOutputTimedOut: boolean;
killIssuedByTimeout: boolean;
killIssuedByAbort?: boolean;
}): number | null {
return (
params.explicitCode ??
params.childExitCode ??
(params.usesWindowsExitCodeShim &&
params.resolvedSignal == null &&
!params.timedOut &&
!params.noOutputTimedOut &&
!params.killIssuedByTimeout &&
!params.killIssuedByAbort
? 0
: null)
);
}