UNPKG

@xec-sh/core

Version:

Universal shell execution engine

135 lines 5.1 kB
import { spawn } from 'child_process'; export function createInteractiveSession(command, options = {}) { const parts = command.split(' '); const cmd = parts[0]; const args = parts.slice(1); if (!cmd) { throw new Error('Command cannot be empty'); } const spawnOptions = { cwd: options.cwd, env: { ...process.env, ...options.env }, shell: options.shell, stdio: ['pipe', 'pipe', 'pipe'] }; const childProcess = spawn(cmd, args, spawnOptions); let outputBuffer = ''; const stderrListeners = []; const exitListeners = []; const errorListeners = []; const dataListeners = []; let expectResolvers = []; childProcess.stdout?.on('data', (data) => { const str = data.toString(options.encoding || 'utf8'); outputBuffer += str; dataListeners.forEach(listener => listener(str)); const toRemove = []; expectResolvers.forEach(resolver => { const patterns = Array.isArray(resolver.pattern) ? resolver.pattern : [resolver.pattern]; for (const pattern of patterns) { const isMatch = typeof pattern === 'string' ? outputBuffer.includes(pattern) : pattern.test(outputBuffer); if (isMatch) { if (resolver.timeout) clearTimeout(resolver.timeout); resolver.resolve(outputBuffer); outputBuffer = ''; toRemove.push(resolver); break; } } }); toRemove.forEach(r => { const index = expectResolvers.indexOf(r); if (index > -1) expectResolvers.splice(index, 1); }); }); childProcess.stderr?.on('data', (data) => { const str = data.toString(options.encoding || 'utf8'); stderrListeners.forEach(listener => listener(str)); dataListeners.forEach(listener => listener(str)); }); childProcess.on('exit', (code) => { exitListeners.forEach(listener => listener(code)); }); childProcess.on('error', (error) => { errorListeners.forEach(listener => listener(error)); expectResolvers.forEach(resolver => { if (resolver.timeout) clearTimeout(resolver.timeout); resolver.reject(error); }); expectResolvers = []; }); const session = { async send(data, addNewline = true) { return new Promise((resolve, reject) => { const toSend = addNewline ? data + '\n' : data; childProcess.stdin?.write(toSend, (error) => { if (error) reject(error); else resolve(); }); }); }, sendRaw(data) { childProcess.stdin?.write(data); }, async expect(pattern, opts = {}) { const timeout = opts.timeout ?? options.timeout ?? 5000; return new Promise((resolve, reject) => { const resolver = { pattern, resolve, reject }; resolver.timeout = setTimeout(() => { const index = expectResolvers.indexOf(resolver); if (index > -1) expectResolvers.splice(index, 1); reject(new Error(`Timeout waiting for pattern: ${pattern}`)); }, timeout); expectResolvers.push(resolver); const patterns = Array.isArray(pattern) ? pattern : [pattern]; for (const p of patterns) { const isMatch = typeof p === 'string' ? outputBuffer.includes(p) : p.test(outputBuffer); if (isMatch) { clearTimeout(resolver.timeout); const result = outputBuffer; outputBuffer = ''; resolve(result); return; } } }); }, async waitForOutput(text) { return this.expect(text); }, async close(force = false) { childProcess.kill(force ? 'SIGKILL' : 'SIGTERM'); childProcess.removeAllListeners(); if (childProcess.stdout) childProcess.stdout.removeAllListeners(); if (childProcess.stderr) childProcess.stderr.removeAllListeners(); if (childProcess.stdin) childProcess.stdin.removeAllListeners(); }, onStderr(callback) { stderrListeners.push(callback); }, onExit(callback) { exitListeners.push(callback); }, onError(callback) { errorListeners.push(callback); }, onData(callback) { dataListeners.push(callback); } }; return session; } //# sourceMappingURL=interactive-process.js.map