/** * Shell command execution utilities */ import { spawn } from 'child_process'; export interface ExecResult { stdout: string; stderr: string; code: number; } /** * Execute a shell command and return a promise with stdout */ export function execCommand(command: string, args: string[] = []): Promise { return new Promise((resolve, reject) => { const proc = spawn(command, args, { stdio: 'pipe', }); let stdout = ''; let stderr = ''; if (proc.stdout) { proc.stdout.on('data', (data) => { stdout += data.toString(); }); } if (proc.stderr) { proc.stderr.on('data', (data) => { stderr += data.toString(); }); } proc.on('close', (code) => { if (code === 0) { resolve(stdout); } else { reject(new Error(`Command failed with code ${code}: ${stderr}`)); } }); proc.on('error', reject); }); } /** * Execute a shell command with stdout/stderr streamed to the console in real time. * Returns the exit code. */ export function execCommandStreaming(command: string, args: string[] = []): Promise { return new Promise((resolve) => { const proc = spawn(command, args, { stdio: 'inherit', }); proc.on('close', (code) => { resolve(code ?? 1); }); proc.on('error', () => { resolve(1); }); }); } /** * Execute a shell command and return full result (doesn't throw on non-zero exit) */ export function execCommandFull(command: string, args: string[] = []): Promise { return new Promise((resolve) => { const proc = spawn(command, args, { stdio: 'pipe', }); let stdout = ''; let stderr = ''; if (proc.stdout) { proc.stdout.on('data', (data) => { stdout += data.toString(); }); } if (proc.stderr) { proc.stderr.on('data', (data) => { stderr += data.toString(); }); } proc.on('close', (code) => { resolve({ stdout, stderr, code: code ?? 1 }); }); proc.on('error', (err) => { resolve({ stdout, stderr: err.message, code: 1 }); }); }); }