diff --git a/apps/cli/src/application/lib/command-executor.ts b/apps/cli/src/application/lib/command-executor.ts new file mode 100644 index 00000000..c8ba6939 --- /dev/null +++ b/apps/cli/src/application/lib/command-executor.ts @@ -0,0 +1,80 @@ +import { exec, execSync } from 'child_process'; +import { promisify } from 'util'; + +const execPromise = promisify(exec); + +export interface CommandResult { + stdout: string; + stderr: string; + exitCode: number; +} + +/** + * Executes an arbitrary shell command + * @param command - The command to execute (e.g., "cat abc.txt | grep 'abc@gmail.com'") + * @param options - Optional execution options + * @returns Promise with stdout, stderr, and exit code + */ +export async function executeCommand( + command: string, + options?: { + cwd?: string; + timeout?: number; // timeout in milliseconds + maxBuffer?: number; // max buffer size in bytes + } +): Promise { + try { + const { stdout, stderr } = await execPromise(command, { + cwd: options?.cwd, + timeout: options?.timeout, + maxBuffer: options?.maxBuffer || 1024 * 1024, // default 1MB + shell: '/bin/sh', // use sh for cross-platform compatibility + }); + + return { + stdout: stdout.trim(), + stderr: stderr.trim(), + exitCode: 0, + }; + } catch (error: any) { + // exec throws an error if the command fails or times out + return { + stdout: error.stdout?.trim() || '', + stderr: error.stderr?.trim() || error.message, + exitCode: error.code || 1, + }; + } +} + +/** + * Executes a command synchronously (blocking) + * Use with caution - prefer executeCommand for async execution + */ +export function executeCommandSync( + command: string, + options?: { + cwd?: string; + timeout?: number; + } +): CommandResult { + try { + const stdout = execSync(command, { + cwd: options?.cwd, + timeout: options?.timeout, + encoding: 'utf-8', + shell: '/bin/sh', + }); + + return { + stdout: stdout.trim(), + stderr: '', + exitCode: 0, + }; + } catch (error: any) { + return { + stdout: error.stdout?.toString().trim() || '', + stderr: error.stderr?.toString().trim() || error.message, + exitCode: error.status || 1, + }; + } +}