feat(smartshell): Add secure spawn APIs, PTY support, interactive/streaming control, timeouts and buffer limits; update README and tests

This commit is contained in:
2025-08-17 15:20:26 +00:00
parent f8e431f41e
commit 529b33fda1
10 changed files with 1748 additions and 615 deletions

View File

@@ -0,0 +1,84 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartshell from '../ts/index.js';
tap.test('should handle programmatic input control with simple commands', async (tools) => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Use cat which works well with pipe mode
const interactive = await testSmartshell.execInteractiveControl('cat');
// Send input programmatically
await interactive.sendLine('TestUser');
interactive.endInput();
// Wait for completion
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('TestUser');
});
tap.test('should handle streaming interactive control with cat', async (tools) => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Use cat for reliable pipe mode operation
const streaming = await testSmartshell.execStreamingInteractiveControl('cat');
// Send multiple inputs
await streaming.sendLine('One');
await streaming.sendLine('Two');
streaming.endInput();
const result = await streaming.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('One');
expect(result.stdout).toContain('Two');
});
tap.test('should handle sendInput without newline', async (tools) => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Use cat for testing input without newline
const interactive = await testSmartshell.execInteractiveControl('cat');
// Send characters without newline, then newline, then EOF
await interactive.sendInput('ABC');
await interactive.sendInput('DEF');
await interactive.sendInput('\n');
interactive.endInput();
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('ABCDEF');
});
tap.test('should mix passthrough and interactive control modes', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Test that passthrough still works
const passthroughResult = await testSmartshell.execPassthrough('echo "Passthrough works"');
expect(passthroughResult.exitCode).toEqual(0);
expect(passthroughResult.stdout).toContain('Passthrough works');
// Test that interactive control works
const interactiveResult = await testSmartshell.execInteractiveControl('echo "Interactive control works"');
const finalResult = await interactiveResult.finalPromise;
expect(finalResult.exitCode).toEqual(0);
expect(finalResult.stdout).toContain('Interactive control works');
});
// Note: Tests requiring bash read with prompts should use PTY mode
// See test.pty.ts for examples of testing commands that require terminal features
export default tap.start();