opencode-navigator 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.24.0] - 2026-10-31
4
+
5
+ - Support the latest stable OpenCode 1.19.5 and 2.2.5 releases, preserving both host adapters and existing sidebar behavior.
6
+ - Preserve Limits scales and reset-credit controls across equivalent quota snapshots and unrelated workspace events, while retaining current-account and credential-change guards.
7
+ - Keep rounded selection backgrounds consistent in nested dialogs and highlighted rows.
8
+ - Configure each toolbar button with zero to three icons and optional custom text, including text-only buttons. Keep legacy/state choices, unsaved drafts, rounded clipping, and width-aware More/wrapping.
9
+ - Keep Quick Action rows and their hover/focus state stable while typing in the main prompt on both supported OpenCode hosts.
10
+ - Show toolbar hover/focus hints without moving Limits or other sections, preserving centered, uniformly sized icon controls and empty-toolbar collapse.
11
+ - Add Settings → About with the running versions, explicit update status/checking, confirmed updates, and a versioned changelog that can be reopened independently of first-launch upgrade notes.
12
+ - Prepare editable Bug reports with confirmed sending through authenticated GitHub CLI and an independent Open in browser action. Retain drafts through errors and cancellation, and require manual review before retrying an uncertain submission.
13
+
3
14
  ## [0.23.0] - 2026-10-28
4
15
 
5
16
  - Support only the latest stable OpenCode 1.x and 2.x releases, currently 1.19.5 and 2.2.0, including the newer terminal renderer.
package/HOST_API.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Public Host APIs And Remaining Gaps
2
2
 
3
- Navigator supports only OpenCode **1.19.5** and **2.2.0**, the current stable
3
+ Navigator supports only OpenCode **1.19.5** and **2.2.5**, the current stable
4
4
  release in each major line. Published SDK declarations and real-host checks
5
5
  define the boundary; internal host components are not plugin APIs.
6
6
 
7
7
  ## Host-Owned Services
8
8
 
9
- | Capability | OpenCode 1.19.5 | OpenCode 2.2.0 |
9
+ | Capability | OpenCode 1.19.5 | OpenCode 2.2.5 |
10
10
  | --- | --- | --- |
11
11
  | Todo | Supported host Todo source/events | Public message cache, completed `todowrite` and revert boundary |
12
12
  | Skills | Host discovery and prompt insertion | Native Skill cache and `session.skill` with exact ID |
@@ -26,7 +26,9 @@ justify bypassing a native API.
26
26
  | Implementation | Missing equivalent / preserved semantics | Removal condition |
27
27
  | --- | --- | --- |
28
28
  | Shared private preferences and atomic lock | A common 1.x/2.x store with existing data, Global/Worktree overrides, portable-default precedence and user-wide pins/groups/trust. The 2.x host store is per-channel/plugin, not this cross-host contract. No mirrored store is created. | A public shared store and supported migration preserving scopes and concurrency. |
29
- | Nested dialog frames | The host has one active dialog, not the preceding frame's query, selection, scroll and unsaved draft. Simple prompts/confirms delegate to native APIs. | Native nested frames with equivalent restore and stale-target behavior. |
29
+ | Nested dialog frames | The host has one active dialog, not the preceding frame's query, selection, scroll and unsaved draft. OpenCode 1 prompts and standalone confirmations delegate to native APIs. The 2.x promise prompt replaces its owner and loses Navigator's return callback, reproduced in the real toolbar-text smoke; Navigator-owned nested one-line prompts use a focused Shared editor. | Native nested frames and prompt ownership/dismissal semantics with equivalent restore and stale-target behavior. |
30
+ | About service port and manual release history | Navigator-specific metadata/update state must be shared between footer, shortcuts and deferred Settings. The shipped changelog is read separately from the first-launch claim; no second updater or preference mirror is created. Nested update confirmations stay in the existing frame stack. | Public host facilities preserving Navigator release identity, changelog browsing and nested return state. |
31
+ | Bug-report transport and external browser launch | Neither supported public TUI contract provides GitHub issue creation or an external browser opener. Direct sending uses the user's authenticated `gh` without reading credential files; a `type: Bug` repository form handles browser reports. Bounded argv-based OS launch has a visible link/clipboard fallback on remote or headless hosts. | Equivalent public host issue/browser APIs preserving authentication ownership, editable draft, real issue type and confirmation semantics. |
30
32
  | Literal Ctrl+, hook on 2.x | Public plugin bindings are strings; the comma expander splits literal commas into alternatives. The named parser does not normalize `comma` to the actual comma event. Dispatch still uses the native command API. | A documented literal-comma binding passing the real Settings smoke. |
31
33
  | Subagent child discovery | Published TUI Data caches sessions but has no complete children-sync operation. Initial/manual/reconnect discovery uses the public paginated API and syncs unknown IDs into the host cache. | A complete children-sync/query on the supplied public Data interface. |
32
34
  | Subagent observation history and retry details | Native status and cached transcript retry data are used first; recent runs, pre-observation clock markers, errors and unloaded-child retry detail remain observation history. | Equivalent public observed-run/retry history without loading every transcript. |
@@ -49,11 +51,28 @@ justify bypassing a native API.
49
51
 
50
52
  Both current hosts have isolated real-host checks. Historical installer fixtures
51
53
  exercise migration only; they do not extend the supported host matrix.
52
- Development uses Bun 1.4.4 and OpenTUI 0.7, leaving Solid/OpenTUI external so the
54
+ The published 2.2.5 TUI context, plugin, index and Solid declarations are identical
55
+ to 2.2.0. OpenTUI 0.8 retains the drawing-only paint-hook contract; renderer tests,
56
+ nested dialogs and frame-level typing probes run against the updated runtime.
57
+ Development uses Bun 1.4.4 and OpenTUI 0.8, leaving Solid/OpenTUI external so the
53
58
  host owns its runtime. The 1.x host ships older runtime modules; its actual smoke
54
59
  must pass rather than injecting the development modules into it.
55
60
 
56
- OpenTUI 0.7 supports Node >=26.4.0 or Bun >=1.3.14. The updater verifies a staged
61
+ OpenTUI 0.8 supports Node >=26.4.0 or Bun >=1.3.14. The updater verifies a staged
57
62
  target with an available compatible runtime before switching a recognized local
58
63
  wrapper. A lazy root import succeeding under an older Node does not prove full
59
64
  renderer/FFI support. CI and package verification use Node 26.12.1.
65
+
66
+ ### Deliberate Compatibility Pins
67
+
68
+ - `web-tree-sitter` stays at **0.25.10**, the exact peer required by OpenTUI
69
+ 0.8.0. Upgrade it when the supported host runtime publishes a compatible peer
70
+ contract; the independent 0.27 line is not a compatible substitution.
71
+ - `typescript` stays at **6.0.3**, the latest compatible compiler for the shared
72
+ linter stack. TypeScript 7.0.2 type-checks Navigator, but SonarJS/`ts-api-utils`
73
+ fails while loading its missing JavaScript compiler API. Upgrade when those
74
+ consumers support the new compiler distribution; do not disable their rules
75
+ to make an incompatible upgrade pass.
76
+ - Bun remains pinned at **1.4.4**, the current stable release. Direct dependencies
77
+ otherwise use their latest compatible stable releases; transitive updates stay
78
+ within upstream ranges. Runtime peers remain host-owned and are not bundled.
package/PROVIDERS.md CHANGED
@@ -19,7 +19,7 @@ network failure does not switch to a credential-based or external reader.
19
19
 
20
20
  | Provider | Native source | Scope and prerequisites | Reference |
21
21
  | --- | --- | --- | --- |
22
- | OpenCode Console / Go | Public OpenCode 2.2 `usage.limits` client API | Workspace identity, applicable USD budgets and percent-based Go windows; no Navigator server required | [Published client](https://cdn.jsdelivr.net/npm/@opencode/client@2.2.0/dist/promise/generated/client.d.ts), [Usage schema](https://github.com/anomalyco/opencode/blob/v2.2.0/packages/schema/src/usage-limits.ts) |
22
+ | OpenCode Console / Go | Public OpenCode 2.2 `usage.limits` client API | Workspace identity, applicable USD budgets and percent-based Go windows; no Navigator server required | [Published client](https://cdn.jsdelivr.net/npm/@opencode/client@2.2.5/dist/promise/generated/client.d.ts), [Usage schema](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/schema/src/usage-limits.ts) |
23
23
  | OpenAI / Codex ChatGPT | Codex app-server account/rate-limit RPC and manual reset credits | Verified account identity; ordinary `codex` quota is account-scoped even with null model metadata; separate model buckets need explicit association; native durations describe quota windows, not time until reset | [Codex protocol](https://github.com/openai/codex/tree/main/codex-rs/app-server-protocol) |
24
24
  | OpenAI API | HTTP request/token/project-token rate-limit headers | Actual response for the selected model/account; HTTP transport | [Rate limits](https://developers.openai.com/api/docs/guides/rate-limits) |
25
25
  | Anthropic API | Native response headers and organization rate-limits API | Per-model headers; configured limits need an eligible org/admin credential and explicit model association | [Rate limits API](https://platform.claude.com/docs/en/manage-claude/rate-limits-api) |
@@ -57,7 +57,7 @@ network failure does not switch to a credential-based or external reader.
57
57
  | Qwen / Alibaba Cloud / DashScope | Model/project/cloud quota and billing permissions; browser OAuth state is not a portable quota source | [Model Studio docs](https://www.alibabacloud.com/help/en/model-studio/) |
58
58
  | Hugging Face Inference Providers | Hub request quotas are not automatically inference-model quotas or inference credits | [Hub limits](https://huggingface.co/docs/hub/rate-limits), [Inference pricing](https://huggingface.co/docs/inference-providers/pricing) |
59
59
  | Cloudflare Workers AI | Account billing/analytics and model/resource limits; do not derive remaining free allowance from static daily examples | [Workers AI limits](https://developers.cloudflare.com/workers-ai/platform/limits/) |
60
- | OpenCode Console / Go boundaries | Native workspace budgets and Go allowance are supported, not an inferred wallet balance; other providers need their own documented source | [Host usage source](https://github.com/anomalyco/opencode/blob/v2.2.0/packages/core/src/usage-limits.ts) |
60
+ | OpenCode Console / Go boundaries | Native workspace budgets and Go allowance are supported, not an inferred wallet balance; other providers need their own documented source | [Host usage source](https://github.com/anomalyco/opencode/blob/v2.2.5/packages/core/src/usage-limits.ts) |
61
61
  | Cursor | Consumer account usage requires a supported external API; no browser cookies or local credential extraction | [Cursor docs](https://docs.cursor.com/) |
62
62
  | Windsurf / Codeium | Consumer plan usage is separate from API response rate limits; no private account endpoints | [Windsurf docs](https://docs.windsurf.com/) |
63
63
  | Amp | Product credits require a supported external account API; no credential-file or cookie integrations | [Amp manual](https://ampcode.com/manual) |
package/README.md CHANGED
@@ -29,6 +29,7 @@ See the [changelog](CHANGELOG.md) for release-by-release changes.
29
29
  - Show or hide each sidebar section independently
30
30
  - Hide the session title or its creation date independently in OpenCode 1.x without losing the settings control
31
31
  - Configure section visibility and order from the sidebar settings button
32
+ - Open Settings → About for the running version, update checks, versioned release history, and editable GitHub Bug reports
32
33
  - Separate adjacent sections with quiet theme-aware dividers instead of background cards
33
34
  - Save visibility, expansion, and order globally or for the current worktree
34
35
  - Copy the selected scope's layout and MCP states as versioned JSON, then validate and preview imports
@@ -43,8 +44,8 @@ collapsed. Use `Save current layout as default` in sidebar settings to reuse the
43
44
  current visible/hidden, expanded/collapsed, and ordering states in new sessions.
44
45
 
45
46
  **Only the latest stable release in each major line is supported: OpenCode
46
- 1.19.5 and 2.2.0.** Both have separate real-host checks. Older releases are not
47
- part of the supported compatibility matrix. The 2.2 integration uses OpenTUI 0.7
47
+ 1.19.5 and 2.2.5.** Both have separate real-host checks. Older releases are not
48
+ part of the supported compatibility matrix. The 2.2 integration uses OpenTUI 0.8
48
49
  and public host caches, events, keymap layers, and client APIs while retaining
49
50
  the separate 1.x adapter. See [Host API boundaries](HOST_API.md) for remaining
50
51
  capability gaps and removal conditions. On OpenCode 2, Navigator reads the same native Todo history as
@@ -394,16 +395,30 @@ Choose a base icon in the searchable picker, then select **Pin to toolbar**.
394
395
  The manager lets you change icons, move buttons, or unpin them. Cancelling the
395
396
  picker returns to the draft; cancelling the draft discards its changes.
396
397
 
398
+ For each button, **Icons and text…** lets you choose **zero to three ordered icons**
399
+ and an optional custom single-line label. Use **Base icons…** to add, replace or
400
+ remove icons, and **Text…** for the label. Zero icons produces a text-only button;
401
+ an entirely empty appearance cannot be saved. Labels accept up to 128 characters
402
+ and are clipped to the available cells without overwriting the rounded ends.
403
+ State-specific icon groups retain the existing per-state choices. Nested edits
404
+ remain drafts until the final **Pin to toolbar** or **Save** action.
405
+
397
406
  Where the host exposes a state, the same button can use a different icon for each
398
407
  state: auto-approve On/Off on OpenCode 1.x, and Idle/Compacting on OpenCode 2.x.
399
408
  The 2.x permissions button opens host Settings and uses its base icon. Hover or
400
409
  keyboard focus shows the button's name, observed state, or unavailable reason.
410
+ The hint uses the existing gap beneath the toolbar, so appearing or disappearing
411
+ does not move Limits or any other section. Default single-icon buttons stay
412
+ centered in uniform three-cell controls in both Nerd Font and Text modes.
401
413
  Skill buttons retain confirmation and trust settings; a missing or ambiguous
402
414
  source stays pinned but cannot run.
403
415
 
404
416
  **Behavior → Toolbar overflow** switches between one row with a **More** menu
405
417
  (the default) and wrapping rows. Left/Right moves along a row; Up/Down moves
406
418
  between rows. The setting follows Global/Worktree scope and portable defaults.
419
+ Overflow uses actual bounded button widths. The largest configured state appearance
420
+ reserves the button's width so changing state does not reshuffle the toolbar.
421
+ Default single-icon buttons remain three cells wide.
407
422
  Pins, their order, icons, and exact Skill sources are private user-wide preferences
408
423
  and are excluded from portable exports. Use **Reload settings from file** in other
409
424
  running sessions after saving changes in another instance.
@@ -412,6 +427,10 @@ running sessions after saving changes in another instance.
412
427
 
413
428
  ![Searchable toolbar icon picker](https://raw.githubusercontent.com/St1ggy/opencode-navigator/main/screenshots/57-toolbar-icon-picker.png)
414
429
 
430
+ ![Toolbar buttons with multiple icons and optional text](screenshots/81-toolbar-icons-and-text.png)
431
+
432
+ ![Toolbar appearance configuration](screenshots/83-toolbar-appearance.png)
433
+
415
434
  In OpenCode 1.x, Navigator saves changes to the auto-approve mode as a user-wide
416
435
  default, including changes made through OpenCode's own command palette. Set the
417
436
  default under **Navigator Settings → Behavior → Default auto-approve permissions**;
@@ -421,6 +440,50 @@ dialog for the current session and `session.permissions` in the user-wide
421
440
  `cli.json` (`"prompt"` or `"autoaccept"`) for its startup default. Restart
422
441
  OpenCode after changing `cli.json`.
423
442
 
443
+ ### About, release history, and support
444
+
445
+ Open **Navigator Settings → About** with the settings control or `Ctrl+,`.
446
+ The tab shows the running Navigator and OpenCode versions, the last update-check
447
+ result, and **Check for updates**. A failed check is reported as a failure rather
448
+ than an up-to-date result. **Update Navigator…** uses the same confirmation,
449
+ installation method/scope, stale-target guards and restart guidance as the footer
450
+ and Navigator shortcuts.
451
+
452
+ **Changelog by version…** browses the `CHANGELOG.md` shipped with the plugin.
453
+ Choose a version to read its scrollable notes; Escape returns to the selected
454
+ version and then to About. Technical-only versions have an explicit empty state.
455
+ Manual browsing does not consume or reset the one-time upgrade notes shown after
456
+ an update.
457
+
458
+ **Report a bug…** opens an editable title and Markdown body. The visible default
459
+ context contains software versions, platform and runtime only. Navigator does
460
+ not collect chat, source, workspace paths, logs or credentials for the report.
461
+
462
+ - **Send to GitHub…** uses an installed, already authenticated [GitHub CLI](https://cli.github.com/)
463
+ with support for `gh issue create --type`. A separate preview confirms the
464
+ repository, the real **Bug** issue type, title and body before sending. GitHub
465
+ credentials stay with `gh`, never in Navigator preferences.
466
+ - **Open in browser** opens the same draft in the repository's Bug issue form,
467
+ independently of CLI installation or authentication. Large reports use an
468
+ explicit **Copy report & open browser** action; browser-launch failures offer
469
+ a copyable link without discarding the draft.
470
+ - A partial result retains its issue URL. An uncertain outcome opens the issues
471
+ list for review and requires an explicit **Retry after checking GitHub** before
472
+ another confirmed send. Navigator never automatically retries issue creation.
473
+
474
+ Enter inserts a newline in the body; Tab moves between fields and actions, and
475
+ `Ctrl+Enter` opens the send preview. Escape/Cancel returns to the previous screen
476
+ with the draft retained for the current Settings flow. Reports are not saved in
477
+ private preferences.
478
+
479
+ Quick Actions also keep their mounted rows while ordinary prompt typing updates
480
+ the host's command state. Live labels, availability and real source updates still
481
+ refresh immediately.
482
+
483
+ ![About with versions and update status](screenshots/67-settings-about.png)
484
+
485
+ ![Bug report preview before sending](screenshots/74-bug-report-confirmation.png)
486
+
424
487
  ## Limits: provider quotas and banked resets
425
488
 
426
489
  Navigator keeps subscription limits separate from OpenCode's session token and
@@ -478,7 +541,7 @@ REST reads use a short in-memory cache; Refresh bypasses it. Observed headers ol
478
541
  than two minutes are marked stale instead of presented as current remaining quota.
479
542
 
480
543
  Navigator shares the host's Solid/OpenTUI runtime. Development and CI use Bun 1.4.4
481
- and Node 26.12.1; OpenTUI 0.7's supported Node path starts at 26.4.0. A confirmed
544
+ and Node 26.12.1; OpenTUI 0.8's supported Node path starts at 26.4.0. A confirmed
482
545
  pinned-snapshot update verifies its target with supported Bun (at least 1.3.14)
483
546
  or Node before switching the wrapper. If neither is available, the running snapshot
484
547
  is preserved with guidance; the updater does not install a runtime globally.
@@ -0,0 +1,17 @@
1
+ function versionParts(value){const match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?/.exec(value)
2
+ return match?{numbers:match.slice(1,4).map(Number),prerelease:match[4]||undefined}:undefined
3
+ }
4
+ function isNewerVersion(latest,current){const next=versionParts(latest)
5
+ const installed=versionParts(current)
6
+ if(!next||!installed)
7
+ return false
8
+ for(let index=0;index<3; index++){if(next.numbers[index]!==installed.numbers[index])
9
+ return next.numbers[index]>installed.numbers[index]
10
+ }
11
+ return installed.prerelease!==undefined&&next.prerelease===undefined
12
+ }
13
+ function isSameMajorVersion(left,right){const first=versionParts(left)
14
+ const second=versionParts(right)
15
+ return Boolean(first&&second&&first.numbers[0]===second.numbers[0])
16
+ }
17
+ export{isNewerVersion,isSameMajorVersion}
@@ -0,0 +1,22 @@
1
+ import {isNewerVersion
2
+ } from "./chunk-fd7394y1.js";
3
+ function parseChangelog2(source){const entries=[]
4
+ let entry
5
+ for(const line of source.split(/\r?\n/)){const heading=/^## \[(\d+\.\d+\.\d+(?:-[\w.-]+)?)\]/.exec(line)
6
+ if(heading){entry={version:heading[1],changes:[]}
7
+ entries.push(entry)
8
+ } else if(line.startsWith("## "))
9
+ entry=undefined
10
+ else if(entry&&line.startsWith("- "))
11
+ entry.changes.push(line.slice(2))
12
+ }
13
+ return entries
14
+ }
15
+ function changesSince2(source,previous,current){return parseChangelog2(source).filter((entry)=>entry.changes.length>0&&isNewerVersion(entry.version,previous)&&!isNewerVersion(entry.version,current))
16
+ }
17
+ async function readBundledChangelog2(){const packaged=Bun.file(new URL("../CHANGELOG.md",import.meta.url))
18
+ if(await packaged.exists())
19
+ return packaged.text()
20
+ return Bun.file(new URL("../../../../CHANGELOG.md",import.meta.url)).text()
21
+ }
22
+ export{parseChangelog2,changesSince2,readBundledChangelog2}
@@ -1,22 +1,10 @@
1
1
  import {PLUGIN_ID
2
- } from "./chunk-bv0nm1tj.js";
2
+ } from "./chunk-bv0nm1tj.js"
3
+ import {parseChangelog2,readBundledChangelog2
4
+ } from "./chunk-pwa1wzer.js"
5
+ import {isNewerVersion,isSameMajorVersion
6
+ } from "./chunk-fd7394y1.js";
3
7
  import {createSignal} from "solid-js"
4
- function versionParts(value){const match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?/.exec(value)
5
- return match?{numbers:match.slice(1,4).map(Number),prerelease:match[4]||undefined}:undefined
6
- }
7
- function isNewerVersion(latest,current){const next=versionParts(latest)
8
- const installed=versionParts(current)
9
- if(!next||!installed)
10
- return false
11
- for(let index=0;index<3; index++){if(next.numbers[index]!==installed.numbers[index])
12
- return next.numbers[index]>installed.numbers[index]
13
- }
14
- return installed.prerelease!==undefined&&next.prerelease===undefined
15
- }
16
- function isSameMajorVersion(left,right){const first=versionParts(left)
17
- const second=versionParts(right)
18
- return Boolean(first&&second&&first.numbers[0]===second.numbers[0])
19
- }
20
8
  var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
21
9
  var REQUEST_TIMEOUT_MS=5000
22
10
  async function readNavigatorVersion(request,signal){const response=await request(NAVIGATOR_PACKAGE_URL,{headers:{accept:"application/json"},signal})
@@ -29,6 +17,7 @@ return metadata.version
29
17
  }
30
18
  function createVersionStatus(api,navigatorVersion,request=fetch,timeoutMs=REQUEST_TIMEOUT_MS){const [openCodeUpdate,setOpenCodeUpdate]=createSignal()
31
19
  const [navigatorUpdate,setNavigatorUpdate]=createSignal()
20
+ const [navigatorCheckState,setNavigatorCheckState]=createSignal({status:"idle"})
32
21
  let controller
33
22
  let pending
34
23
  let disposed=false
@@ -43,6 +32,7 @@ function refreshNavigator(){if(disposed)
43
32
  return Promise.resolve("unavailable")
44
33
  if(pending)
45
34
  return pending
35
+ setNavigatorCheckState((previous)=>({...previous,status:"checking"}))
46
36
  const active=new AbortController
47
37
  controller=active
48
38
  const timeout=setTimeout(()=>active.abort(),timeoutMs)
@@ -54,8 +44,12 @@ const reading=Promise.race([readNavigatorVersion(request,active.signal),cancelle
54
44
  return "unavailable"
55
45
  const newer=isNewerVersion(version,navigatorVersion)
56
46
  setNavigatorUpdate(newer?version:undefined)
47
+ setNavigatorCheckState({status:newer?"available":"current",latest:version,checkedAt:Date.now()})
57
48
  return newer?"update":"current"
58
- }).catch(()=>"unavailable").finally(()=>{clearTimeout(timeout)
49
+ }).catch(()=>{if(!disposed)
50
+ setNavigatorCheckState((previous)=>({...previous,status:"error",checkedAt:Date.now()}))
51
+ return "unavailable"
52
+ }).finally(()=>{clearTimeout(timeout)
59
53
  active.signal.removeEventListener("abort",abort)
60
54
  if(controller===active)
61
55
  controller=undefined
@@ -66,7 +60,7 @@ pending=reading
66
60
  return reading
67
61
  }
68
62
  refreshNavigator()
69
- return{openCodeUpdate,navigatorUpdate,refreshNavigator}}
63
+ return{openCodeUpdate,navigatorUpdate,refreshNavigator,navigatorCheckState}}
70
64
  import {effect as _$effect3} from "@opentui/solid"
71
65
  import {insertNode as _$insertNode3} from "@opentui/solid"
72
66
  import {createComponent as _$createComponent2} from "@opentui/solid"
@@ -88,7 +82,8 @@ import {insert as _$insert} from "@opentui/solid"
88
82
  import {setProp as _$setProp} from "@opentui/solid"
89
83
  import {createElement as _$createElement} from "@opentui/solid"
90
84
  import {useIcons} from "./tui.js"
91
- import {createComponent as _$createComponent3} from "@opentui/solid"
85
+ import {createComponent as _$createComponent4} from "@opentui/solid"
86
+ import {createSignal as createSignal2} from "solid-js"
92
87
  function createManualUpdateCheck(api,status,current,onUpdate){let checking=false
93
88
  async function read(){try{return await status.refreshNavigator?.()??"unavailable"
94
89
  } catch{return "unavailable"
@@ -108,7 +103,28 @@ api.ui.toast({variant:result==="unavailable"?"warning":"info",title:"Navigator u
108
103
  })
109
104
  } finally{checking=false
110
105
  }}}}
106
+ import {createComponent as _$createComponent3} from "@opentui/solid"
107
+ import {onCleanup} from "solid-js"
108
+ import {GuardedConfirmation,SelectionMenu} from "./tui.js"
109
+ function NestedVersionConfirmation(props){onCleanup(props.dismiss)
110
+ return _$createComponent3(GuardedConfirmation,{get api(){return props.api
111
+ },get current(){return props.current
112
+ },get title(){return `Update ${props.name}`
113
+ },get message(){return `Install ${props.version} using the current installation method?`
114
+ },get onConfirm(){return props.confirm
115
+ },render:({confirm
116
+ })=>_$createComponent3(SelectionMenu,{get api(){return props.api
117
+ },get title(){return `Update ${props.name}`
118
+ },get options(){return [{title:"Cancel",value:"cancel",description:"Return without installing an update"
119
+ },{title:`Install ${props.name} ${props.version}`,value:"install",description:"Keep the current installation method and scope; restart OpenCode afterward"
120
+ }]
121
+ },onSelect:(option)=>option.value==="install"?confirm():props.cancel()
122
+ })
123
+ })
124
+ }
111
125
  function createVersionUpdateActions(api,status,updater,rememberNavigatorVersion){const busy=new Set
126
+ const [navigatorUpdating,setNavigatorUpdating]=createSignal2(false)
127
+ const [navigatorInstalled,setNavigatorInstalled]=createSignal2()
112
128
  const installed={}
113
129
  let disposed=false
114
130
  let confirmation=0
@@ -124,6 +140,8 @@ return
124
140
  }
125
141
  api.ui.dialog.clear()
126
142
  busy.add(kind)
143
+ if(kind==="navigator")
144
+ setNavigatorUpdating(true)
127
145
  api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing ${version}...`
128
146
  });
129
147
  (async ()=>{if(kind==="navigator")
@@ -132,6 +150,8 @@ if(disposed||version!==target(kind))
132
150
  throw new Error("Update target changed; check again")
133
151
  await updater[kind](version)
134
152
  })().then(()=>{installed[kind]=version
153
+ if(kind==="navigator")
154
+ setNavigatorInstalled(version)
135
155
  if(disposed)
136
156
  return
137
157
  api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart OpenCode to use the new version.",duration:6000
@@ -140,16 +160,34 @@ api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart
140
160
  return
141
161
  api.ui.toast({variant:"error",title:`Could not update ${label(kind)}`,message:error instanceof Error?error.message:"Update failed",duration:6000
142
162
  })
143
- }).finally(()=>busy.delete(kind))
163
+ }).finally(()=>{busy.delete(kind)
164
+ if(kind==="navigator")
165
+ setNavigatorUpdating(false)
166
+ })
144
167
  }
145
- function open(kind,version=target(kind)){if(disposed||!version||version!==target(kind)||busy.has(kind))
168
+ function open(kind,version=target(kind),navigation){if(disposed||!version||version!==target(kind)||busy.has(kind))
146
169
  return
147
170
  if(installed[kind]===version){api.ui.toast({variant:"info",title:`${label(kind)} updated`,message:"Restart OpenCode to use the installed update."
148
171
  })
149
172
  return
150
173
  }
151
174
  const id=++confirmation
152
- api.ui.dialog.replace(()=>_$createComponent3(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
175
+ const onConfirm=()=>{if(id!==confirmation)
176
+ return
177
+ confirmation++
178
+ update(kind,version)
179
+ }
180
+ if(navigation){navigation.open(()=>_$createComponent4(NestedVersionConfirmation,{api,get name(){return label(kind)
181
+ },version,current:()=>!disposed&&id===confirmation&&version===target(kind),confirm:onConfirm,cancel:()=>{if(id!==confirmation)
182
+ return
183
+ confirmation++
184
+ navigation.back()
185
+ },dismiss:()=>{if(id===confirmation)
186
+ confirmation++
187
+ }}))
188
+ return
189
+ }
190
+ api.ui.dialog.replace(()=>_$createComponent4(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
153
191
  },message:`Update to ${version} using the current installation method? OpenCode must be restarted afterward.`,onConfirm:()=>{if(id!==confirmation)
154
192
  return
155
193
  confirmation++
@@ -169,7 +207,7 @@ const unregister=api.keymap.registerLayer({commands:[{name:`${PLUGIN_ID}.check-u
169
207
  api.lifecycle.onDispose(()=>{disposed=true
170
208
  unregister()
171
209
  })
172
- return{openCode:(version)=>open("openCode",version),navigator:(version)=>open("navigator",version)
210
+ return{openCode:(version)=>open("openCode",version),navigator:(version,navigation)=>open("navigator",version,navigation),navigatorUpdating,navigatorInstalled
173
211
  }}
174
212
  import {homedir} from "os"
175
213
  import {basename,dirname,isAbsolute,join} from "path"
@@ -241,5 +279,5 @@ if(item.source.target.startsWith("."))
241
279
  throw new Error("Unrecognized local Navigator source")
242
280
  await context.client.plugin.update({location:response.location,targets:[item.source.target]})
243
281
  }}}
244
- export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions
282
+ export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions,parseChangelog2 as parseChangelog,readBundledChangelog2 as readBundledChangelog
245
283
  }
@@ -589,10 +589,10 @@ _$setProp3(_el$,"width","100%")
589
589
  _$setProp3(_el$,"minWidth",0)
590
590
  _$setProp3(_el$,"gap",1)
591
591
  _$setProp3(_el$,"overflow","hidden")
592
- _$insert3(_el$,_$createComponent4(For,{get each(){return props.windows
593
- },children:(window)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
594
- },window
595
- })
592
+ _$insert3(_el$,_$createComponent4(For,{get each(){return props.windows.map((window)=>window.id)
593
+ },children:(_,index)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
594
+ },get window(){return props.windows[index()]
595
+ }})
596
596
  }))
597
597
  return _el$
598
598
  })()
@@ -644,7 +644,7 @@ listeners.clear()
644
644
  return{adapter,onUpdated(listener){listeners.add(listener)
645
645
  return()=>listeners.delete(listener)
646
646
  }}}
647
- var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode Console / Go",["workspace-budget","subscription"],"OpenCode 2.2 public usage API: applicable workspace USD budgets and percent-based Go allowance; not a universal account balance API.","https://github.com/anomalyco/opencode/blob/v2.2.0/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
647
+ var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode Console / Go",["workspace-budget","subscription"],"OpenCode 2.2 public usage API: applicable workspace USD budgets and percent-based Go allowance; not a universal account balance API.","https://github.com/anomalyco/opencode/blob/v2.2.5/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
648
648
  var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
649
649
  var aliases={fireworks:"fireworks-ai",novita:"novita-ai",moonshot:"moonshotai","minimax-coding-plan":"minimax","minimax-coding-plan-cn":"minimax-cn",together:"togetherai","silicon-flow":"siliconflow","github-copilot-enterprise":"github-copilot","google-vertex-anthropic":"google-vertex","z-ai":"zai",zhipuai:"zai",dashscope:"alibaba","alibaba-cn":"alibaba",qwen:"alibaba","opencode-go":"opencode","opencode-zen":"opencode",codeium:"windsurf","azure-cognitive-services":"azure"
650
650
  }
@@ -667,7 +667,10 @@ return [item.type,item.name,item.status]
667
667
  const key=()=>JSON.stringify([location(),connection(),revision()])
668
668
  const invalidate=()=>{generation++
669
669
  }
670
- const disposers=[context.data.on("credential.updated",invalidate),context.data.on("integration.updated",invalidate),context.data.on("credential.switched",(event)=>{if(event.data.integrationID!=="opencode")
670
+ const disposers=[context.data.on("credential.updated",invalidate),context.data.on("integration.updated",(event)=>{const directory=event.location?.directory
671
+ if(!directory||directory===location().directory)
672
+ invalidate()
673
+ }),context.data.on("credential.switched",(event)=>{if(event.data.integrationID!=="opencode")
671
674
  return
672
675
  generation++
673
676
  setRevision((value)=>value + 1)
@@ -1172,7 +1175,11 @@ if(lastModel&&!disposed)
1172
1175
  read(lastModel)
1173
1176
  }
1174
1177
  const events=["credential.switched","credential.updated","integration.updated","provider.updated","server.connected"]
1175
- const unsubscribes=events.map((event)=>context.data.on(event,()=>untrack2(()=>invalidate(event!=="provider.updated"))))
1178
+ const unsubscribes=events.map((event)=>context.data.on(event,(observation)=>untrack2(()=>{const directory=observation?.location?.directory
1179
+ if((event==="provider.updated"||event==="integration.updated")&&directory&&directory!==location().directory)
1180
+ return
1181
+ invalidate(event!=="provider.updated")
1182
+ })))
1176
1183
  return{suspend(){if(!lastModel)
1177
1184
  return
1178
1185
  lastModel=undefined
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "version": 1,
3
3
  "dependencies": {
4
- "@opencode/plugin": "2.2.0",
5
- "@opentui/core": "0.7.0",
6
- "@opentui/keymap": "0.7.0",
7
- "@opentui/solid": "0.7.0",
8
- "solid-js": "1.9.15"
4
+ "@opencode/plugin": "2.2.5",
5
+ "@opentui/core": "0.8.0",
6
+ "@opentui/keymap": "0.8.0",
7
+ "@opentui/solid": "0.8.0",
8
+ "solid-js": "1.9.18"
9
9
  }
10
10
  }