start-command 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -231
- package/bun.lock +5 -0
- package/eslint.config.mjs +1 -1
- package/package.json +11 -6
- package/src/bin/cli.js +275 -137
- package/src/lib/args-parser.js +118 -0
- package/src/lib/execution-store.js +722 -0
- package/src/lib/isolation.js +51 -0
- package/src/lib/status-formatter.js +121 -0
- package/src/lib/version.js +143 -0
- package/test/args-parser.test.js +107 -0
- package/test/cli.test.js +11 -1
- package/test/docker-autoremove.test.js +11 -16
- package/test/execution-store.test.js +483 -0
- package/test/isolation-cleanup.test.js +11 -16
- package/test/isolation.test.js +11 -17
- package/test/public-exports.test.js +105 -0
- package/test/status-query.test.js +195 -0
- package/.github/workflows/release.yml +0 -352
- package/.husky/pre-commit +0 -1
- package/ARCHITECTURE.md +0 -297
- package/LICENSE +0 -24
- package/README.md +0 -339
- package/REQUIREMENTS.md +0 -299
- package/docs/PIPES.md +0 -243
- package/docs/USAGE.md +0 -194
- package/docs/case-studies/issue-15/README.md +0 -208
- package/docs/case-studies/issue-18/README.md +0 -343
- package/docs/case-studies/issue-18/issue-comments.json +0 -1
- package/docs/case-studies/issue-18/issue-data.json +0 -7
- package/docs/case-studies/issue-22/analysis.md +0 -547
- package/docs/case-studies/issue-22/issue-data.json +0 -12
- package/docs/case-studies/issue-25/README.md +0 -232
- package/docs/case-studies/issue-25/issue-data.json +0 -21
- package/docs/case-studies/issue-28/README.md +0 -405
- package/docs/case-studies/issue-28/issue-data.json +0 -105
- package/docs/case-studies/issue-28/raw-issue-data.md +0 -92
- package/experiments/debug-regex.js +0 -49
- package/experiments/isolation-design.md +0 -131
- package/experiments/screen-output-test.js +0 -265
- package/experiments/test-cli.sh +0 -42
- package/experiments/test-command-stream-cjs.cjs +0 -30
- package/experiments/test-command-stream-wrapper.js +0 -54
- package/experiments/test-command-stream.mjs +0 -56
- package/experiments/test-screen-attached.js +0 -126
- package/experiments/test-screen-logfile.js +0 -286
- package/experiments/test-screen-modes.js +0 -128
- package/experiments/test-screen-output.sh +0 -27
- package/experiments/test-screen-tee-debug.js +0 -237
- package/experiments/test-screen-tee-fallback.js +0 -230
- package/experiments/test-substitution.js +0 -143
- package/experiments/user-isolation-research.md +0 -83
- package/scripts/changeset-version.mjs +0 -38
- package/scripts/check-file-size.mjs +0 -103
- package/scripts/create-github-release.mjs +0 -93
- package/scripts/create-manual-changeset.mjs +0 -89
- package/scripts/format-github-release.mjs +0 -83
- package/scripts/format-release-notes.mjs +0 -219
- package/scripts/instant-version-bump.mjs +0 -121
- package/scripts/publish-to-npm.mjs +0 -129
- package/scripts/setup-npm.mjs +0 -37
- package/scripts/validate-changeset.mjs +0 -107
- package/scripts/version-and-commit.mjs +0 -237
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/* global console */
|
|
3
|
-
/**
|
|
4
|
-
* Experiment: Test command-stream with CommonJS require
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
(async () => {
|
|
8
|
-
console.log(
|
|
9
|
-
'=== Testing command-stream with dynamic import in CommonJS ===\n'
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
// Need to use dynamic import since command-stream is ESM
|
|
13
|
-
const { $ } = await import('command-stream');
|
|
14
|
-
|
|
15
|
-
// Test 1: Basic sync command
|
|
16
|
-
console.log('Test 1: Basic sync command');
|
|
17
|
-
const result1 = $`echo "Hello from CJS"`.sync();
|
|
18
|
-
console.log('stdout:', result1.stdout);
|
|
19
|
-
console.log('exit code:', result1.code);
|
|
20
|
-
console.log('');
|
|
21
|
-
|
|
22
|
-
// Test 2: which command
|
|
23
|
-
console.log('Test 2: which command');
|
|
24
|
-
const result2 = $`which echo`.sync();
|
|
25
|
-
console.log('stdout:', result2.stdout.trim());
|
|
26
|
-
console.log('exit code:', result2.code);
|
|
27
|
-
console.log('');
|
|
28
|
-
|
|
29
|
-
console.log('=== All tests completed ===');
|
|
30
|
-
})();
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Experiment: Test command-stream wrapper module
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
execCommand,
|
|
8
|
-
execCommandAsync,
|
|
9
|
-
commandExists,
|
|
10
|
-
getCommandPath,
|
|
11
|
-
getToolVersion,
|
|
12
|
-
} = require('../src/lib/command-stream');
|
|
13
|
-
|
|
14
|
-
(async () => {
|
|
15
|
-
console.log('=== Testing command-stream wrapper ===\n');
|
|
16
|
-
|
|
17
|
-
// Test 1: Basic sync command
|
|
18
|
-
console.log('Test 1: execCommand (sync-like via promise)');
|
|
19
|
-
const result1 = await execCommand('echo "Hello from wrapper"');
|
|
20
|
-
console.log('stdout:', result1.stdout);
|
|
21
|
-
console.log('code:', result1.code);
|
|
22
|
-
console.log('');
|
|
23
|
-
|
|
24
|
-
// Test 2: Check if command exists
|
|
25
|
-
console.log('Test 2: commandExists');
|
|
26
|
-
const hasScreen = await commandExists('screen');
|
|
27
|
-
const hasFakeCmd = await commandExists('nonexistent-command-xyz');
|
|
28
|
-
console.log('screen exists:', hasScreen);
|
|
29
|
-
console.log('nonexistent-command-xyz exists:', hasFakeCmd);
|
|
30
|
-
console.log('');
|
|
31
|
-
|
|
32
|
-
// Test 3: Get command path
|
|
33
|
-
console.log('Test 3: getCommandPath');
|
|
34
|
-
const echoPath = await getCommandPath('echo');
|
|
35
|
-
console.log('echo path:', echoPath);
|
|
36
|
-
console.log('');
|
|
37
|
-
|
|
38
|
-
// Test 4: Get tool version
|
|
39
|
-
console.log('Test 4: getToolVersion');
|
|
40
|
-
const screenVersion = await getToolVersion('screen', '-v', true);
|
|
41
|
-
console.log('screen version:', screenVersion);
|
|
42
|
-
const tmuxVersion = await getToolVersion('tmux', '-V', true);
|
|
43
|
-
console.log('tmux version:', tmuxVersion);
|
|
44
|
-
console.log('');
|
|
45
|
-
|
|
46
|
-
// Test 5: Async command
|
|
47
|
-
console.log('Test 5: execCommandAsync');
|
|
48
|
-
const result5 = await execCommandAsync('uname -r');
|
|
49
|
-
console.log('stdout:', result5.stdout);
|
|
50
|
-
console.log('code:', result5.code);
|
|
51
|
-
console.log('');
|
|
52
|
-
|
|
53
|
-
console.log('=== All tests completed ===');
|
|
54
|
-
})();
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Experiment: Test command-stream basic usage
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { $ } from 'command-stream';
|
|
7
|
-
|
|
8
|
-
console.log('=== Testing command-stream ===\n');
|
|
9
|
-
|
|
10
|
-
// Test 1: Basic async command
|
|
11
|
-
console.log('Test 1: Basic async command');
|
|
12
|
-
const result1 = await $`echo "Hello from command-stream"`;
|
|
13
|
-
console.log('stdout:', result1.stdout);
|
|
14
|
-
console.log('exit code:', result1.code);
|
|
15
|
-
console.log('');
|
|
16
|
-
|
|
17
|
-
// Test 2: Sync command
|
|
18
|
-
console.log('Test 2: Sync command');
|
|
19
|
-
const result2 = $`echo "Sync hello"`.sync();
|
|
20
|
-
console.log('stdout:', result2.stdout);
|
|
21
|
-
console.log('exit code:', result2.code);
|
|
22
|
-
console.log('');
|
|
23
|
-
|
|
24
|
-
// Test 3: Command that uses system tools
|
|
25
|
-
console.log('Test 3: which command');
|
|
26
|
-
try {
|
|
27
|
-
const result3 = $`which echo`.sync();
|
|
28
|
-
console.log('stdout:', result3.stdout);
|
|
29
|
-
console.log('exit code:', result3.code);
|
|
30
|
-
} catch (e) {
|
|
31
|
-
console.log('Error:', e.message);
|
|
32
|
-
}
|
|
33
|
-
console.log('');
|
|
34
|
-
|
|
35
|
-
// Test 4: Get OS version on Linux
|
|
36
|
-
console.log('Test 4: uname command');
|
|
37
|
-
const result4 = $`uname -r`.sync();
|
|
38
|
-
console.log('stdout:', result4.stdout.trim());
|
|
39
|
-
console.log('exit code:', result4.code);
|
|
40
|
-
console.log('');
|
|
41
|
-
|
|
42
|
-
// Test 5: Silent mode (no mirror)
|
|
43
|
-
console.log('Test 5: Silent mode with $({ mirror: false })');
|
|
44
|
-
const $silent = $({ mirror: false, capture: true });
|
|
45
|
-
const result5 = await $silent`echo "This should be silent"`;
|
|
46
|
-
console.log('stdout:', result5.stdout);
|
|
47
|
-
console.log('');
|
|
48
|
-
|
|
49
|
-
// Test 6: Check if command exists
|
|
50
|
-
console.log('Test 6: Check if screen exists');
|
|
51
|
-
const result6 = $`which screen`.sync();
|
|
52
|
-
console.log('screen exists:', result6.code === 0);
|
|
53
|
-
console.log('screen path:', result6.stdout.trim());
|
|
54
|
-
console.log('');
|
|
55
|
-
|
|
56
|
-
console.log('=== All tests completed ===');
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Experiment to test different approaches for running screen in attached mode
|
|
4
|
-
* from Bun without a TTY
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { spawn, spawnSync, execSync } = require('child_process');
|
|
8
|
-
|
|
9
|
-
async function testApproaches() {
|
|
10
|
-
console.log('=== Testing Attached Mode Approaches ===\n');
|
|
11
|
-
|
|
12
|
-
// Approach 1: Using script command as wrapper
|
|
13
|
-
console.log('Approach 1: script -q -c "screen ..." /dev/null');
|
|
14
|
-
try {
|
|
15
|
-
const sessionName = `approach1-${Date.now()}`;
|
|
16
|
-
const result = spawnSync(
|
|
17
|
-
'script',
|
|
18
|
-
[
|
|
19
|
-
'-q',
|
|
20
|
-
'-c',
|
|
21
|
-
`screen -S ${sessionName} /bin/sh -c "echo hello; exit 0"`,
|
|
22
|
-
'/dev/null',
|
|
23
|
-
],
|
|
24
|
-
{
|
|
25
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
26
|
-
timeout: 5000,
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
console.log(
|
|
30
|
-
` stdout: "${result.stdout.toString().trim().replace(/\n/g, '\\n')}"`
|
|
31
|
-
);
|
|
32
|
-
console.log(
|
|
33
|
-
` stderr: "${result.stderr.toString().trim().replace(/\n/g, '\\n')}"`
|
|
34
|
-
);
|
|
35
|
-
console.log(` exit: ${result.status}`);
|
|
36
|
-
// Cleanup
|
|
37
|
-
try {
|
|
38
|
-
execSync(`screen -S ${sessionName} -X quit 2>/dev/null`);
|
|
39
|
-
} catch {}
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.log(` Error: ${e.message}`);
|
|
42
|
-
}
|
|
43
|
-
console.log('');
|
|
44
|
-
|
|
45
|
-
// Approach 2: Using detached mode + wait for completion + capture output via log
|
|
46
|
-
console.log('Approach 2: detached + log capture');
|
|
47
|
-
try {
|
|
48
|
-
const sessionName = `approach2-${Date.now()}`;
|
|
49
|
-
const logFile = `/tmp/screen-${sessionName}.log`;
|
|
50
|
-
|
|
51
|
-
// Start with logging
|
|
52
|
-
execSync(
|
|
53
|
-
`screen -dmS ${sessionName} -L -Logfile ${logFile} /bin/sh -c "echo 'hello from approach2'"`,
|
|
54
|
-
{
|
|
55
|
-
stdio: 'inherit',
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// Wait for completion
|
|
60
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
61
|
-
|
|
62
|
-
// Read log
|
|
63
|
-
const output = execSync(`cat ${logFile}`, { encoding: 'utf8' });
|
|
64
|
-
console.log(` Output: "${output.trim()}"`);
|
|
65
|
-
console.log(` Status: SUCCESS`);
|
|
66
|
-
|
|
67
|
-
// Cleanup
|
|
68
|
-
try {
|
|
69
|
-
execSync(`rm ${logFile} 2>/dev/null`);
|
|
70
|
-
} catch {}
|
|
71
|
-
try {
|
|
72
|
-
execSync(`screen -S ${sessionName} -X quit 2>/dev/null`);
|
|
73
|
-
} catch {}
|
|
74
|
-
} catch (e) {
|
|
75
|
-
console.log(` Error: ${e.message}`);
|
|
76
|
-
}
|
|
77
|
-
console.log('');
|
|
78
|
-
|
|
79
|
-
// Approach 3: Using stdio: 'inherit' with proper terminal allocation via script
|
|
80
|
-
console.log('Approach 3: Run through script, inherit all stdio');
|
|
81
|
-
try {
|
|
82
|
-
const sessionName = `approach3-${Date.now()}`;
|
|
83
|
-
const result = spawnSync(
|
|
84
|
-
'script',
|
|
85
|
-
[
|
|
86
|
-
'-q',
|
|
87
|
-
'-e',
|
|
88
|
-
'-c',
|
|
89
|
-
`screen -S ${sessionName} /bin/sh -c "echo hello_approach3"`,
|
|
90
|
-
'/dev/null',
|
|
91
|
-
],
|
|
92
|
-
{
|
|
93
|
-
stdio: 'inherit',
|
|
94
|
-
timeout: 5000,
|
|
95
|
-
}
|
|
96
|
-
);
|
|
97
|
-
console.log(` exit: ${result.status}`);
|
|
98
|
-
// Cleanup
|
|
99
|
-
try {
|
|
100
|
-
execSync(`screen -S ${sessionName} -X quit 2>/dev/null`);
|
|
101
|
-
} catch {}
|
|
102
|
-
} catch (e) {
|
|
103
|
-
console.log(` Error: ${e.message}`);
|
|
104
|
-
}
|
|
105
|
-
console.log('');
|
|
106
|
-
|
|
107
|
-
// Approach 4: Direct run without screen for attached mode (fallback)
|
|
108
|
-
console.log(
|
|
109
|
-
'Approach 4: Just spawn the shell command directly (fallback, no screen)'
|
|
110
|
-
);
|
|
111
|
-
try {
|
|
112
|
-
const result = spawnSync('/bin/sh', ['-c', 'echo hello_direct'], {
|
|
113
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
114
|
-
timeout: 5000,
|
|
115
|
-
});
|
|
116
|
-
console.log(` stdout: "${result.stdout.toString().trim()}"`);
|
|
117
|
-
console.log(` exit: ${result.status}`);
|
|
118
|
-
} catch (e) {
|
|
119
|
-
console.log(` Error: ${e.message}`);
|
|
120
|
-
}
|
|
121
|
-
console.log('');
|
|
122
|
-
|
|
123
|
-
console.log('=== Approaches Tested ===');
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
testApproaches();
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Experiment to test screen's logfile capture functionality
|
|
4
|
-
* to understand the root cause of issue #15
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { execSync, spawnSync } = require('child_process');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const os = require('os');
|
|
11
|
-
|
|
12
|
-
async function sleep(ms) {
|
|
13
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function testScreenLogfile() {
|
|
17
|
-
console.log('=== Testing Screen Logfile Capture ===\n');
|
|
18
|
-
|
|
19
|
-
// Test environment info
|
|
20
|
-
console.log('Environment:');
|
|
21
|
-
console.log(` Platform: ${process.platform}`);
|
|
22
|
-
console.log(` Node: ${process.version}`);
|
|
23
|
-
try {
|
|
24
|
-
const screenVersion = execSync('screen --version', {
|
|
25
|
-
encoding: 'utf8',
|
|
26
|
-
}).trim();
|
|
27
|
-
console.log(` Screen: ${screenVersion}`);
|
|
28
|
-
} catch (e) {
|
|
29
|
-
console.log(` Screen: Not available - ${e.message}`);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
console.log(
|
|
33
|
-
` TTY: stdin=${process.stdin.isTTY}, stdout=${process.stdout.isTTY}`
|
|
34
|
-
);
|
|
35
|
-
console.log('');
|
|
36
|
-
|
|
37
|
-
// Test 1: Basic logfile capture with -L -Logfile
|
|
38
|
-
console.log('Test 1: Basic -L -Logfile capture');
|
|
39
|
-
const sessionName1 = `logtest-${Date.now()}`;
|
|
40
|
-
const logFile1 = path.join(os.tmpdir(), `screen-log-${sessionName1}.log`);
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
// Run screen with logging
|
|
44
|
-
const screenArgs = [
|
|
45
|
-
'-dmS',
|
|
46
|
-
sessionName1,
|
|
47
|
-
'-L',
|
|
48
|
-
'-Logfile',
|
|
49
|
-
logFile1,
|
|
50
|
-
'/bin/sh',
|
|
51
|
-
'-c',
|
|
52
|
-
'echo "TESTOUTPUT123"',
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
console.log(` Command: screen ${screenArgs.join(' ')}`);
|
|
56
|
-
|
|
57
|
-
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
58
|
-
stdio: 'inherit',
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Wait for completion (screen runs command and exits)
|
|
62
|
-
await sleep(500);
|
|
63
|
-
|
|
64
|
-
// Check if session still exists
|
|
65
|
-
let sessionExists = false;
|
|
66
|
-
try {
|
|
67
|
-
const sessions = execSync('screen -ls', {
|
|
68
|
-
encoding: 'utf8',
|
|
69
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
70
|
-
});
|
|
71
|
-
sessionExists = sessions.includes(sessionName1);
|
|
72
|
-
} catch {
|
|
73
|
-
// screen -ls returns non-zero if no sessions
|
|
74
|
-
}
|
|
75
|
-
console.log(` Session exists after 500ms: ${sessionExists}`);
|
|
76
|
-
|
|
77
|
-
// Check log file
|
|
78
|
-
if (fs.existsSync(logFile1)) {
|
|
79
|
-
const content = fs.readFileSync(logFile1, 'utf8');
|
|
80
|
-
console.log(` Log file exists: YES`);
|
|
81
|
-
console.log(` Log file size: ${content.length} bytes`);
|
|
82
|
-
console.log(
|
|
83
|
-
` Log content: "${content.trim().replace(/\n/g, '\\n').slice(0, 200)}"`
|
|
84
|
-
);
|
|
85
|
-
console.log(
|
|
86
|
-
` Contains expected output: ${content.includes('TESTOUTPUT123') ? 'YES ✓' : 'NO ✗'}`
|
|
87
|
-
);
|
|
88
|
-
fs.unlinkSync(logFile1);
|
|
89
|
-
} else {
|
|
90
|
-
console.log(` Log file exists: NO ✗`);
|
|
91
|
-
console.log(` Expected path: ${logFile1}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Cleanup
|
|
95
|
-
try {
|
|
96
|
-
execSync(`screen -S ${sessionName1} -X quit 2>/dev/null`);
|
|
97
|
-
} catch {}
|
|
98
|
-
} catch (e) {
|
|
99
|
-
console.log(` Error: ${e.message}`);
|
|
100
|
-
}
|
|
101
|
-
console.log('');
|
|
102
|
-
|
|
103
|
-
// Test 2: Test with sleep to ensure buffer flush
|
|
104
|
-
console.log('Test 2: With sleep for buffer flush');
|
|
105
|
-
const sessionName2 = `logtest2-${Date.now()}`;
|
|
106
|
-
const logFile2 = path.join(os.tmpdir(), `screen-log-${sessionName2}.log`);
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const screenArgs = [
|
|
110
|
-
'-dmS',
|
|
111
|
-
sessionName2,
|
|
112
|
-
'-L',
|
|
113
|
-
'-Logfile',
|
|
114
|
-
logFile2,
|
|
115
|
-
'/bin/sh',
|
|
116
|
-
'-c',
|
|
117
|
-
'echo "FLUSHED_OUTPUT" && sleep 0.5',
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
console.log(` Command: screen ${screenArgs.join(' ')}`);
|
|
121
|
-
|
|
122
|
-
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
123
|
-
stdio: 'inherit',
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Wait longer for flush (default is 10 seconds)
|
|
127
|
-
await sleep(1500);
|
|
128
|
-
|
|
129
|
-
// Check log file
|
|
130
|
-
if (fs.existsSync(logFile2)) {
|
|
131
|
-
const content = fs.readFileSync(logFile2, 'utf8');
|
|
132
|
-
console.log(` Log file exists: YES`);
|
|
133
|
-
console.log(` Log file size: ${content.length} bytes`);
|
|
134
|
-
console.log(
|
|
135
|
-
` Contains expected output: ${content.includes('FLUSHED_OUTPUT') ? 'YES ✓' : 'NO ✗'}`
|
|
136
|
-
);
|
|
137
|
-
fs.unlinkSync(logFile2);
|
|
138
|
-
} else {
|
|
139
|
-
console.log(` Log file exists: NO ✗`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Cleanup
|
|
143
|
-
try {
|
|
144
|
-
execSync(`screen -S ${sessionName2} -X quit 2>/dev/null`);
|
|
145
|
-
} catch {}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
console.log(` Error: ${e.message}`);
|
|
148
|
-
}
|
|
149
|
-
console.log('');
|
|
150
|
-
|
|
151
|
-
// Test 3: Alternative - using hardstatus/output redirection
|
|
152
|
-
console.log('Test 3: Direct command output capture (no screen logging)');
|
|
153
|
-
const sessionName3 = `logtest3-${Date.now()}`;
|
|
154
|
-
const logFile3 = path.join(os.tmpdir(), `screen-log-${sessionName3}.log`);
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
// Run command through screen but capture output to file within the command
|
|
158
|
-
const command = `echo "DIRECT_CAPTURE" | tee ${logFile3}`;
|
|
159
|
-
const screenArgs = ['-dmS', sessionName3, '/bin/sh', '-c', command];
|
|
160
|
-
|
|
161
|
-
console.log(` Command: screen ${screenArgs.join(' ')}`);
|
|
162
|
-
|
|
163
|
-
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
164
|
-
stdio: 'inherit',
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
await sleep(500);
|
|
168
|
-
|
|
169
|
-
// Check log file
|
|
170
|
-
if (fs.existsSync(logFile3)) {
|
|
171
|
-
const content = fs.readFileSync(logFile3, 'utf8');
|
|
172
|
-
console.log(` Log file exists: YES`);
|
|
173
|
-
console.log(
|
|
174
|
-
` Contains expected output: ${content.includes('DIRECT_CAPTURE') ? 'YES ✓' : 'NO ✗'}`
|
|
175
|
-
);
|
|
176
|
-
fs.unlinkSync(logFile3);
|
|
177
|
-
} else {
|
|
178
|
-
console.log(` Log file exists: NO ✗`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Cleanup
|
|
182
|
-
try {
|
|
183
|
-
execSync(`screen -S ${sessionName3} -X quit 2>/dev/null`);
|
|
184
|
-
} catch {}
|
|
185
|
-
} catch (e) {
|
|
186
|
-
console.log(` Error: ${e.message}`);
|
|
187
|
-
}
|
|
188
|
-
console.log('');
|
|
189
|
-
|
|
190
|
-
// Test 4: Script command approach
|
|
191
|
-
console.log('Test 4: Using script command to capture output');
|
|
192
|
-
const sessionName4 = `logtest4-${Date.now()}`;
|
|
193
|
-
const logFile4 = path.join(os.tmpdir(), `script-log-${sessionName4}.log`);
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
// Use script to capture output
|
|
197
|
-
const result = spawnSync(
|
|
198
|
-
'script',
|
|
199
|
-
[
|
|
200
|
-
'-q',
|
|
201
|
-
logFile4,
|
|
202
|
-
'-c',
|
|
203
|
-
`screen -dmS ${sessionName4} /bin/sh -c "echo SCRIPT_CAPTURE"`,
|
|
204
|
-
],
|
|
205
|
-
{
|
|
206
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
207
|
-
timeout: 5000,
|
|
208
|
-
}
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
await sleep(500);
|
|
212
|
-
|
|
213
|
-
console.log(` Exit code: ${result.status}`);
|
|
214
|
-
|
|
215
|
-
// Check log file
|
|
216
|
-
if (fs.existsSync(logFile4)) {
|
|
217
|
-
const content = fs.readFileSync(logFile4, 'utf8');
|
|
218
|
-
console.log(` Log file exists: YES`);
|
|
219
|
-
console.log(` Log file size: ${content.length} bytes`);
|
|
220
|
-
fs.unlinkSync(logFile4);
|
|
221
|
-
} else {
|
|
222
|
-
console.log(` Log file exists: NO`);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Cleanup
|
|
226
|
-
try {
|
|
227
|
-
execSync(`screen -S ${sessionName4} -X quit 2>/dev/null`);
|
|
228
|
-
} catch {}
|
|
229
|
-
} catch (e) {
|
|
230
|
-
console.log(` Error: ${e.message}`);
|
|
231
|
-
}
|
|
232
|
-
console.log('');
|
|
233
|
-
|
|
234
|
-
// Test 5: Test with -T option (terminal type)
|
|
235
|
-
console.log('Test 5: With explicit terminal type -T xterm');
|
|
236
|
-
const sessionName5 = `logtest5-${Date.now()}`;
|
|
237
|
-
const logFile5 = path.join(os.tmpdir(), `screen-log-${sessionName5}.log`);
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
const screenArgs = [
|
|
241
|
-
'-T',
|
|
242
|
-
'xterm',
|
|
243
|
-
'-dmS',
|
|
244
|
-
sessionName5,
|
|
245
|
-
'-L',
|
|
246
|
-
'-Logfile',
|
|
247
|
-
logFile5,
|
|
248
|
-
'/bin/sh',
|
|
249
|
-
'-c',
|
|
250
|
-
'echo "TERMINAL_OUTPUT" && sleep 0.3',
|
|
251
|
-
];
|
|
252
|
-
|
|
253
|
-
console.log(` Command: screen ${screenArgs.join(' ')}`);
|
|
254
|
-
|
|
255
|
-
execSync(`screen ${screenArgs.map((a) => `"${a}"`).join(' ')}`, {
|
|
256
|
-
stdio: 'inherit',
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
await sleep(1000);
|
|
260
|
-
|
|
261
|
-
// Check log file
|
|
262
|
-
if (fs.existsSync(logFile5)) {
|
|
263
|
-
const content = fs.readFileSync(logFile5, 'utf8');
|
|
264
|
-
console.log(` Log file exists: YES`);
|
|
265
|
-
console.log(` Log file size: ${content.length} bytes`);
|
|
266
|
-
console.log(
|
|
267
|
-
` Contains expected output: ${content.includes('TERMINAL_OUTPUT') ? 'YES ✓' : 'NO ✗'}`
|
|
268
|
-
);
|
|
269
|
-
fs.unlinkSync(logFile5);
|
|
270
|
-
} else {
|
|
271
|
-
console.log(` Log file exists: NO ✗`);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Cleanup
|
|
275
|
-
try {
|
|
276
|
-
execSync(`screen -S ${sessionName5} -X quit 2>/dev/null`);
|
|
277
|
-
} catch {}
|
|
278
|
-
} catch (e) {
|
|
279
|
-
console.log(` Error: ${e.message}`);
|
|
280
|
-
}
|
|
281
|
-
console.log('');
|
|
282
|
-
|
|
283
|
-
console.log('=== Tests Complete ===');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
testScreenLogfile();
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Experiment to test different screen invocation modes
|
|
4
|
-
* This helps understand how screen behaves in different contexts
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { spawn, execSync, spawnSync } = require('child_process');
|
|
8
|
-
|
|
9
|
-
async function testScreenModes() {
|
|
10
|
-
console.log('=== Testing Screen Modes ===\n');
|
|
11
|
-
|
|
12
|
-
// Test 1: Check if running in a terminal
|
|
13
|
-
console.log('1. Terminal check:');
|
|
14
|
-
console.log(` process.stdin.isTTY: ${process.stdin.isTTY}`);
|
|
15
|
-
console.log(` process.stdout.isTTY: ${process.stdout.isTTY}`);
|
|
16
|
-
console.log(` TERM: ${process.env.TERM || 'not set'}`);
|
|
17
|
-
console.log('');
|
|
18
|
-
|
|
19
|
-
// Test 2: Detached mode should work
|
|
20
|
-
console.log('2. Testing detached mode (screen -dmS):');
|
|
21
|
-
try {
|
|
22
|
-
const sessionName = `test-${Date.now()}`;
|
|
23
|
-
execSync(
|
|
24
|
-
`screen -dmS ${sessionName} /bin/sh -c "echo hello > /tmp/screen-test-${sessionName}.txt"`,
|
|
25
|
-
{
|
|
26
|
-
stdio: 'inherit',
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
// Wait a bit for the command to complete
|
|
30
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
31
|
-
|
|
32
|
-
const output = execSync(
|
|
33
|
-
`cat /tmp/screen-test-${sessionName}.txt 2>/dev/null || echo "file not found"`,
|
|
34
|
-
{ encoding: 'utf8' }
|
|
35
|
-
);
|
|
36
|
-
console.log(` Output: ${output.trim()}`);
|
|
37
|
-
|
|
38
|
-
// Cleanup
|
|
39
|
-
try {
|
|
40
|
-
execSync(`screen -S ${sessionName} -X quit 2>/dev/null`);
|
|
41
|
-
} catch {}
|
|
42
|
-
try {
|
|
43
|
-
execSync(`rm /tmp/screen-test-${sessionName}.txt 2>/dev/null`);
|
|
44
|
-
} catch {}
|
|
45
|
-
|
|
46
|
-
console.log(' Status: SUCCESS');
|
|
47
|
-
} catch (e) {
|
|
48
|
-
console.log(` Status: FAILED - ${e.message}`);
|
|
49
|
-
}
|
|
50
|
-
console.log('');
|
|
51
|
-
|
|
52
|
-
// Test 3: Attached mode with spawn (current implementation)
|
|
53
|
-
console.log('3. Testing attached mode with spawn (current implementation):');
|
|
54
|
-
try {
|
|
55
|
-
const sessionName = `test-${Date.now()}`;
|
|
56
|
-
const result = spawnSync(
|
|
57
|
-
'screen',
|
|
58
|
-
['-S', sessionName, '/bin/sh', '-c', 'echo hello'],
|
|
59
|
-
{
|
|
60
|
-
stdio: 'inherit',
|
|
61
|
-
}
|
|
62
|
-
);
|
|
63
|
-
console.log(` Exit code: ${result.status}`);
|
|
64
|
-
console.log(` Status: ${result.status === 0 ? 'SUCCESS' : 'FAILED'}`);
|
|
65
|
-
} catch (e) {
|
|
66
|
-
console.log(` Status: FAILED - ${e.message}`);
|
|
67
|
-
}
|
|
68
|
-
console.log('');
|
|
69
|
-
|
|
70
|
-
// Test 4: Try to use script command to allocate PTY
|
|
71
|
-
console.log('4. Testing with script command to allocate PTY:');
|
|
72
|
-
try {
|
|
73
|
-
const result = spawnSync(
|
|
74
|
-
'script',
|
|
75
|
-
['-q', '-c', 'echo hello', '/dev/null'],
|
|
76
|
-
{
|
|
77
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
console.log(` Output: ${result.stdout.toString().trim()}`);
|
|
81
|
-
console.log(` Exit code: ${result.status}`);
|
|
82
|
-
console.log(` Status: SUCCESS`);
|
|
83
|
-
} catch (e) {
|
|
84
|
-
console.log(` Status: FAILED - ${e.message}`);
|
|
85
|
-
}
|
|
86
|
-
console.log('');
|
|
87
|
-
|
|
88
|
-
// Test 5: Detached mode with log capture
|
|
89
|
-
console.log('5. Testing detached mode with log capture:');
|
|
90
|
-
try {
|
|
91
|
-
const sessionName = `test-${Date.now()}`;
|
|
92
|
-
const logFile = `/tmp/screen-log-${sessionName}.txt`;
|
|
93
|
-
|
|
94
|
-
// Create detached session with logging
|
|
95
|
-
execSync(
|
|
96
|
-
`screen -dmS ${sessionName} -L -Logfile ${logFile} /bin/sh -c "echo 'hello from screen'; sleep 0.2"`,
|
|
97
|
-
{
|
|
98
|
-
stdio: 'inherit',
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Wait for command to complete
|
|
103
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
104
|
-
|
|
105
|
-
const output = execSync(
|
|
106
|
-
`cat ${logFile} 2>/dev/null || echo "log not found"`,
|
|
107
|
-
{ encoding: 'utf8' }
|
|
108
|
-
);
|
|
109
|
-
console.log(` Log content: ${output.trim().replace(/\n/g, '\\n')}`);
|
|
110
|
-
|
|
111
|
-
// Cleanup
|
|
112
|
-
try {
|
|
113
|
-
execSync(`screen -S ${sessionName} -X quit 2>/dev/null`);
|
|
114
|
-
} catch {}
|
|
115
|
-
try {
|
|
116
|
-
execSync(`rm ${logFile} 2>/dev/null`);
|
|
117
|
-
} catch {}
|
|
118
|
-
|
|
119
|
-
console.log(' Status: SUCCESS');
|
|
120
|
-
} catch (e) {
|
|
121
|
-
console.log(` Status: FAILED - ${e.message}`);
|
|
122
|
-
}
|
|
123
|
-
console.log('');
|
|
124
|
-
|
|
125
|
-
console.log('=== Tests Complete ===');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
testScreenModes();
|