Something went wrong. Try again.
more up to date at codeberg https://codeberg.org/cowie/tau.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108/** * Shared command execution utilities for extensions and custom tools. */
import { spawn } from "node:child_process";import { waitForChildProcess } from "../utils/child-process.ts";
/** * Options for executing shell commands. */export interface ExecOptions { /** AbortSignal to cancel the command */ signal?: AbortSignal; /** Timeout in milliseconds */ timeout?: number; /** Working directory */ cwd?: string;}
/** * Result of executing a shell command. */export interface ExecResult { stdout: string; stderr: string; code: number; killed: boolean;}
/** * Execute a shell command and return stdout/stderr/code. * Supports timeout and abort signal. */export async function execCommand( command: string, args: string[], cwd: string, options?: ExecOptions,): Promise<ExecResult> { return new Promise((resolve) => { const proc = spawn(command, args, { cwd, shell: false, stdio: ["ignore", "pipe", "pipe"], });
let stdout = ""; let stderr = ""; let killed = false; let timeoutId: NodeJS.Timeout | undefined;
const killProcess = () => { if (!killed) { killed = true; proc.kill("SIGTERM"); // Force kill after 5 seconds if SIGTERM doesn't work setTimeout(() => { if (!proc.killed) { proc.kill("SIGKILL"); } }, 5000); } };
// Handle abort signal if (options?.signal) { if (options.signal.aborted) { killProcess(); } else { options.signal.addEventListener("abort", killProcess, { once: true }); } }
// Handle timeout if (options?.timeout && options.timeout > 0) { timeoutId = setTimeout(() => { killProcess(); }, options.timeout); }
proc.stdout?.on("data", (data) => { stdout += data.toString(); });
proc.stderr?.on("data", (data) => { stderr += data.toString(); });
// Wait for process termination without hanging on inherited stdio handles // held open by detached descendants. waitForChildProcess(proc) .then((code) => { if (timeoutId) clearTimeout(timeoutId); if (options?.signal) { options.signal.removeEventListener("abort", killProcess); } resolve({ stdout, stderr, code: code ?? 0, killed }); }) .catch((_err) => { if (timeoutId) clearTimeout(timeoutId); if (options?.signal) { options.signal.removeEventListener("abort", killProcess); } resolve({ stdout, stderr, code: 1, killed }); }); });}