fix: skip Continue update status when updates are disabled

Continue still displayed Checking for updates even when version lookup was disabled. Skip the update service before it emits checking state and retain the lower-level lookup guard in the shared local and Agent adapter.

Add a regression for disabled and normal update paths and record real terminal and production Electron validation. Seven real model requests returned HTTP 200; Linux validation covered startup only. The reported UI freeze was not reproduced, full Agent/model acceptance remains open, and full-suite failures are documented.

Release note: 修复 Continue 禁用自动更新后仍显示 Checking for updates 的问题,终端现在保持正常空闲状态。
This commit is contained in:
mesalogo
2026-09-27 15:23:41 +08:00
parent eb53c827c6
commit 0f0b6ac21d
4 changed files with 103 additions and 0 deletions
@@ -1,5 +1,85 @@
# 工作栏实现与验证进度
## 2026-09-27 Continue Update Status
The shared Continue adapter now skips the update service before it emits the
checking state; behavior is specified in the [native client design](./runtime-native-client-proposal.md#62-continue-与-opencode).
The regression executes the patched method and verifies idle-only state when
disabled, while retaining the original path when the flag is absent.
- Windows production coordinator/PTY and real DS browser validation completed:
Continue made 2 supplier requests, OpenCode 1, and DS Web 2; all five returned
HTTP 200 and the clients displayed the expected answers. Captured terminal
output contained no `Checking for updates`. Original settings stayed byte-identical.
- Current adapter source was bundled and uploaded through pinned-identity SSH to
the shared Linux x64 Host, under a dedicated `/root/tmp/gb-update-check-*`
directory. Continue 1.5.47 reached its initialized Ask TUI and displayed
`Continue CLI`; 15 seconds of PTY capture contained no update-check text.
This startup check made zero model calls and did not run the Agent protocol or
remote model bridge. Agent helper imports this same adapter, but complete
remote shortcut and real-model acceptance remain unfinished.
- Adapter tests: 30 passed. Repository typecheck and lint passed. Full `npm test`
reached the tool's 600-second limit without a final summary; it is not recorded
as a passing full run.
Evidence is in the local temporary `opencode/dsh-017-verify` directory:
`update-check-evidence.json`, `update-check-main-result.json`, and
`remote-pty-run.cjs`. The local action/IPC harness uses jsdom and simulated
Electron transport with real child clients. It does not reproduce or rule out
full desktop-window freezing. The inherited TLS verification override and lack
of a complete background-network capture also limit the evidence.
### Full production App follow-up
A fresh `npm run build` passed. The resulting production Main, Preload and
Renderer were launched in an isolated Windows Electron profile. Native mouse
input selected Continue and opened its terminal; native text input and Enter
submitted a minimal request. The actual Continue output contained the expected
joined answer and no `Checking for updates`. Main HTTP dispatch instrumentation
recorded two additional provider requests, both HTTP 200: this fix's validation
now totals seven external model requests. A subsequent no-submit probe copied
initialized TUI content and typed input from the active xterm buffer.
Early driver attempts targeted a restored inactive terminal, giving an empty
clipboard; selecting the newly opened terminal resolved this without product
changes. Another attempt sent Enter before focus/input settled and dispatched
zero requests. These harness failures are not product freeze fixes. The full App
remained callable and accepted terminal input in the successful runs; the user's
original freeze has not been reproduced or uniquely diagnosed. Captured PNGs
have not been visually reviewed. The production TLS policy still enabled the
verification override, despite the launcher's strict environment setting.
Local evidence: `full-app-model-events.json`, `full-app-calls.json`,
`full-app-buffer.txt` and the `full-app*.cjs` drivers in the same temporary folder.
The isolated profile was removed after checking that no probe-owned Electron or
Node processes remained; the separate diagnostic evidence was retained.
The full-suite rerun completed in 734 seconds: 5,298 passed,
86 skipped and 3 failed tests, plus one failed suite setup. Failures were the
OpenCode isolation fixture (`spawnSync npm.cmd EINVAL`), Magic Notes layout's
90-second timeout, and two HeartbeatCenter tablist assertions. These files were
not changed here; this is not an all-green suite. Original runtime settings were
byte-identical after the full-App probes, and no probe-owned processes remained.
Remote shortcut integration and full Agent/model acceptance remain outstanding.
### Failure triage follow-up
The OpenCode isolation and native Magic Notes layout files passed together through
`npm test` (8 tests). The earlier full-suite supervisor invoked Vitest directly,
without npm's launcher environment; its OpenCode setup error did not recur through
the npm entry point. The layout timeout also did not recur. These focused results
do not replace the failed full-suite result.
Both HeartbeatCenter language cases still fail independently. Source inspection
shows the graph contains a separate, named PageTabs for events/entities/relations
inside the page-level tabs; the older test assumes there can only be one tablist
anywhere on the page. Neither that feature nor its test was changed for this
Continue fix. No unrelated UI change or assertion relaxation was made.
The retained full-App log contains no matching unresponsive, crash, timeout or
maximum-update-depth errors. This does not establish the cause of the reported
freeze. This follow-up made zero external model requests (the fix-validation total
remains seven); the OpenCode regression uses loopback model fixtures.
## 2026-09-27 Local Native Client Validation
Scope: [native client proposal](./runtime-native-client-proposal.md), FR-2/3/5/6/7/8,
@@ -205,6 +205,8 @@ Skills/MCP 配置,避免继承测试机器的全局默认模型。
模拟键入一段含凭据的启动命令。退出 CLI 后是否保留终端输出沿用既有终端体验;其桥接应
随 CLI 生命周期收束,不等到整个聊天结束。
Continue 的共享宿主适配器在 `GOODBUDDY_DISABLE_CONTINUE_UPDATES=1` 时,直接让更新服务保持 idle,并显示 `Continue CLI`,不进入 checking 状态。仅跳过底层版本查询仍会短暂显示 `Checking for updates`,因此服务入口和版本查询两处都需跳过。本地原生终端与 Agent Continue helper 使用同一适配器;已有缓存按补丁内容哈希重新生成。实测范围见[更新提示验证记录](./progress.md#2026-09-27-continue-update-status)。
模型桥作为客户端会话资源保持有效。复用前检查当前桥接是否依赖单轮请求、单一消息 ID
或短时授权;必要时只提取所需的会话级资源接口。真实供应商密钥不写入 CLI 配置,客户端
仅获得桥接所需的局部连接信息。
@@ -78,6 +78,7 @@ async function createDistribution(version = '1.5.47'): Promise<{
'let j=(0,atn.default)();j.use(atn.default.json()),j.get("/state"',
'listen(i,async()=>{console.log(Ht.green(`Server started on http://localhost:${i}`))',
'async function SCt(e){return n5e||',
'async checkAndAutoUpdate(){throw new Error("Update check reached")}',
'shouldUseResponsesEndpoint(t){return this.config.useResponsesApi===!1?!1:this.apiBase==="https://api.openai.com/v1/"&&A0e(t)}',
'function uAe(e,t){let n={provider:e.provider,model:e.model,apiKey:e.apiKey,apiBase:e.apiBase,requestOptions:e.requestOptions,env:e.env};return CGn(n)??null}',
'function Sin(e,t){let n=[];n.push({role:"system",content:t});let r=oot(e);return n.push(...r),n}',
@@ -232,6 +233,20 @@ describe('ContinueHostAdapter', () => {
)
})
it('skips update work and checking state when updates are disabled', async () => {
const distribution = await createDistribution()
const adapter = new ContinueHostAdapter({ binaryPath: distribution.entryPath, configPath: '', workspace: process.cwd(), cacheRoot: distribution.cacheRoot, trustedBundleHashes: [distribution.sourceHash] })
const prepared = await adapter.getPreparedHost()
const bundle = await readFile(join(prepared.entryPath, '..', 'index.js'), 'utf8')
const method = bundle.slice(bundle.indexOf('async checkAndAutoUpdate(){'), bundle.indexOf(';shouldUseResponsesEndpoint'))
const service = new Function('process', `return {${method}}`)({ env: { GOODBUDDY_DISABLE_CONTINUE_UPDATES: '1' } })
service.setState = vi.fn()
await service.checkAndAutoUpdate()
expect(service.setState).toHaveBeenCalledExactlyOnceWith({ autoUpdate: false, status: 'idle', message: 'Continue CLI', isUpdateAvailable: false })
const enabled = new Function('process', `return {${method}}`)({ env: {} })
await expect(enabled.checkAndAutoUpdate()).rejects.toThrow('Update check reached')
})
it('rejects unsupported Continue versions without patching them', async () => {
const distribution = await createDistribution('1.6.0')
const adapter = new ContinueHostAdapter({
+6
View File
@@ -893,6 +893,7 @@ export class ContinueHostAdapter {
'listen(i,async()=>{console.log(Ht.green(`Server started on http://localhost:${i}`))'
const versionCheckMarker =
'async function SCt(e){return n5e||'
const updateServiceMarker = 'async checkAndAutoUpdate(){'
const responseRoutingMarker =
'shouldUseResponsesEndpoint(t){return this.config.useResponsesApi===!1?!1:this.apiBase==="https://api.openai.com/v1/"&&A0e(t)}'
const modelConfigurationMarker =
@@ -957,6 +958,11 @@ export class ContinueHostAdapter {
versionCheckMarker,
'async function SCt(e){if(process.env.GOODBUDDY_DISABLE_CONTINUE_UPDATES==="1")return null;return n5e||'
)
patched = replaceExactly(
patched,
updateServiceMarker,
`${updateServiceMarker}if(process.env.GOODBUDDY_DISABLE_CONTINUE_UPDATES==="1"){this.setState({autoUpdate:!1,status:"idle",message:"Continue CLI",isUpdateAvailable:!1});return;}`
)
patched = replaceExactly(
patched,
responseRoutingMarker,