Skip to content

Commit 339d645

Browse files
authored
Fix for reding finished process outputs (#210)
* After switch to start process in some situations reading process output after it finished stopped working. Added tests and fixed issue * Few more improvements and fixes
1 parent dfc5505 commit 339d645

File tree

3 files changed

+120
-4
lines changed

3 files changed

+120
-4
lines changed

src/terminal-manager.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class TerminalManager {
164164
// Store completed session before removing active session
165165
this.completedSessions.set(childProcess.pid, {
166166
pid: childProcess.pid,
167-
output: output + session.lastOutput, // Combine all output
167+
output: output, // Use only the main output variable
168168
exitCode: code,
169169
startTime: session.startTime,
170170
endTime: new Date()
@@ -199,9 +199,15 @@ export class TerminalManager {
199199
// Then check completed sessions
200200
const completedSession = this.completedSessions.get(pid);
201201
if (completedSession) {
202-
// Format completion message with exit code and runtime
202+
// Format with output first, then completion info
203203
const runtime = (completedSession.endTime.getTime() - completedSession.startTime.getTime()) / 1000;
204-
return `Process completed with exit code ${completedSession.exitCode}\nRuntime: ${runtime}s\nFinal output:\n${completedSession.output}`;
204+
const output = completedSession.output.trim();
205+
206+
if (output) {
207+
return `${output}\n\nProcess completed with exit code ${completedSession.exitCode}\nRuntime: ${runtime}s`;
208+
} else {
209+
return `Process completed with exit code ${completedSession.exitCode}\nRuntime: ${runtime}s\n(No output produced)`;
210+
}
205211
}
206212

207213
return null;

src/tools/improved-process-tools.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,20 @@ export async function readProcessOutput(args: unknown): Promise<ServerResult> {
110110

111111
const session = terminalManager.getSession(pid);
112112
if (!session) {
113+
// Check if this is a completed session
114+
const completedOutput = terminalManager.getNewOutput(pid);
115+
if (completedOutput) {
116+
return {
117+
content: [{
118+
type: "text",
119+
text: completedOutput
120+
}],
121+
};
122+
}
123+
124+
// Neither active nor completed session found
113125
return {
114-
content: [{ type: "text", text: `No active session found for PID ${pid}` }],
126+
content: [{ type: "text", text: `No session found for PID ${pid}` }],
115127
isError: true,
116128
};
117129
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import assert from 'assert';
2+
import { startProcess, readProcessOutput } from '../dist/tools/improved-process-tools.js';
3+
4+
/**
5+
* Proper test for read_process_output on completed processes
6+
*
7+
* This test should:
8+
* - FAIL when the bug exists (current behavior)
9+
* - PASS when the bug is fixed (desired behavior)
10+
*/
11+
async function testReadCompletedProcessOutput() {
12+
console.log('Testing read_process_output on completed process...');
13+
14+
// Start echo command with delay, but timeout before echo happens
15+
const startResult = await startProcess({
16+
// Cross-platform delay + output using Node
17+
command: 'node -e "setTimeout(() => console.log(\'SUCCESS MESSAGE\'), 1000)"',
18+
timeout_ms: 500 // Returns before the output happens
19+
});
20+
21+
// Extract PID
22+
const pidMatch = startResult.content[0].text.match(/Process started with PID (\d+)/);
23+
assert(pidMatch, 'Should get PID from start_process');
24+
const pid = parseInt(pidMatch[1]);
25+
26+
// Wait for the actual command to complete
27+
await new Promise(resolve => setTimeout(resolve, 2000));
28+
29+
// Try to read the output - this should work when fixed
30+
const readResult = await readProcessOutput({ pid, timeout_ms: 1000 });
31+
32+
// ASSERT: Should be able to read from completed process
33+
assert(!readResult.isError,
34+
'Should be able to read from completed process without error');
35+
36+
// ASSERT: Should contain the echo output
37+
assert(readResult.content[0].text.includes('SUCCESS MESSAGE'),
38+
'Should contain the echo output from completed process');
39+
40+
console.log('✅ Successfully read from completed process');
41+
console.log('✅ Retrieved echo output:', readResult.content[0].text);
42+
}
43+
44+
/**
45+
* Test immediate completion scenario
46+
*/
47+
async function testImmediateCompletion() {
48+
console.log('Testing immediate completion...');
49+
50+
const startResult = await startProcess({
51+
command: 'node -e "console.log(\'IMMEDIATE OUTPUT\')"',
52+
timeout_ms: 2000
53+
});
54+
55+
// Extract PID
56+
const pidMatch = startResult.content[0].text.match(/Process started with PID (\d+)/);
57+
assert(pidMatch, 'Should get PID from start_process');
58+
const pid = parseInt(pidMatch[1]);
59+
60+
// Small delay to ensure process completed
61+
await new Promise(resolve => setTimeout(resolve, 100));
62+
63+
// Should be able to read from immediately completed process
64+
const readResult = await readProcessOutput({ pid, timeout_ms: 1000 });
65+
66+
assert(!readResult.isError,
67+
'Should be able to read from immediately completed process');
68+
69+
assert(readResult.content[0].text.includes('IMMEDIATE OUTPUT'),
70+
'Should contain immediate output from completed process');
71+
72+
console.log('✅ Successfully read from immediately completed process');
73+
}
74+
75+
// Run tests
76+
async function runTests() {
77+
try {
78+
await testReadCompletedProcessOutput();
79+
await testImmediateCompletion();
80+
console.log('\n🎉 All tests passed - read_process_output works on completed processes!');
81+
return true;
82+
} catch (error) {
83+
console.log('\n❌ Test failed:', error.message);
84+
console.log('\n💡 This indicates the bug still exists:');
85+
console.log(' read_process_output cannot read from completed processes');
86+
console.log(' Expected behavior: Should return completion info and final output');
87+
return false;
88+
}
89+
}
90+
91+
runTests()
92+
.then(success => {
93+
process.exit(success ? 0 : 1);
94+
})
95+
.catch(error => {
96+
console.error('Test error:', error);
97+
process.exit(1);
98+
});

0 commit comments

Comments
 (0)