opencode-navigator 0.22.0 → 0.23.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 +8 -0
- package/HOST_API.md +59 -0
- package/PROVIDERS.md +11 -4
- package/README.md +30 -10
- package/dist/{chunk-dz8cfnfs.js → chunk-bv0nm1tj.js} +4 -4
- package/dist/layout-preset-actions.js +5 -11
- package/dist/navigator-updates.js +1 -1
- package/dist/provider-limits.js +115 -17
- package/dist/quick-actions-settings.js +2 -8
- package/dist/runtime-dependencies.json +4 -4
- package/dist/runtime-requirements.json +1 -0
- package/dist/server.js +11 -4
- package/dist/sidebar-settings.js +2 -8
- package/dist/sidebar-shortcuts.js +1 -1
- package/dist/toolbar-settings.js +1 -1
- package/dist/tui.js +337 -247
- package/package.json +13 -11
- package/scripts/snapshot-verifier.mjs +36 -0
- package/scripts/update-local-snapshot.mjs +29 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.23.0] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
- 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.
|
|
6
|
+
- Keep Skills, MCP servers, and Subagents current as OpenCode 2 updates, and launch the intended Skill when multiple sources have the same name.
|
|
7
|
+
- Show OpenCode Console workspace budgets and Go allowance through the public host usage API without a Navigator server plugin. Preserve workspace scope, native units, reset times, and explicit failure/retry states.
|
|
8
|
+
- Preserve Navigator shortcuts and nested-dialog navigation with OpenCode 2.2's keyboard handling.
|
|
9
|
+
- Verify a supported runtime before changing a pinned Navigator snapshot, preserving the installed version when no compatible verifier is available.
|
|
10
|
+
|
|
3
11
|
## [0.22.0] - 2026-10-23
|
|
4
12
|
|
|
5
13
|
- Pin Quick Actions and Skills to a compact sidebar toolbar independently of bookmarks. Choose icons in a searchable picker, customize supported state icons, reorder buttons, and select a More menu or wrapping rows. Preserve Skill confirmations, unavailable reasons, keyboard navigation, and Text fallback; hide the toolbar entirely when empty.
|
package/HOST_API.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Public Host APIs And Remaining Gaps
|
|
2
|
+
|
|
3
|
+
Navigator supports only OpenCode **1.19.5** and **2.2.0**, the current stable
|
|
4
|
+
release in each major line. Published SDK declarations and real-host checks
|
|
5
|
+
define the boundary; internal host components are not plugin APIs.
|
|
6
|
+
|
|
7
|
+
## Host-Owned Services
|
|
8
|
+
|
|
9
|
+
| Capability | OpenCode 1.19.5 | OpenCode 2.2.0 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Todo | Supported host Todo source/events | Public message cache, completed `todowrite` and revert boundary |
|
|
12
|
+
| Skills | Host discovery and prompt insertion | Native Skill cache and `session.skill` with exact ID |
|
|
13
|
+
| MCP | Host status and connection API | Native cache, sync/invalidate and connect/disconnect |
|
|
14
|
+
| Subagents | Supported child/status API and events | Native session cache/status and typed execution events |
|
|
15
|
+
| Commands | Public keymap | Public owned layers, targets, priorities, modes, discovery and dispatch |
|
|
16
|
+
| Dialogs/toasts | Public dialog components and mounting | Public show/prompt/confirm/set/clear and toast |
|
|
17
|
+
| Model/variant | Saved session model and host pickers | Reactive prompt model, variant API and host pickers |
|
|
18
|
+
| Provider usage | Codex's documented external protocol | Public Console/Go usage first; documented external sources outside coverage |
|
|
19
|
+
| Package updates | Public plugin manager | Public plugin manager |
|
|
20
|
+
|
|
21
|
+
## Focused Navigator Implementations
|
|
22
|
+
|
|
23
|
+
Each row records a missing capability, rather than a request failure that would
|
|
24
|
+
justify bypassing a native API.
|
|
25
|
+
|
|
26
|
+
| Implementation | Missing equivalent / preserved semantics | Removal condition |
|
|
27
|
+
| --- | --- | --- |
|
|
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. |
|
|
30
|
+
| 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
|
+
| 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
|
+
| 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. |
|
|
33
|
+
| Read-only Todo projection | Native completed tool calls and revert are public; the host's `sessionTodos` helper is not exported. No direct CRUD/priority API exists. | A public equivalent projection; editing also requires an explicitly selected interaction and supported API. |
|
|
34
|
+
| Console/Go request lifecycle and projection | `client.usage.limits` is public, but the internal usage cache and budget selectors are not in published Plugin Context. Workspace IDs are not provider account IDs. | Equivalent public cache and selector on the plugin interface. |
|
|
35
|
+
| External quotas / reset credits | Console/Go is not a universal provider API and has no Codex reset-credit consume operation. Sources stay documented, account-bound and portable. Native auth/request errors never enable a bypass reader. | Host APIs matching provider/account/units/resets and idempotent consume semantics. |
|
|
36
|
+
| Pinned-file snapshot installer | Native package updates do not implement the recognized versioned wrapper/staging layout. Preserve installation source and scope. | A public atomic file-snapshot update API with equivalent verification. |
|
|
37
|
+
| Corner masks and product UI | Multiline shape, three-cell controls, filters, groups, presets and pins are product features. Paint hooks only draw; scroll restoration runs outside paint. | Equivalent public primitives preserving geometry, behavior and fallback mode. |
|
|
38
|
+
|
|
39
|
+
## Unavailable Features
|
|
40
|
+
|
|
41
|
+
- OpenCode 2.2 publishes no LSP source, restart operation or diagnostic counters
|
|
42
|
+
to TUI plugins. The 1.x status source remains available.
|
|
43
|
+
- The 2.x title is host-owned; no public fixed-title/date visibility slot exists.
|
|
44
|
+
- The 1.19.5 TUI API lacks a prompt-model accessor before submission.
|
|
45
|
+
- GUI, image-generation and instructions-entry APIs do not reactivate retired
|
|
46
|
+
Todo guidance or unrelated features.
|
|
47
|
+
|
|
48
|
+
## Verification And Runtime
|
|
49
|
+
|
|
50
|
+
Both current hosts have isolated real-host checks. Historical installer fixtures
|
|
51
|
+
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
|
|
53
|
+
host owns its runtime. The 1.x host ships older runtime modules; its actual smoke
|
|
54
|
+
must pass rather than injecting the development modules into it.
|
|
55
|
+
|
|
56
|
+
OpenTUI 0.7 supports Node >=26.4.0 or Bun >=1.3.14. The updater verifies a staged
|
|
57
|
+
target with an available compatible runtime before switching a recognized local
|
|
58
|
+
wrapper. A lazy root import succeeding under an older Node does not prove full
|
|
59
|
+
renderer/FFI support. CI and package verification use Node 26.12.1.
|
package/PROVIDERS.md
CHANGED
|
@@ -5,15 +5,21 @@ It never treats session token/cost totals as remaining provider quota. Subscript
|
|
|
5
5
|
windows, API rate limits, configured capacities, and monetary balances are different
|
|
6
6
|
measurements and are labelled separately.
|
|
7
7
|
|
|
8
|
-
The
|
|
8
|
+
The external provider contracts were rechecked on 2026-10-06; the public
|
|
9
|
+
OpenCode Console/Go contract was added on 2026-10-28. The runtime also includes providers from
|
|
9
10
|
the host catalog; an unrecognized provider gets an explicit unverified-source state
|
|
10
11
|
rather than fabricated quota values. Availability depends on the selected account,
|
|
11
12
|
permissions, region, transport, and supported host APIs.
|
|
12
13
|
|
|
13
14
|
## Audited Read Sources
|
|
14
15
|
|
|
16
|
+
OpenCode 2.2 adds a direct public `client.usage.limits` source for Console/Go.
|
|
17
|
+
It takes priority for verified host coverage; an auth, permission, rate-limit or
|
|
18
|
+
network failure does not switch to a credential-based or external reader.
|
|
19
|
+
|
|
15
20
|
| Provider | Native source | Scope and prerequisites | Reference |
|
|
16
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) |
|
|
17
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) |
|
|
18
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) |
|
|
19
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) |
|
|
@@ -51,7 +57,7 @@ permissions, region, transport, and supported host APIs.
|
|
|
51
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/) |
|
|
52
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) |
|
|
53
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/) |
|
|
54
|
-
| OpenCode
|
|
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) |
|
|
55
61
|
| Cursor | Consumer account usage requires a supported external API; no browser cookies or local credential extraction | [Cursor docs](https://docs.cursor.com/) |
|
|
56
62
|
| Windsurf / Codeium | Consumer plan usage is separate from API response rate limits; no private account endpoints | [Windsurf docs](https://docs.windsurf.com/) |
|
|
57
63
|
| Amp | Product credits require a supported external account API; no credential-file or cookie integrations | [Amp manual](https://ampcode.com/manual) |
|
|
@@ -80,8 +86,9 @@ permissions, region, transport, and supported host APIs.
|
|
|
80
86
|
triggers inference, a credit purchase, a reset, or an authorization flow.
|
|
81
87
|
- HTTP-only observations may be absent for WebSocket transports. Report that
|
|
82
88
|
limitation instead of changing the host transport or reading private frames.
|
|
83
|
-
-
|
|
84
|
-
1.x keeps Codex support and capability
|
|
89
|
+
- Console/Go use the host client API directly; other native provider adapters use
|
|
90
|
+
the OpenCode 2 server plugin. OpenCode 1.x keeps Codex support and capability
|
|
91
|
+
guidance without accessing host internals.
|
|
85
92
|
- REST credentials are not reused against a native provider origin when the host
|
|
86
93
|
explicitly configures a different gateway origin. Redirects are rejected.
|
|
87
94
|
- Contract fixtures cover endpoint selection, regions, units, pagination, account
|
package/README.md
CHANGED
|
@@ -42,10 +42,12 @@ Todo starts expanded. Subagents, skills, quick actions, LSP, and MCP start
|
|
|
42
42
|
collapsed. Use `Save current layout as default` in sidebar settings to reuse the
|
|
43
43
|
current visible/hidden, expanded/collapsed, and ordering states in new sessions.
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
**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
|
|
48
|
+
and public host caches, events, keymap layers, and client APIs while retaining
|
|
49
|
+
the separate 1.x adapter. See [Host API boundaries](HOST_API.md) for remaining
|
|
50
|
+
capability gaps and removal conditions. On OpenCode 2, Navigator reads the same native Todo history as
|
|
49
51
|
the host sidebar, including completed `todowrite` calls and the current revert
|
|
50
52
|
boundary. Viewing Todo needs no Navigator server plugin. OpenCode 2.1 exposes
|
|
51
53
|
content and four statuses, without priorities or direct Todo CRUD. LSP is hidden on 2.x while retaining Subagents,
|
|
@@ -149,7 +151,7 @@ the uninstalled-font result remains visible rather than being documented only in
|
|
|
149
151
|
|
|
150
152
|

|
|
151
153
|
|
|
152
|
-
The [complete
|
|
154
|
+
The [complete screenshot gallery](screenshots/README.md) shows every
|
|
153
155
|
Todo and Subagent filter, sidebar section, Search and Settings tab, preset menu
|
|
154
156
|
and preview, confirmation, keyboard-help, setup, and icon-fallback state.
|
|
155
157
|
|
|
@@ -436,7 +438,7 @@ account usage. Separate model-specific buckets require explicit model associatio
|
|
|
436
438
|
Switching provider, model, session, or account invalidates stale reads and credit actions.
|
|
437
439
|
In OpenCode 2.x, Limits follows the prompt's selected model immediately, before a
|
|
438
440
|
message is sent, through the public reactive `ui.model.current()` API. OpenCode
|
|
439
|
-
1.x, rechecked at 1.19.
|
|
441
|
+
1.x, rechecked at 1.19.5, exposes only the saved session model: a newly selected prompt
|
|
440
442
|
model becomes visible to Navigator when the host saves it, normally on submission.
|
|
441
443
|
An immediate pre-submission switch in 1.x needs a new public host accessor or event.
|
|
442
444
|
CodexBar is useful for discovering providers, but its cookie, credential-file,
|
|
@@ -444,6 +446,7 @@ and undocumented endpoint integrations are not Navigator data sources.
|
|
|
444
446
|
|
|
445
447
|
| Source | Native measurement | Banked reset credits |
|
|
446
448
|
| --- | --- | --- |
|
|
449
|
+
| OpenCode Console / Go | Public host usage API: applicable workspace budgets in USD and Go allowance percentages with reset times | Read-only |
|
|
447
450
|
| Codex app-server | Rate-limit windows and account ID | Available count, optional details and expiry, confirmed manual consumption |
|
|
448
451
|
| MiniMax and NanoGPT | Explicit model-associated subscription windows; separate pay-as-you-go balances | Read-only |
|
|
449
452
|
| GitHub Copilot | Official SDK token-scoped premium allowance, checked against the selected model's billing metadata | Read-only |
|
|
@@ -453,10 +456,17 @@ and undocumented endpoint integrations are not Navigator data sources.
|
|
|
453
456
|
| OpenCode session statistics | Token and cost statistics, not subscription quota | Not applicable |
|
|
454
457
|
| Other providers | Capability guidance until a documented source and model/account mapping are verified | No action without a documented consume API |
|
|
455
458
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
459
|
+
OpenCode Console and Go use OpenCode 2.2's **public usage API directly**, without
|
|
460
|
+
the Navigator server plugin or credential reads. Workspace identity stays separate
|
|
461
|
+
from provider account identity. Shared and per-model budgets retain their native
|
|
462
|
+
scope; free models and unrelated model budgets are excluded. Missing data does
|
|
463
|
+
not mean zero spend or an unlimited allowance. Authentication and request failures
|
|
464
|
+
stay on the native source with explicit guidance or Retry.
|
|
465
|
+
|
|
466
|
+
Other additional sources use the **OpenCode 2 Navigator server plugin** and its
|
|
467
|
+
public integration/HTTP APIs. Configure it on the 2.x launch path as described in
|
|
468
|
+
the installation section. Native Todo remains independent. OpenCode 1.x retains
|
|
469
|
+
Codex support; sources requiring the 2.x server show setup guidance when it is absent.
|
|
460
470
|
The [source inventory](PROVIDERS.md) includes audited public API references,
|
|
461
471
|
permission requirements, and unsupported-source reasons.
|
|
462
472
|
|
|
@@ -467,6 +477,12 @@ response bodies. HTTP-only observations may be absent on WebSocket transports.
|
|
|
467
477
|
REST reads use a short in-memory cache; Refresh bypasses it. Observed headers older
|
|
468
478
|
than two minutes are marked stale instead of presented as current remaining quota.
|
|
469
479
|
|
|
480
|
+
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
|
|
482
|
+
pinned-snapshot update verifies its target with supported Bun (at least 1.3.14)
|
|
483
|
+
or Node before switching the wrapper. If neither is available, the running snapshot
|
|
484
|
+
is preserved with guidance; the updater does not install a runtime globally.
|
|
485
|
+
|
|
470
486
|
Limits is visible and expanded by default, first below the session title. Change
|
|
471
487
|
its visibility and order under **Settings → Sections** in Global or Current
|
|
472
488
|
worktree scope. Hiding or collapsing it does not stop background refreshes, and
|
|
@@ -475,6 +491,10 @@ available from the command palette.
|
|
|
475
491
|
|
|
476
492
|

|
|
477
493
|
|
|
494
|
+

|
|
495
|
+
|
|
496
|
+

|
|
497
|
+
|
|
478
498
|
### Link the intended Codex account
|
|
479
499
|
|
|
480
500
|
Install a Codex CLI exposing the documented read and consume RPCs (verified with
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
var SIDEBAR_SECTIONS=["limits","todo","subagents","skills","quick_actions","lsp","mcp"]
|
|
2
|
+
var SECTION_DEFINITIONS=[{name:"limits",label:"Limits"},{name:"todo",label:"Todo"},{name:"subagents",label:"Subagents"},{name:"skills",label:"Skills"},{name:"quick_actions",label:"Quick actions"},{name:"lsp",label:"LSP"},{name:"mcp",label:"MCP"}];
|
|
3
|
+
var MAX_LIMITS_REFRESH_MINUTES=Math.floor(2147483647 / 60000);
|
|
1
4
|
var PLUGIN_ID="opencode-navigator"
|
|
2
5
|
var TOGGLE_COMMAND=`${PLUGIN_ID}.toggle`
|
|
3
6
|
var FOCUS_COMMAND=`${PLUGIN_ID}.focus`
|
|
4
7
|
var SEARCH_COMMAND=`${PLUGIN_ID}.search`
|
|
5
|
-
var DEFAULT_SEARCH_KEY="ctrl+shift+k"
|
|
6
|
-
var SIDEBAR_SECTIONS=["limits","todo","subagents","skills","quick_actions","lsp","mcp"]
|
|
7
|
-
var SECTION_DEFINITIONS=[{name:"limits",label:"Limits"},{name:"todo",label:"Todo"},{name:"subagents",label:"Subagents"},{name:"skills",label:"Skills"},{name:"quick_actions",label:"Quick actions"},{name:"lsp",label:"LSP"},{name:"mcp",label:"MCP"}];
|
|
8
|
-
var MAX_LIMITS_REFRESH_MINUTES=Math.floor(2147483647 / 60000)
|
|
8
|
+
var DEFAULT_SEARCH_KEY="ctrl+shift+k"
|
|
9
9
|
export{SIDEBAR_SECTIONS,SECTION_DEFINITIONS,PLUGIN_ID,DEFAULT_SEARCH_KEY}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {SECTION_DEFINITIONS,PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js"
|
|
3
3
|
import"./chunk-qmntexwm.js"
|
|
4
4
|
import {mcpPresetPreview
|
|
5
5
|
} from "./chunk-v6bz59xn.js";
|
|
@@ -214,13 +214,7 @@ _$setProp3(_el$5,"contentOptions",{minHeight:0
|
|
|
214
214
|
})
|
|
215
215
|
_$setProp3(_el$6,"gap",1)
|
|
216
216
|
_$insert3(_el$6,()=>props.children)
|
|
217
|
-
_$effect3((
|
|
218
|
-
_v$3!==_p$.e&&(_p$.e=_$setProp3(_el$5,"renderBefore",_v$3,_p$.e))
|
|
219
|
-
_v$4!==_p$.t&&(_p$.t=_$setProp3(_el$5,"renderAfter",_v$4,_p$.t))
|
|
220
|
-
_v$5!==_p$.a&&(_p$.a=_$setProp3(_el$5,"height",_v$5,_p$.a))
|
|
221
|
-
return _p$
|
|
222
|
-
},{e:undefined,t:undefined,a:undefined
|
|
223
|
-
})
|
|
217
|
+
_$effect3((_$p)=>_$setProp3(_el$5,"height",Math.min(Math.max(2,Math.floor(dimensions().height*0.75)-14),Math.max(1,props.rowCount*3-1)),_$p))
|
|
224
218
|
return _el$5
|
|
225
219
|
})(),_$createComponent3(Show2,{get when(){return props.blocked||props.notice
|
|
226
220
|
},get children(){var _el$7=_$createElement3("box"),_el$8=_$createElement3("text"),_el$9=_$createElement3("text")
|
|
@@ -234,9 +228,9 @@ _$setProp3(_el$9,"flexGrow",1)
|
|
|
234
228
|
_$setProp3(_el$9,"wrapMode","word")
|
|
235
229
|
_$setProp3(_el$9,"height",2)
|
|
236
230
|
_$insert3(_el$9,()=>props.blocked||props.notice)
|
|
237
|
-
_$effect3((_p$)=>{var _v$
|
|
238
|
-
_v$
|
|
239
|
-
_v$
|
|
231
|
+
_$effect3((_p$)=>{var _v$3=props.blocked?theme().warning:theme().accent,_v$4=props.blocked?theme().warning:theme().textMuted
|
|
232
|
+
_v$3!==_p$.e&&(_p$.e=_$setProp3(_el$8,"fg",_v$3,_p$.e))
|
|
233
|
+
_v$4!==_p$.t&&(_p$.t=_$setProp3(_el$9,"fg",_v$4,_p$.t))
|
|
240
234
|
return _p$
|
|
241
235
|
},{e:undefined,t:undefined
|
|
242
236
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js";
|
|
3
3
|
import {createSignal} from "solid-js"
|
|
4
4
|
function versionParts(value){const match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?/.exec(value)
|
|
5
5
|
return match?{numbers:match.slice(1,4).map(Number),prerelease:match[4]||undefined}:undefined
|
package/dist/provider-limits.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js";
|
|
3
3
|
function createCodexAppServerClient(input){const command=input.command??["codex","app-server","--stdio"]
|
|
4
4
|
const timeoutMs=input.timeoutMs??1e4
|
|
5
5
|
const pending=new Map
|
|
@@ -115,9 +115,15 @@ return await send(method,params,signal)
|
|
|
115
115
|
failAll(new DOMException("Codex app-server closed","AbortError"))
|
|
116
116
|
child?.kill()
|
|
117
117
|
}}}
|
|
118
|
-
function
|
|
118
|
+
function windowScopeMatches(snapshot,window){if(window.scope==="workspace")
|
|
119
|
+
return Boolean(snapshot.workspaceId)&&(window.modelID===undefined||window.modelID===snapshot.model.modelID)
|
|
120
|
+
if(window.scope==="account")
|
|
121
|
+
return Boolean(snapshot.accountId)&&window.modelID===undefined
|
|
122
|
+
return window.modelID===snapshot.model.modelID
|
|
119
123
|
}
|
|
120
|
-
|
|
124
|
+
function normalizeProviderQuota(snapshot){if(!snapshot.providerId||!snapshot.providerName||!snapshot.model.providerID||!snapshot.model.modelID||!snapshot.model.sessionID||snapshot.workspaceId!==undefined&&(typeof snapshot.workspaceId!=="string"||!snapshot.workspaceId.trim())||!Number.isFinite(snapshot.fetchedAt)){throw new Error("Invalid provider quota identity")
|
|
125
|
+
}
|
|
126
|
+
const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||window.scope!==undefined&&window.scope!=="account"&&window.scope!=="model"&&window.scope!=="workspace"||!windowScopeMatches(snapshot,window)||!window.unit||window.used!==undefined&&!Number.isFinite(window.used)||window.remaining!==undefined&&!Number.isFinite(window.remaining)||window.used===undefined&&window.remaining===undefined&&window.total===undefined||window.total!==undefined&&(!Number.isFinite(window.total)||window.total<0)||window.durationMinutes!==undefined&&(!Number.isFinite(window.durationMinutes)||window.durationMinutes<0)||window.resetsAt!==undefined&&!Number.isFinite(window.resetsAt)){throw new Error("Quota window does not match the selected model or contains invalid values")
|
|
121
127
|
}
|
|
122
128
|
return{...window}})
|
|
123
129
|
const bankedResets=snapshot.bankedResets
|
|
@@ -239,6 +245,24 @@ requests.abortAll()
|
|
|
239
245
|
for(const adapter of adapters)
|
|
240
246
|
adapter.dispose?.()
|
|
241
247
|
}}}
|
|
248
|
+
function projectWorkspaceUsage(info,model,provider,costs=[]){if(!info.workspace.id||provider.id!==model.providerID||provider.integrationID!=="opencode")
|
|
249
|
+
throw new Error("Workspace usage does not match the active provider")
|
|
250
|
+
let windows=[]
|
|
251
|
+
if(provider.id==="opencode-go"){windows=Object.entries(info.go??{}).map(([id,meter])=>{if(!Number.isFinite(meter.percent)||meter.percent<0||meter.percent>100||!Number.isFinite(meter.resetsAt))
|
|
252
|
+
throw new Error("Invalid native Go meter")
|
|
253
|
+
return{id:`go:${id}`,label:id,unit:"%",kind:"subscription",scope:"workspace",total:100,used:meter.percent,remaining:100-meter.percent,resetsAt:meter.resetsAt / 1000,limited:meter.limited
|
|
254
|
+
}})
|
|
255
|
+
} else if(!(costs.length>0&&costs.every((cost)=>cost.input===0))){windows=info.budgets.flatMap((budget)=>{const entry=budget.models.find((item)=>item.providerID===model.providerID&&item.modelID===model.modelID)
|
|
256
|
+
if(budget.coverage==="models"&&!entry)
|
|
257
|
+
return []
|
|
258
|
+
const spent=budget.per==="model"&&entry?entry.spent??0:budget.spent
|
|
259
|
+
if([spent,budget.limit,budget.resetsAt].some((value)=>!Number.isFinite(value))||spent<0||budget.limit<0)
|
|
260
|
+
throw new Error("Invalid native Console budget")
|
|
261
|
+
return [{id:`budget:${budget.id}`,label:"Workspace budget",unit:"USD",kind:"configured_limit",scope:"workspace",...budget.per==="model"&&entry&&{modelID:model.modelID},total:budget.limit,used:spent,remaining:Math.max(0,budget.limit-spent),resetsAt:budget.resetsAt / 1000,budget:{scope:budget.scope,per:budget.per,shared:budget.shared}}]
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
return{model,providerId:provider.id,providerName:provider.name,workspaceId:info.workspace.id,...info.workspace.name&&{workspaceName:info.workspace.name},fetchedAt:Date.now(),availability:"ready",windows,...windows.length===0&&{message:provider.id==="opencode-go"?"No Go allowance was reported for this workspace.":"No workspace budgets apply to this model."
|
|
265
|
+
}}}
|
|
242
266
|
function codexQuotaWindows(usage,modelID){const buckets=new Map
|
|
243
267
|
const legacy=usage.rateLimits
|
|
244
268
|
if(legacy.limitId==="codex"||!legacy.limitId&&!legacy.normalModelSlug)
|
|
@@ -620,7 +644,7 @@ listeners.clear()
|
|
|
620
644
|
return{adapter,onUpdated(listener){listeners.add(listener)
|
|
621
645
|
return()=>listeners.delete(listener)
|
|
622
646
|
}}}
|
|
623
|
-
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
|
|
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"]]
|
|
624
648
|
var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
|
|
625
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"
|
|
626
650
|
}
|
|
@@ -628,6 +652,62 @@ function providerQuotaCapability(id,canonical){const key=aliases[canonical??id]?
|
|
|
628
652
|
const found=PROVIDER_QUOTA_CAPABILITIES.find((entry)=>entry.id===key)
|
|
629
653
|
return found??{id,name:id,sources:[],reason:"No independent quota/balance API or native counter semantics are verified for this provider.",documentation:["https://opencode.ai/docs/providers/"]
|
|
630
654
|
}}
|
|
655
|
+
import {createSignal as createSignal5} from "solid-js"
|
|
656
|
+
function createOpenCodeUsageAdapter(context){const [revision,setRevision]=createSignal5(0)
|
|
657
|
+
let generation=0
|
|
658
|
+
const location=()=>context.location??context.data.location.default()
|
|
659
|
+
const provider=(model)=>context.data.location.provider?.list(location())?.find((item)=>item.id===model.providerID)
|
|
660
|
+
const connection=()=>{const item=context.data.location.integration?.list(location())?.find((entry)=>entry.id==="opencode")?.connections[0]
|
|
661
|
+
if(!item)
|
|
662
|
+
return null
|
|
663
|
+
if(item.type==="credential")
|
|
664
|
+
return [item.type,item.id,item.method,item.status]
|
|
665
|
+
return [item.type,item.name,item.status]
|
|
666
|
+
}
|
|
667
|
+
const key=()=>JSON.stringify([location(),connection(),revision()])
|
|
668
|
+
const invalidate=()=>{generation++
|
|
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")
|
|
671
|
+
return
|
|
672
|
+
generation++
|
|
673
|
+
setRevision((value)=>value + 1)
|
|
674
|
+
}),context.data.on("server.connected",()=>{generation++
|
|
675
|
+
setRevision((value)=>value + 1)
|
|
676
|
+
})]
|
|
677
|
+
const adapter={id:"opencode-usage",name:"OpenCode workspace limits",supports:(model)=>typeof context.client.usage?.limits==="function"&&provider(model)?.integrationID==="opencode",async read(model,signal){signal.throwIfAborted()
|
|
678
|
+
const epoch=generation
|
|
679
|
+
const target=key()
|
|
680
|
+
const activeProvider=provider(model)
|
|
681
|
+
if(!activeProvider||activeProvider.integrationID!=="opencode")
|
|
682
|
+
throw new Error("Native usage provider changed")
|
|
683
|
+
try{const result=await context.client.usage.limits({location:{directory:location().directory}},{signal})
|
|
684
|
+
if(epoch!==generation||target!==key()||signal.aborted||result.location.directory!==location().directory)
|
|
685
|
+
throw new DOMException("Workspace usage target changed","AbortError")
|
|
686
|
+
if(!result.data)
|
|
687
|
+
return{model,providerId:model.providerID,providerName:activeProvider.name,fetchedAt:Date.now(),availability:"unauthenticated",windows:[],message:"Sign in to OpenCode Console to view workspace limits."
|
|
688
|
+
}
|
|
689
|
+
const costs=context.data.location.model?.list(location())?.find((item)=>item.providerID===model.providerID&&item.id===model.modelID)?.cost
|
|
690
|
+
return projectWorkspaceUsage(result.data,model,activeProvider,costs)
|
|
691
|
+
} catch(error){if(signal.aborted||epoch!==generation||target!==key()||error instanceof DOMException&&error.name==="AbortError")
|
|
692
|
+
throw error
|
|
693
|
+
const status=error?.status??error?.response?.status
|
|
694
|
+
const tag=error?._tag
|
|
695
|
+
if(status===undefined&&tag!=="UnauthorizedError"&&tag!=="ForbiddenError")
|
|
696
|
+
throw new Error("OpenCode workspace limits are unavailable. Refresh to retry the native source.",{cause:error
|
|
697
|
+
})
|
|
698
|
+
const states={401:"unauthenticated",403:"permission_required",429:"rate_limited"}
|
|
699
|
+
let availability="unavailable"
|
|
700
|
+
if(status!==undefined&&status in states)
|
|
701
|
+
availability=states[status]
|
|
702
|
+
if(tag==="UnauthorizedError")
|
|
703
|
+
availability="unauthenticated"
|
|
704
|
+
else if(tag==="ForbiddenError")
|
|
705
|
+
availability="permission_required"
|
|
706
|
+
return{model,providerId:model.providerID,providerName:activeProvider.name,fetchedAt:Date.now(),windows:[],availability,message:"OpenCode workspace limits are unavailable. Refresh to retry the native source."
|
|
707
|
+
}}},dispose(){for(const dispose of disposers)
|
|
708
|
+
dispose()
|
|
709
|
+
}}
|
|
710
|
+
return{adapter,target:(model)=>({...model,quotaContext:key()})}}
|
|
631
711
|
import {insert as _$insert9} from "@opentui/solid"
|
|
632
712
|
import {mergeProps as _$mergeProps} from "@opentui/solid"
|
|
633
713
|
import {setProp as _$setProp10} from "@opentui/solid"
|
|
@@ -654,7 +734,7 @@ import {insert as _$insert4} from "@opentui/solid"
|
|
|
654
734
|
import {setProp as _$setProp4} from "@opentui/solid"
|
|
655
735
|
import {createElement as _$createElement4} from "@opentui/solid"
|
|
656
736
|
import {SelectionBox} from "./tui.js"
|
|
657
|
-
import {createSignal as
|
|
737
|
+
import {createSignal as createSignal6,onCleanup} from "solid-js"
|
|
658
738
|
function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
|
|
659
739
|
let backgroundColor=theme.backgroundPanel
|
|
660
740
|
let foregroundColor=normalColor
|
|
@@ -670,7 +750,7 @@ else if(highlighted)
|
|
|
670
750
|
foregroundColor=theme.selectedListItemText
|
|
671
751
|
return{backgroundColor,foregroundColor}}
|
|
672
752
|
function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
|
|
673
|
-
const [hovered,setHovered]=
|
|
753
|
+
const [hovered,setHovered]=createSignal6(false)
|
|
674
754
|
const disabled=()=>descriptor.disabled?.()===true
|
|
675
755
|
const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
|
|
676
756
|
const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
|
|
@@ -737,7 +817,7 @@ import {insert as _$insert5} from "@opentui/solid"
|
|
|
737
817
|
import {setProp as _$setProp6} from "@opentui/solid"
|
|
738
818
|
import {createElement as _$createElement6} from "@opentui/solid"
|
|
739
819
|
import {memo as _$memo3} from "@opentui/solid"
|
|
740
|
-
import {createEffect as createEffect2,createSignal as
|
|
820
|
+
import {createEffect as createEffect2,createSignal as createSignal7,on} from "solid-js"
|
|
741
821
|
import {SelectionBox as SelectionBox2} from "./tui.js"
|
|
742
822
|
var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
|
|
743
823
|
})
|
|
@@ -754,7 +834,7 @@ result += segment
|
|
|
754
834
|
}
|
|
755
835
|
return result + ellipsis
|
|
756
836
|
}
|
|
757
|
-
function SectionHeaderControl(props){const [width,setWidth]=
|
|
837
|
+
function SectionHeaderControl(props){const [width,setWidth]=createSignal7()
|
|
758
838
|
const control=useSidebarItem(props.api,props.interaction,{id:props.action.id,position:()=>props.position,disabled:props.action.disabled,activate:props.action.onActivate
|
|
759
839
|
},()=>props.api.theme.current.accent,"control")
|
|
760
840
|
createEffect2(on(()=>[props.action.label(),props.resetWidth],()=>setWidth(undefined)))
|
|
@@ -883,10 +963,10 @@ import {memo as _$memo6} from "@opentui/solid"
|
|
|
883
963
|
import {insert as _$insert8} from "@opentui/solid"
|
|
884
964
|
import {setProp as _$setProp9} from "@opentui/solid"
|
|
885
965
|
import {createElement as _$createElement9} from "@opentui/solid"
|
|
886
|
-
import {Show as Show6,createSignal as
|
|
966
|
+
import {Show as Show6,createSignal as createSignal8} from "solid-js"
|
|
887
967
|
import {SelectionBox as SelectionBox3,useIcons as useIcons4} from "./tui.js"
|
|
888
968
|
function Section(props){const icons=useIcons4()
|
|
889
|
-
const [headerWidth,setHeaderWidth]=
|
|
969
|
+
const [headerWidth,setHeaderWidth]=createSignal8(0)
|
|
890
970
|
const icon=()=>props.section?icons.section(props.section):props.icon
|
|
891
971
|
const theme=()=>props.api.theme.current
|
|
892
972
|
const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
|
|
@@ -1020,7 +1100,7 @@ const model=api.state.session.get(sessionID)?.model
|
|
|
1020
1100
|
if(!model)
|
|
1021
1101
|
return
|
|
1022
1102
|
return{sessionID,providerID:model.providerID,modelID:model.id,...model.variant&&{variant:model.variant}}}
|
|
1023
|
-
import {createSignal as
|
|
1103
|
+
import {createSignal as createSignal9,untrack as untrack2} from "solid-js"
|
|
1024
1104
|
var CHATGPT_METHODS=new Set(["chatgpt-browser","chatgpt-headless"])
|
|
1025
1105
|
function providerAccountIdentity(provider,credentials){if(!provider.integrationID||provider.activation==="disabled")
|
|
1026
1106
|
return{status:"unsupported"}
|
|
@@ -1044,7 +1124,7 @@ const header=Object.entries(provider.headers??{}).find(([name])=>name.toLowerCas
|
|
|
1044
1124
|
if(accountID&&header&&header!==accountID)
|
|
1045
1125
|
return{status:"loading"}
|
|
1046
1126
|
return{status:"ready",id,method:"oauth",providerID,...accountID&&{accountID}}}
|
|
1047
|
-
function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=
|
|
1127
|
+
function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal9({})
|
|
1048
1128
|
let generation=0
|
|
1049
1129
|
let disposed=false
|
|
1050
1130
|
let lastModel
|
|
@@ -1093,7 +1173,12 @@ read(lastModel)
|
|
|
1093
1173
|
}
|
|
1094
1174
|
const events=["credential.switched","credential.updated","integration.updated","provider.updated","server.connected"]
|
|
1095
1175
|
const unsubscribes=events.map((event)=>context.data.on(event,()=>untrack2(()=>invalidate(event!=="provider.updated"))))
|
|
1096
|
-
return{
|
|
1176
|
+
return{suspend(){if(!lastModel)
|
|
1177
|
+
return
|
|
1178
|
+
lastModel=undefined
|
|
1179
|
+
generation++
|
|
1180
|
+
pending=undefined
|
|
1181
|
+
},current(model){lastModel=model
|
|
1097
1182
|
return{...model,hostConnection:connections()[key(model)]??{status:"loading"}}},async refresh(model){await read(model)
|
|
1098
1183
|
},async validate(model,signal){const actual=await read(model,signal)
|
|
1099
1184
|
const expected=model.hostConnection
|
|
@@ -1137,16 +1222,29 @@ if(route.type!=="session"||!model)
|
|
|
1137
1222
|
return
|
|
1138
1223
|
return{sessionID:route.sessionID,providerID:model.providerID,modelID:model.modelID,...model.variant&&{variant:model.variant}}}
|
|
1139
1224
|
function createProviderQuotaIntegration(api,preferences,version,context){const selectModel=()=>context?selectedV2Model(context):selectedV1Model(api)
|
|
1140
|
-
const
|
|
1225
|
+
const hostAccounts=context?createOpenCodeV2AccountSource(context,api.lifecycle.signal):undefined
|
|
1226
|
+
const usage=context?createOpenCodeUsageAdapter(context):undefined
|
|
1227
|
+
const accountSource=hostAccounts&&{...hostAccounts,current(model){if(usage?.adapter.supports(model)){hostAccounts.suspend?.()
|
|
1228
|
+
return usage.target(model)
|
|
1229
|
+
}
|
|
1230
|
+
return hostAccounts.current(model)
|
|
1231
|
+
},refresh(model){if(usage?.adapter.supports(model)){hostAccounts.suspend?.()
|
|
1232
|
+
return Promise.resolve()
|
|
1233
|
+
}
|
|
1234
|
+
return hostAccounts.refresh(model)
|
|
1235
|
+
}}
|
|
1141
1236
|
const codex=createCodexQuotaAdapter(createCodexAppServerClient({version}),accountSource?.validate)
|
|
1142
1237
|
const native=context?createNativeQuotaAdapter(context):undefined
|
|
1143
|
-
const controller=createProviderLimitsController([codex,...native?[native.adapter]:[]],()=>{const model=selectModel()
|
|
1144
|
-
|
|
1238
|
+
const controller=createProviderLimitsController([...usage?[usage.adapter]:[],codex,...native?[native.adapter]:[]],()=>{const model=selectModel()
|
|
1239
|
+
if(!model)
|
|
1240
|
+
return
|
|
1241
|
+
const selected=accountSource?.current(model)??model
|
|
1242
|
+
return usage?.adapter.supports(model)?selected:bindCodexAccount(selected,preferences)
|
|
1145
1243
|
},api.lifecycle.signal,accountSource&&(async ()=>{const model=selectModel()
|
|
1146
1244
|
if(model)
|
|
1147
1245
|
await accountSource.refresh(model)
|
|
1148
1246
|
}))
|
|
1149
1247
|
return{controller,codex,accountSource,selectModel,modelSelection:createLimitsModelSelection(api,selectModel,context)
|
|
1150
1248
|
}}
|
|
1151
|
-
export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
|
|
1249
|
+
export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeUsageAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
|
|
1152
1250
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js";
|
|
3
3
|
import {memo as _$memo} from "@opentui/solid"
|
|
4
4
|
import {createComponent as _$createComponent} from "@opentui/solid"
|
|
5
5
|
import {use as _$use} from "@opentui/solid"
|
|
@@ -124,13 +124,7 @@ return _el$21
|
|
|
124
124
|
})()]
|
|
125
125
|
}})
|
|
126
126
|
}))
|
|
127
|
-
_$effect((
|
|
128
|
-
_v$4!==_p$.e&&(_p$.e=_$setProp(_el$0,"renderBefore",_v$4,_p$.e))
|
|
129
|
-
_v$5!==_p$.t&&(_p$.t=_$setProp(_el$0,"renderAfter",_v$5,_p$.t))
|
|
130
|
-
_v$6!==_p$.a&&(_p$.a=_$setProp(_el$0,"height",_v$6,_p$.a))
|
|
131
|
-
return _p$
|
|
132
|
-
},{e:undefined,t:undefined,a:undefined
|
|
133
|
-
})
|
|
127
|
+
_$effect((_$p)=>_$setProp(_el$0,"height",Math.min(actions().length*2-1,Math.max(3,Math.floor(dimensions().height*0.75)-8)),_$p))
|
|
134
128
|
return _el$0
|
|
135
129
|
})(),(()=>{var _el$10=_$createElement("text"),_el$11=_$createTextNode(` navigate \xB7 `),_el$12=_$createTextNode(` toggle \xB7 `),_el$13=_$createTextNode(` or `),_el$15=_$createTextNode(` reorder`)
|
|
136
130
|
_$insertNode(_el$10,_el$11)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@opencode/plugin": "2.
|
|
5
|
-
"@opentui/core": "0.
|
|
6
|
-
"@opentui/keymap": "0.
|
|
7
|
-
"@opentui/solid": "0.
|
|
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
8
|
"solid-js": "1.9.15"
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":1,"bun":"1.3.14","node":"26.4.0"}
|