This commit is contained in:
2026-04-26 16:19:09 -05:00
parent cb4e28e7b1
commit 8178cd0a08
3 changed files with 184 additions and 44 deletions

View File

@@ -1,21 +1,71 @@
import { execFile } from 'node:child_process';
import { spawn } from 'node:child_process';
export interface ShellResult {
export interface RunResult {
stdout: string;
stderr: string;
code: number;
}
export function run(cmd: string, args: string[], timeoutMs = 120000): Promise<ShellResult> {
export function run(
command: string,
args: string[] = [],
timeoutMs = 120000,
): Promise<RunResult> {
return new Promise((resolve, reject) => {
execFile(cmd, args, { timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
const err = new Error(`Command failed: ${cmd} ${args.join(' ')}\n${stderr || error.message}`);
(err as any).stdout = stdout;
(err as any).stderr = stderr;
const printable = [command, ...args].join(' ');
console.log(`[shell] running: ${printable}`);
const child = spawn(command, args, {
stdio: ['ignore', 'pipe', 'pipe'],
env: process.env,
});
let stdout = '';
let stderr = '';
const timer = setTimeout(() => {
child.kill('SIGKILL');
reject(new Error(`Command timed out after ${timeoutMs}ms: ${printable}`));
}, timeoutMs);
child.stdout.on('data', (chunk) => {
stdout += chunk.toString();
});
child.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
child.on('error', (err) => {
clearTimeout(timer);
console.error(`[shell] failed to start: ${printable}`);
console.error(`[shell] error: ${err.message}`);
reject(err);
});
child.on('close', (code) => {
clearTimeout(timer);
const exitCode = code ?? -1;
if (stdout.trim()) {
console.log(`[shell] stdout for ${printable}:\n${stdout.trim()}`);
}
if (stderr.trim()) {
console.warn(`[shell] stderr for ${printable}:\n${stderr.trim()}`);
}
if (exitCode !== 0) {
const err = new Error(
`Command failed with exit code ${exitCode}: ${printable}\n${stderr || stdout}`,
);
reject(err);
return;
}
resolve({ stdout, stderr });
console.log(`[shell] completed: ${printable}`);
resolve({ stdout, stderr, code: exitCode });
});
});
}