diff --git a/packages/opencode/src/effect/cross-spawn-spawner.ts b/packages/opencode/src/effect/cross-spawn-spawner.ts index 76982a613367..06c6c5bdb8b5 100644 --- a/packages/opencode/src/effect/cross-spawn-spawner.ts +++ b/packages/opencode/src/effect/cross-spawn-spawner.ts @@ -267,17 +267,13 @@ export const make = Effect.gen(function* () { const signal = Deferred.makeUnsafe() const proc = launch(command.command, command.args, opts) let end = false - let exit: readonly [code: number | null, signal: NodeJS.Signals | null] | undefined proc.on("error", (err) => { resume(Effect.fail(toPlatformError("spawn", err, command))) }) proc.on("exit", (...args) => { - exit = args - }) - proc.on("close", (...args) => { if (end) return end = true - Deferred.doneUnsafe(signal, Exit.succeed(exit ?? args)) + Deferred.doneUnsafe(signal, Exit.succeed(args)) }) proc.on("spawn", () => { resume(Effect.succeed([proc, signal])) diff --git a/packages/opencode/test/effect/cross-spawn-spawner.test.ts b/packages/opencode/test/effect/cross-spawn-spawner.test.ts index 2cc5092029be..16b2f9b04451 100644 --- a/packages/opencode/test/effect/cross-spawn-spawner.test.ts +++ b/packages/opencode/test/effect/cross-spawn-spawner.test.ts @@ -214,6 +214,41 @@ describe("cross-spawn spawner", () => { ) }) + describe("background child processes", () => { + fx.effect( + "completes immediately when shell spawns background child", + Effect.gen(function* () { + const started = Date.now() + const handle = yield* ChildProcess.make("bash", ["-c", "sleep 10 &"]) + const code = yield* handle.exitCode + const elapsed = Date.now() - started + expect(code).toBe(ChildProcessSpawner.ExitCode(0)) + expect(elapsed).toBeLessThan(500) + }), + ) + + fx.effect( + "handles nohup background processes", + Effect.gen(function* () { + const started = Date.now() + const handle = yield* ChildProcess.make("bash", ["-c", "nohup sleep 10 >/dev/null 2>&1 &"]) + const code = yield* handle.exitCode + const elapsed = Date.now() - started + expect(code).toBe(ChildProcessSpawner.ExitCode(0)) + expect(elapsed).toBeLessThan(500) + }), + ) + + fx.effect( + "reports correct exit code with background children", + Effect.gen(function* () { + const handle = yield* ChildProcess.make("bash", ["-c", "exit 42"]) + const code = yield* handle.exitCode + expect(code).toBe(ChildProcessSpawner.ExitCode(42)) + }), + ) + }) + describe("process control", () => { fx.effect( "kills a running process",