opencode-navigator 0.21.8 → 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.
@@ -1,5 +1,8 @@
1
1
  import {SECTION_DEFINITIONS,PLUGIN_ID
2
- } from "./chunk-d1bws32s.js";
2
+ } from "./chunk-bv0nm1tj.js"
3
+ import"./chunk-qmntexwm.js"
4
+ import {mcpPresetPreview
5
+ } from "./chunk-v6bz59xn.js";
3
6
  import {createComponent as _$createComponent7} from "@opentui/solid"
4
7
  import {setProp as _$setProp5} from "@opentui/solid"
5
8
  import {effect as _$effect5} from "@opentui/solid"
@@ -13,25 +16,6 @@ import {memo as _$memo4} from "@opentui/solid"
13
16
  import {For as For3,createMemo as createMemo3,onMount as onMount2} from "solid-js"
14
17
  import {useDialogs as useDialogs3,useIcons as useIcons6} from "./tui.js"
15
18
  import {createMemo} from "solid-js"
16
- function mcpToggleAction(status){if(status==="pending")
17
- return
18
- return status==="connected"?"disconnect":"connect"
19
- }
20
- function mcpPresetPreview(items,states){const byName=new Map(items.map((item)=>[item.name,item]))
21
- const names=[...new Set([...Object.keys(states),...byName.keys()])].sort((a,b)=>a.localeCompare(b))
22
- const rows=names.map((name)=>{const item=byName.get(name)
23
- const desired=Object.hasOwn(states,name)?states[name]:undefined
24
- const action=item&&mcpToggleAction(item.status)
25
- let change="unchanged"
26
- if(desired&&!item)
27
- change="missing"
28
- else if(desired&&!action)
29
- change="unavailable"
30
- else if(desired==="enabled"&&action==="connect"||desired==="disabled"&&action==="disconnect"){change=action
31
- }
32
- return{name,status:item?.status,desired,change}})
33
- const changes=rows.flatMap((row)=>row.change==="connect"||row.change==="disconnect"?[{name:row.name,action:row.change}]:[])
34
- return{rows,changes}}
35
19
  function createLayoutPresetPreview(preferences,name){const scope=preferences.selectedScopeKey()
36
20
  const valid=()=>preferences.selectedScopeKey()===scope
37
21
  const preset=createMemo(()=>preferences.layoutPresets()[name])
@@ -230,13 +214,7 @@ _$setProp3(_el$5,"contentOptions",{minHeight:0
230
214
  })
231
215
  _$setProp3(_el$6,"gap",1)
232
216
  _$insert3(_el$6,()=>props.children)
233
- _$effect3((_p$)=>{var{restore:_v$3,save:_v$4}=scroll,_v$5=Math.min(Math.max(2,Math.floor(dimensions().height*0.75)-14),Math.max(1,props.rowCount*3-1))
234
- _v$3!==_p$.e&&(_p$.e=_$setProp3(_el$5,"renderBefore",_v$3,_p$.e))
235
- _v$4!==_p$.t&&(_p$.t=_$setProp3(_el$5,"renderAfter",_v$4,_p$.t))
236
- _v$5!==_p$.a&&(_p$.a=_$setProp3(_el$5,"height",_v$5,_p$.a))
237
- return _p$
238
- },{e:undefined,t:undefined,a:undefined
239
- })
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))
240
218
  return _el$5
241
219
  })(),_$createComponent3(Show2,{get when(){return props.blocked||props.notice
242
220
  },get children(){var _el$7=_$createElement3("box"),_el$8=_$createElement3("text"),_el$9=_$createElement3("text")
@@ -250,9 +228,9 @@ _$setProp3(_el$9,"flexGrow",1)
250
228
  _$setProp3(_el$9,"wrapMode","word")
251
229
  _$setProp3(_el$9,"height",2)
252
230
  _$insert3(_el$9,()=>props.blocked||props.notice)
253
- _$effect3((_p$)=>{var _v$6=props.blocked?theme().warning:theme().accent,_v$7=props.blocked?theme().warning:theme().textMuted
254
- _v$6!==_p$.e&&(_p$.e=_$setProp3(_el$8,"fg",_v$6,_p$.e))
255
- _v$7!==_p$.t&&(_p$.t=_$setProp3(_el$9,"fg",_v$7,_p$.t))
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))
256
234
  return _p$
257
235
  },{e:undefined,t:undefined
258
236
  })
@@ -1,5 +1,5 @@
1
1
  import {PLUGIN_ID
2
- } from "./chunk-d1bws32s.js";
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
@@ -13,6 +13,10 @@ return next.numbers[index]>installed.numbers[index]
13
13
  }
14
14
  return installed.prerelease!==undefined&&next.prerelease===undefined
15
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
+ }
16
20
  var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
17
21
  var REQUEST_TIMEOUT_MS=5000
18
22
  async function readNavigatorVersion(request,signal){const response=await request(NAVIGATOR_PACKAGE_URL,{headers:{accept:"application/json"},signal})
@@ -29,7 +33,7 @@ let controller
29
33
  let pending
30
34
  let disposed=false
31
35
  const unsubscribe=api.event.on("installation.update-available",(event)=>{const version=event.properties.version
32
- setOpenCodeUpdate(isNewerVersion(version,api.app.version)?version:undefined)
36
+ setOpenCodeUpdate(isSameMajorVersion(version,api.app.version)&&isNewerVersion(version,api.app.version)?version:undefined)
33
37
  })
34
38
  api.lifecycle.onDispose(()=>{disposed=true
35
39
  controller?.abort()
@@ -170,6 +174,10 @@ return{openCode:(version)=>open("openCode",version),navigator:(version)=>open("n
170
174
  import {homedir} from "os"
171
175
  import {basename,dirname,isAbsolute,join} from "path"
172
176
  import {fileURLToPath} from "url"
177
+ async function upgradeHost(current,target,execute){if(!isSameMajorVersion(current,target))
178
+ throw new Error("OpenCode updates must stay on the current major version")
179
+ await execute([process.execPath,"upgrade",target])
180
+ }
173
181
  var run=async (command)=>{const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
174
182
  const error=new Response(child.stderr).text()
175
183
  const exitCode=await child.exited
@@ -207,7 +215,7 @@ if(configHasPlugin(await read(path),spec))
207
215
  return false
208
216
  return configHasPlugin(await read(global),spec)
209
217
  }
210
- function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
218
+ function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>upgradeHost(api.app.version,target,execute),async navigator(target){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
211
219
  return pinnedUpdate(wrapper,target,execute)
212
220
  }
213
221
  if(meta.source!=="npm")
@@ -217,7 +225,7 @@ const result=await api.plugins.install(`opencode-navigator@${target}`,{global:aw
217
225
  if(!result.ok)
218
226
  throw new Error(result.message)
219
227
  }}}
220
- function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>execute([process.execPath,"upgrade",target]),async navigator(target){const response=await context.client.plugin.list(context.location?{location:context.location}:{})
228
+ function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>upgradeHost(context.app.version,target,execute),async navigator(target){const response=await context.client.plugin.list(context.location?{location:context.location}:{})
221
229
  const item=response.data.find((plugin)=>plugin.id===PLUGIN_ID)
222
230
  if(!item)
223
231
  throw new Error("Navigator is not in the active plugin list")
@@ -65,7 +65,8 @@ function parsePluginSettings(value){const input=record(value)
65
65
  if(!input)
66
66
  return{}
67
67
  return{...typeof input.toggleKey==="string"&&input.toggleKey.trim()&&{toggleKey:input.toggleKey.trim()},...typeof input.focusKey==="string"&&input.focusKey.trim()&&{focusKey:input.focusKey.trim()},...typeof input.searchKey==="string"&&input.searchKey.trim()&&{searchKey:input.searchKey.trim()},...typeof input.persistMcp==="boolean"&&{persistMcp:input.persistMcp},...parseLimitsRefreshMinutes(input.limitsRefreshMinutes)!==undefined&&{limitsRefreshMinutes:parseLimitsRefreshMinutes(input.limitsRefreshMinutes)
68
- },...typeof input.startInChat==="boolean"&&{startInChat:input.startInChat},...typeof input.showSessionTitle==="boolean"&&{showSessionTitle:input.showSessionTitle},...typeof input.showSessionDate==="boolean"&&{showSessionDate:input.showSessionDate},...typeof input.cornerFont==="boolean"&&{cornerFont:input.cornerFont},...(input.lspIconStyle==="nerd"||input.lspIconStyle==="text")&&{lspIconStyle:input.lspIconStyle},...(input.rowDensity==="compact"||input.rowDensity==="comfortable")&&{rowDensity:input.rowDensity
68
+ },...typeof input.startInChat==="boolean"&&{startInChat:input.startInChat},...(input.toolbarOverflow==="menu"||input.toolbarOverflow==="wrap")&&{toolbarOverflow:input.toolbarOverflow
69
+ },...typeof input.showSessionTitle==="boolean"&&{showSessionTitle:input.showSessionTitle},...typeof input.showSessionDate==="boolean"&&{showSessionDate:input.showSessionDate},...typeof input.cornerFont==="boolean"&&{cornerFont:input.cornerFont},...(input.lspIconStyle==="nerd"||input.lspIconStyle==="text")&&{lspIconStyle:input.lspIconStyle},...(input.rowDensity==="compact"||input.rowDensity==="comfortable")&&{rowDensity:input.rowDensity
69
70
  },...Object.keys(parseSectionItemLimits(input.sectionItemLimits)).length>0&&{sectionItemLimits:parseSectionItemLimits(input.sectionItemLimits)
70
71
  },...parseQuickActionOrder(input.quickActionOrder)&&{quickActionOrder:parseQuickActionOrder(input.quickActionOrder)
71
72
  },...Object.keys(parseQuickActionVisibility(input.quickActionVisibility)).length>0&&{quickActionVisibility:parseQuickActionVisibility(input.quickActionVisibility)
@@ -189,12 +190,19 @@ throw new Error(`Expected format ${PORTABLE_SETTINGS_FORMAT}`)
189
190
  if(input.version!==1)
190
191
  throw new Error(`Unsupported settings version ${String(input.version)}`)
191
192
  const unsupported=[]
192
- unknownKeys(input,["format","version","layout","mcp"],"",unsupported)
193
+ unknownKeys(input,["format","version","layout","mcp","behavior"],"",unsupported)
193
194
  const hasLayout=Object.hasOwn(input,"layout")
194
195
  const hasMcp=Object.hasOwn(input,"mcp")
195
- if(!hasLayout&&!hasMcp)
196
+ const behavior={}
197
+ if(input.behavior!==undefined){const raw=object(input.behavior,"/behavior")
198
+ unknownKeys(raw,["toolbarOverflow"],"/behavior",unsupported)
199
+ if(raw.toolbarOverflow!==undefined){if(raw.toolbarOverflow!=="menu"&&raw.toolbarOverflow!=="wrap")
200
+ throw new Error("/behavior/toolbarOverflow must be menu or wrap")
201
+ behavior.toolbarOverflow=raw.toolbarOverflow
202
+ }}
203
+ if(!hasLayout&&!hasMcp&&!behavior.toolbarOverflow)
196
204
  throw new Error("Settings JSON contains no supported settings")
197
- const settings={format:PORTABLE_SETTINGS_FORMAT,version:1,...hasLayout&&{layout:parseLayout(input.layout,unsupported)},...hasMcp&&{mcp:parseMcp(input.mcp)}}
205
+ const settings={format:PORTABLE_SETTINGS_FORMAT,version:1,...hasLayout&&{layout:parseLayout(input.layout,unsupported)},...hasMcp&&{mcp:parseMcp(input.mcp)},...behavior.toolbarOverflow&&{behavior}}
198
206
  unsupported.sort()
199
207
  return{settings,unsupported}}
200
208
  var FILENAME=join(".opencode","navigator.json")
@@ -1,5 +1,5 @@
1
1
  import {PLUGIN_ID
2
- } from "./chunk-d1bws32s.js";
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 normalizeProviderQuota(snapshot){if(!snapshot.providerId||!snapshot.providerName||!snapshot.model.providerID||!snapshot.model.modelID||!snapshot.model.sessionID||!Number.isFinite(snapshot.fetchedAt)){throw new Error("Invalid provider quota identity")
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
- const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||window.scope!==undefined&&window.scope!=="account"&&window.scope!=="model"||(window.scope==="account"?!snapshot.accountId||window.modelID!==undefined:window.modelID!==snapshot.model.modelID)||!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")
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 Zen / Go",[],"Session token/cost statistics are not provider quota; no undocumented billing endpoints.","https://opencode.ai/docs/zen/"],["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.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 createSignal5,onCleanup} from "solid-js"
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]=createSignal5(false)
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 createSignal6,on} from "solid-js"
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]=createSignal6()
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 createSignal7} from "solid-js"
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]=createSignal7(0)
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 createSignal8,untrack as untrack2} from "solid-js"
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"}
@@ -1029,6 +1109,8 @@ if(active.length!==1)
1029
1109
  return{status:"unavailable"}
1030
1110
  const credential=active[0]
1031
1111
  const id=`credential:${credential.id}`
1112
+ if(credential.value.type==="external")
1113
+ return{status:"unsupported",id}
1032
1114
  const providerID=provider.canonical??provider.id
1033
1115
  if(credential.value.type==="key")
1034
1116
  return{status:"ready",id,method:"api",providerID}
@@ -1042,7 +1124,7 @@ const header=Object.entries(provider.headers??{}).find(([name])=>name.toLowerCas
1042
1124
  if(accountID&&header&&header!==accountID)
1043
1125
  return{status:"loading"}
1044
1126
  return{status:"ready",id,method:"oauth",providerID,...accountID&&{accountID}}}
1045
- function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal8({})
1127
+ function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal9({})
1046
1128
  let generation=0
1047
1129
  let disposed=false
1048
1130
  let lastModel
@@ -1091,7 +1173,12 @@ read(lastModel)
1091
1173
  }
1092
1174
  const events=["credential.switched","credential.updated","integration.updated","provider.updated","server.connected"]
1093
1175
  const unsubscribes=events.map((event)=>context.data.on(event,()=>untrack2(()=>invalidate(event!=="provider.updated"))))
1094
- return{current(model){lastModel=model
1176
+ return{suspend(){if(!lastModel)
1177
+ return
1178
+ lastModel=undefined
1179
+ generation++
1180
+ pending=undefined
1181
+ },current(model){lastModel=model
1095
1182
  return{...model,hostConnection:connections()[key(model)]??{status:"loading"}}},async refresh(model){await read(model)
1096
1183
  },async validate(model,signal){const actual=await read(model,signal)
1097
1184
  const expected=model.hostConnection
@@ -1135,16 +1222,29 @@ if(route.type!=="session"||!model)
1135
1222
  return
1136
1223
  return{sessionID:route.sessionID,providerID:model.providerID,modelID:model.modelID,...model.variant&&{variant:model.variant}}}
1137
1224
  function createProviderQuotaIntegration(api,preferences,version,context){const selectModel=()=>context?selectedV2Model(context):selectedV1Model(api)
1138
- const accountSource=context?createOpenCodeV2AccountSource(context,api.lifecycle.signal):undefined
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
+ }}
1139
1236
  const codex=createCodexQuotaAdapter(createCodexAppServerClient({version}),accountSource?.validate)
1140
1237
  const native=context?createNativeQuotaAdapter(context):undefined
1141
- const controller=createProviderLimitsController([codex,...native?[native.adapter]:[]],()=>{const model=selectModel()
1142
- return model?bindCodexAccount(accountSource?.current(model)??model,preferences):undefined
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)
1143
1243
  },api.lifecycle.signal,accountSource&&(async ()=>{const model=selectModel()
1144
1244
  if(model)
1145
1245
  await accountSource.refresh(model)
1146
1246
  }))
1147
1247
  return{controller,codex,accountSource,selectModel,modelSelection:createLimitsModelSelection(api,selectModel,context)
1148
1248
  }}
1149
- export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
1249
+ export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeUsageAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
1150
1250
  }
@@ -1,5 +1,5 @@
1
- import {QUICK_ACTION_IDS,PLUGIN_ID
2
- } from "./chunk-d1bws32s.js";
1
+ import {PLUGIN_ID
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"
@@ -11,16 +11,8 @@ import {setProp as _$setProp} from "@opentui/solid"
11
11
  import {createElement as _$createElement} from "@opentui/solid"
12
12
  import {TextAttributes} from "@opentui/core"
13
13
  import {useTerminalDimensions} from "@opentui/solid"
14
- import {For,createEffect,createMemo,onCleanup as onCleanup2} from "solid-js"
15
- import {createSignal,onCleanup} from "solid-js"
16
- var QUICK_ACTION_METADATA=[["\uEA73","Rename"],["\uEA82","Timeline"],["\uEBCC","Copy transcript"],["\uEBAC","Export"],["\uEAF5","Compact"],["\uEBEB","Switch session"],["\uEA60","New session"],["\uEB9E","Fork session"],["\uEBCC","Copy last response"],["\uEAA1","First message"],["\uEA9A","Last message"],["\uEA82","Last user message"],["\uEA9A","Next message"],["\uEAA1","Previous message"],["\uEBB3","Auto-approve permissions"]]
17
- var QUICK_ACTIONS=QUICK_ACTION_IDS.map((command,index)=>({command,icon:QUICK_ACTION_METADATA[index][0],label:QUICK_ACTION_METADATA[index][1]
18
- }))
19
- function quickActionLabel(api,action){if(action.command!=="permission.mode")
20
- return action.label
21
- const command=api.keymap.getCommands?.({visibility:"registered"}).find((candidate)=>candidate.name===action.command)
22
- return typeof command?.title==="string"?command.title:action.label
23
- }
14
+ import {For,createEffect,createMemo,onCleanup} from "solid-js"
15
+ import {QUICK_ACTIONS,quickActionLabel} from "./tui.js"
24
16
  import {DialogSurface,SelectionBox,useDialogScroll,useDialogState,useDialogs,useIcons} from "./tui.js"
25
17
  function QuickActionsDialog(props){const order=props.preferences.selectedQuickActionOrder
26
18
  const dialogs=useDialogs(props.api)
@@ -59,7 +51,7 @@ const unregister=props.api.keymap.registerLayer({mode:"modal",priority:1000,comm
59
51
  },{key:"escape",cmd:`${prefix}.back`
60
52
  }]
61
53
  })
62
- onCleanup2(unregister)
54
+ onCleanup(unregister)
63
55
  return _$createComponent(DialogSurface,{get api(){return props.api
64
56
  },id:prefix,lift:true,get children(){return [(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createTextNode(` Quick actions`),_el$4=_$createElement("text"),_el$5=_$createTextNode(` Back (`),_el$6=_$createTextNode(`)`)
65
57
  _$insertNode(_el$,_el$2)
@@ -132,13 +124,7 @@ return _el$21
132
124
  })()]
133
125
  }})
134
126
  }))
135
- _$effect((_p$)=>{var{restore:_v$4,save:_v$5}=scroll,_v$6=Math.min(actions().length*2-1,Math.max(3,Math.floor(dimensions().height*0.75)-8))
136
- _v$4!==_p$.e&&(_p$.e=_$setProp(_el$0,"renderBefore",_v$4,_p$.e))
137
- _v$5!==_p$.t&&(_p$.t=_$setProp(_el$0,"renderAfter",_v$5,_p$.t))
138
- _v$6!==_p$.a&&(_p$.a=_$setProp(_el$0,"height",_v$6,_p$.a))
139
- return _p$
140
- },{e:undefined,t:undefined,a:undefined
141
- })
127
+ _$effect((_$p)=>_$setProp(_el$0,"height",Math.min(actions().length*2-1,Math.max(3,Math.floor(dimensions().height*0.75)-8)),_$p))
142
128
  return _el$0
143
129
  })(),(()=>{var _el$10=_$createElement("text"),_el$11=_$createTextNode(` navigate \xB7 `),_el$12=_$createTextNode(` toggle \xB7 `),_el$13=_$createTextNode(` or `),_el$15=_$createTextNode(` reorder`)
144
130
  _$insertNode(_el$10,_el$11)
package/dist/rpc.js CHANGED
@@ -11,6 +11,7 @@ var NavigatorTodoRpc={id:"opencode-navigator.todo",methods:{list:{input:{type:"o
11
11
  },output
12
12
  }},events:{updated:{schema:{type:"object",properties:{sessionID,todos,guidance:{type:"boolean"}},required:["sessionID","todos","guidance"],additionalProperties:false
13
13
  }}}};
14
+ var LegacyTodoArchiveRpc={id:NavigatorTodoRpc.id,methods:{list:NavigatorTodoRpc.methods.list},events:{}};
14
15
  var text={type:"string",minLength:1,maxLength:512}
15
16
  var model={type:"object",properties:{providerID:text,modelID:text,sessionID:text,connectionID:text,variant:text,force:{type:"boolean"}},required:["providerID","modelID","sessionID"],additionalProperties:false
16
17
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 1,
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"
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ {"version":1,"bun":"1.3.14","node":"26.4.0"}