diff --git a/packages/coding-agent/src/ssh/ssh-executor.ts b/packages/coding-agent/src/ssh/ssh-executor.ts index 17c48f210..368970a1a 100644 --- a/packages/coding-agent/src/ssh/ssh-executor.ts +++ b/packages/coding-agent/src/ssh/ssh-executor.ts @@ -79,7 +79,7 @@ export async function executeSSH( using child = ptree.spawn(["ssh", ...(await buildRemoteCommand(host, resolvedCommand))], { signal: options?.signal, timeout: options?.timeout, - exposeStderr: true, + stderr: "full", }); const sink = new OutputSink({ diff --git a/packages/utils/src/ptree.ts b/packages/utils/src/ptree.ts index 3f8a99f21..495dc0eb0 100644 --- a/packages/utils/src/ptree.ts +++ b/packages/utils/src/ptree.ts @@ -103,7 +103,10 @@ export class ChildProcess { #exited: Promise; #stderrStream?: ReadableStream; - constructor(public readonly proc: PipedSubprocess, options?: { exposeStderr?: boolean }) { + constructor( + public readonly proc: PipedSubprocess, + readonly exposeStderr: boolean, + ) { // Eagerly drain stderr into a truncated tail string + raw chunks. const dec = new TextDecoder(); const trim = () => { @@ -111,7 +114,7 @@ export class ChildProcess { this.#stderrTail = this.#stderrTail.slice(-NonZeroExitError.MAX_TRACE); }; let stderrStream = proc.stderr; - if (options?.exposeStderr) { + if (exposeStderr) { const [teeStream, drainStream] = stderrStream.tee(); this.#stderrStream = teeStream; stderrStream = drainStream; @@ -311,12 +314,12 @@ type ChildSpawnOptions = Omit< > & { signal?: AbortSignal; detached?: boolean; - exposeStderr?: boolean; + stderr?: "full" | null; }; /** Spawn a child process with piped stdout/stderr. */ export function spawn(cmd: string[], opts?: ChildSpawnOptions): ChildProcess { - const { timeout = -1, signal, exposeStderr, ...rest } = opts ?? {}; + const { timeout = -1, signal, stderr, ...rest } = opts ?? {}; const child = Bun.spawn(cmd, { stdin: "ignore", stdout: "pipe", @@ -324,14 +327,14 @@ export function spawn(cmd: string[], opts?: ChildSpa windowsHide: true, ...rest, }); - const cp = new ChildProcess(child, { exposeStderr }); + const cp = new ChildProcess(child, stderr === "full"); if (signal) cp.attachSignal(signal); if (timeout > 0) cp.attachTimeout(timeout); return cp; } /** Options for exec. */ -export interface ExecOptions extends Omit, WaitOptions { +export interface ExecOptions extends Omit, WaitOptions { input?: string | Buffer | Uint8Array; }