From b1729125e1ca7b6b5c825c25720fcd84075d7999 Mon Sep 17 00:00:00 2001 From: mesalogo Date: Mon, 31 Aug 2026 13:25:49 +0800 Subject: [PATCH] fix: allow slower OpenCode startup on Linux ARM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenCode health polling restored a ten-second startup limit, causing Linux ARM systems and QEMU environments that became healthy just after that boundary to be reported unavailable. Managed local OpenCode now retains health-based readiness detection while allowing up to thirty seconds for startup. Release note: 修复部分 Linux ARM 设备切换 OpenCode 时因启动超过 10 秒而误报超时的问题;健康检查现在最多等待 30 秒。 --- BUILD.md | 3 +++ src/main/agent/opencode-runtime.test.ts | 33 ++++++++++++++++++++++++- src/main/agent/opencode-runtime.ts | 4 +-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/BUILD.md b/BUILD.md index 3a302b9c..11317b1a 100644 --- a/BUILD.md +++ b/BUILD.md @@ -212,6 +212,9 @@ package format v1 归档已经包含固定 Agent 和 Node,可直接用于 Host 跨架构打包前,确认目标架构的 OpenCode 资源已经准备完成。不要用其他架构的二进制替代目标资源。 +GoodBuddy 管理的本机 OpenCode 启动后会轮询已认证的 loopback health 接口,最多等待 +30 秒。该窗口用于兼容 Linux ARM 等较慢环境;不要改回依赖 stdout 文案或 10 秒固定上限。 + ## GoodBuddy Agent 工件 面向用户发布的是按远端 Host 架构区分的复合 `.gbagent`,每个包同时包含签名 diff --git a/src/main/agent/opencode-runtime.test.ts b/src/main/agent/opencode-runtime.test.ts index 2120dcfd..15500d1a 100644 --- a/src/main/agent/opencode-runtime.test.ts +++ b/src/main/agent/opencode-runtime.test.ts @@ -969,6 +969,37 @@ describe('OpenCodeRuntime embedded launcher', () => { }) }) + it('allows ARM-class startup to become healthy after ten seconds', async () => { + vi.useFakeTimers() + try { + const child = fakeChild() + let healthy = false + const checkServerHealth = vi.fn(async () => healthy) + const { deps } = dependencies(child, { + checkServerHealth + }) + const runtime = new OpenCodeRuntime(options(), deps) + let settled = false + const statusPromise = runtime.getStatus().finally(() => { + settled = true + }) + + await vi.advanceTimersByTimeAsync(10_100) + expect(settled).toBe(false) + + healthy = true + await vi.advanceTimersByTimeAsync(100) + await expect(statusPromise).resolves.toMatchObject({ + available: true + }) + expect(checkServerHealth).toHaveBeenCalled() + + await runtime.dispose() + } finally { + vi.useRealTimers() + } + }) + it('times out, terminates the process tree, and does not expose stderr', async () => { const child = fakeChild() const secret = 'private-config-token' @@ -983,7 +1014,7 @@ describe('OpenCodeRuntime embedded launcher', () => { expect(status).toMatchObject({ available: false, - detail: 'OpenCode Server 启动超时(10 秒)' + detail: 'OpenCode Server 启动超时(30 秒)' }) expect(status.detail).not.toContain(secret) expect(child.kill).toHaveBeenCalledWith('SIGTERM') diff --git a/src/main/agent/opencode-runtime.ts b/src/main/agent/opencode-runtime.ts index 95057a1e..2449925b 100644 --- a/src/main/agent/opencode-runtime.ts +++ b/src/main/agent/opencode-runtime.ts @@ -67,7 +67,7 @@ import { import { toOpenCodeSubagentEvent } from './opencode-subagent' import { promptWithUntrustedConversationHistory } from './runtime-conversation-history' -const STARTUP_TIMEOUT_MS = 10_000 +const STARTUP_TIMEOUT_MS = 30_000 const STARTUP_POLL_INTERVAL_MS = 100 const STARTUP_PROBE_TIMEOUT_MS = 500 const CONTROL_REQUEST_TIMEOUT_MS = 30_000 @@ -1079,7 +1079,7 @@ export class OpenCodeRuntime implements AgentRuntime { } } const timeout = setTimeout(() => { - fail('OpenCode Server 启动超时(10 秒)') + fail('OpenCode Server 启动超时(30 秒)') }, this.dependencies.startupTimeoutMs) if (!stdout || !stderr) {