From 89ed1878f4efb8ab1fa1e9a553fcc49f8d075aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=86=A0=E8=BE=B0?= Date: Tue, 26 May 2026 15:32:07 +0800 Subject: [PATCH] fix(opencode): flush stdio before cli exit --- packages/opencode/src/cli/stdout.ts | 12 +++++++ packages/opencode/src/index.ts | 2 ++ packages/opencode/test/cli/stdout.test.ts | 40 +++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 packages/opencode/src/cli/stdout.ts create mode 100644 packages/opencode/test/cli/stdout.test.ts diff --git a/packages/opencode/src/cli/stdout.ts b/packages/opencode/src/cli/stdout.ts new file mode 100644 index 000000000000..0b126547ba85 --- /dev/null +++ b/packages/opencode/src/cli/stdout.ts @@ -0,0 +1,12 @@ +export type FlushableWriteStream = { + destroyed?: boolean + writableEnded?: boolean + write(chunk: string, callback: () => void): boolean +} + +export function flushWriteStream(stream: FlushableWriteStream) { + if (stream.destroyed || stream.writableEnded) return Promise.resolve() + return new Promise((resolve) => { + stream.write("", () => resolve()) + }) +} diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index d20f29dd4d2f..fa03a39366db 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -40,6 +40,7 @@ import { Heap } from "./cli/heap" import { drizzle } from "drizzle-orm/bun-sqlite" import { ensureProcessMetadata } from "@opencode-ai/core/util/opencode-process" import { isRecord } from "@/util/record" +import { flushWriteStream } from "@/cli/stdout" const processMetadata = ensureProcessMetadata("main") @@ -247,5 +248,6 @@ try { // Most notably, some docker-container-based MCP servers don't handle such signals unless // run using `docker run --init`. // Explicitly exit to avoid any hanging subprocesses. + await Promise.all([flushWriteStream(process.stdout), flushWriteStream(process.stderr)]) process.exit() } diff --git a/packages/opencode/test/cli/stdout.test.ts b/packages/opencode/test/cli/stdout.test.ts new file mode 100644 index 000000000000..f8e449b45675 --- /dev/null +++ b/packages/opencode/test/cli/stdout.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, test } from "bun:test" +import { flushWriteStream } from "../../src/cli/stdout" + +describe("flushWriteStream", () => { + test("waits for the stream write callback", async () => { + let flush: (() => void) | undefined + let resolved = false + + const pending = flushWriteStream({ + write(_chunk, callback) { + flush = () => callback() + return false + }, + }).then(() => { + resolved = true + }) + + await Promise.resolve() + expect(resolved).toBe(false) + + flush?.() + await pending + + expect(resolved).toBe(true) + }) + + test("skips destroyed streams", async () => { + let wrote = false + + await flushWriteStream({ + destroyed: true, + write() { + wrote = true + return true + }, + }) + + expect(wrote).toBe(false) + }) +})