-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Problem
The stream() async iterator on ProcessRunner (v0.9.4) has two related issues:
1. No {type:'exit'} chunks yielded
The stream() method only yields {type:'stdout', data} and {type:'stderr', data} chunks. It does NOT yield {type:'exit', code} chunks, even though the documentation (issue-07-stream-output.mjs) and README examples show chunk.type === 'exit' handling.
The finish() method in $.process-runner-base.mjs does emit 'exit' as an EventEmitter event, but the stream() generator only listens to 'data' and 'end' events — it never captures or yields the exit information.
This means code like:
for await (const chunk of command.stream()) {
if (chunk.type === 'exit') {
exitCode = chunk.code; // ← This never executes
}
}...is dead code. The exit code can only be obtained from (await command).code or command.result.code after the loop.
2. stream() hangs if child process stdout stays open
The stream() generator only terminates when the 'end' event is emitted by ProcessRunner. However, finish() (which emits 'end') is called from executeChildProcess() only after:
const code = await exited;
await Promise.all([outPump, errPump, stdinPumpPromise]); // ← This can hangThe outPump is pumpReadable() which does for await (const chunk of readable) on the child's stdout. If the child process keeps stdout open (common with some CLI tools), pumpReadable() hangs → finish() never fires → stream() iterator never terminates → the for await loop in user code hangs forever.
Reproduction
const { $ } = await use('command-stream');
// Simulate a process that outputs result but keeps stdout open
// (Like Claude CLI which sends result JSON but doesn't close stdout immediately)
const cmd = $`sh -c 'echo "result"; sleep 999'`;
for await (const chunk of cmd.stream()) {
console.log(chunk.type, chunk.data?.toString().trim());
// Prints: stdout result
// Then hangs forever waiting for stream to end
}
// Never reaches hereExpected Behavior
stream()should yield{type:'exit', code}chunks when the process exits- When
stream()does not yield exit chunks, the documentation should clearly state this - Consider adding a mechanism to detect when the process has exited even if pipes are still open
Workaround
We currently work around this by:
- Detecting the "result" event in the application-level JSON output
- Setting a timeout (30 seconds) after the result event
- Force-killing the process with SIGTERM/SIGKILL if the stream doesn't close
let resultTimeoutId = null;
for await (const chunk of cmd.stream()) {
if (chunk.type === 'stdout') {
const data = JSON.parse(chunk.data.toString());
if (data.type === 'result') {
resultTimeoutId = setTimeout(() => cmd.kill('SIGTERM'), 30000);
}
}
}
if (resultTimeoutId) clearTimeout(resultTimeoutId);Environment
- command-stream v0.9.4
- Node.js v20.20.0
- Linux x86_64
Related
- hive-mind Issue #1280:
--tool claudecommand execution stuck after success - The
chunk.type === 'exit'dead code pattern exists in 6 source files across the hive-mind codebase