Run a command, capture output, throw on non-zero exit.
(cmd: string, args: string[], opts: { stdin?: string; timeoutMs?: number } = {})
| 56 | |
| 57 | /** Run a command, capture output, throw on non-zero exit. */ |
| 58 | function runCmd(cmd: string, args: string[], opts: { stdin?: string; timeoutMs?: number } = {}): Promise<{ stdout: string; stderr: string }> { |
| 59 | return new Promise((resolve, reject) => { |
| 60 | const child = spawn(cmd, args, { stdio: ['pipe', 'pipe', 'pipe'] }); |
| 61 | let stdout = ''; |
| 62 | let stderr = ''; |
| 63 | const timer = opts.timeoutMs ? setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, opts.timeoutMs) : null; |
| 64 | child.stdout.on('data', (d: Buffer) => { stdout += d.toString('utf-8'); }); |
| 65 | child.stderr.on('data', (d: Buffer) => { stderr += d.toString('utf-8'); }); |
| 66 | child.on('error', (err) => { if (timer) clearTimeout(timer); reject(err); }); |
| 67 | child.on('exit', (code) => { |
| 68 | if (timer) clearTimeout(timer); |
| 69 | if (code !== 0) reject(new Error(`${cmd} exited ${code}: ${stderr.trim() || stdout.trim()}`)); |
| 70 | else resolve({ stdout, stderr }); |
| 71 | }); |
| 72 | if (opts.stdin) { |
| 73 | child.stdin?.write(opts.stdin); |
| 74 | child.stdin?.end(); |
| 75 | } else { |
| 76 | child.stdin?.end(); |
| 77 | } |
| 78 | }); |
| 79 | } |
| 80 | |
| 81 | async function which(cmd: string): Promise<string | null> { |
| 82 | try { |