Files
oh-my-opencode/src/tools/shared/search-process-output.ts
T
YeonGyu-Kim 4ea7562f50 feat(shared): add Node-safe process stream reader and search output collector
Introduces:
- src/shared/process-stream-reader.ts: Buffer-concat stream reader compatible with both Bun and Node ChildProcess stdout (replaces Web Response API usage)
- src/tools/shared/search-process-output.ts: structured subprocess output collector with timeout, kill, and rejection cleanup
- bun-spawn-shim hardened: Node path forces windowsHide: true; spawn errors no longer escape as unhandledRejection

Foundation for #3919 fix.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-22 20:39:52 +09:00

47 lines
1.3 KiB
TypeScript

import type { SpawnedProcess } from "../../shared/bun-spawn-shim"
import { readProcessStream } from "../../shared/process-stream-reader"
export interface SearchProcessOutput {
readonly stdout: string
readonly stderr: string
readonly exitCode: number
}
function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error)
}
function createProcessTimeout(
proc: SpawnedProcess,
timeoutMs: number,
timeoutMessage: string
): Promise<never> {
return new Promise<never>((_, reject) => {
const id = setTimeout(() => {
proc.kill()
reject(new Error(timeoutMessage))
}, timeoutMs)
// #3919: Handle rejected exits here so timeout cleanup cannot leak unhandled rejections.
void proc.exited.then(
() => clearTimeout(id),
() => clearTimeout(id)
)
})
}
export async function collectSearchProcessOutput(
proc: SpawnedProcess,
timeoutMs: number,
timeoutMessage: string
): Promise<SearchProcessOutput> {
const stderrPromise = readProcessStream(proc.stderr).catch(getErrorMessage)
const stdout = await Promise.race([
readProcessStream(proc.stdout),
createProcessTimeout(proc, timeoutMs, timeoutMessage),
])
const [exitCode, stderr] = await Promise.all([proc.exited, stderrPromise])
return { stdout, stderr, exitCode }
}