@xec-sh/core
Version:
Universal shell execution engine
135 lines • 5.1 kB
JavaScript
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