opencode-navigator 0.21.0 → 0.21.2

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.
@@ -117,7 +117,7 @@ child?.kill()
117
117
  }}}
118
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")
119
119
  }
120
- const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||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")
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")
121
121
  }
122
122
  return{...window}})
123
123
  const bankedResets=snapshot.bankedResets
@@ -239,8 +239,23 @@ requests.abortAll()
239
239
  for(const adapter of adapters)
240
240
  adapter.dispose?.()
241
241
  }}}
242
- function windows(limit,model){return ["primary","secondary"].flatMap((name)=>{const value=limit[name]
243
- return value?[{id:`${limit.limitId ?? model.modelID}.${name}`,label:limit.limitName?`${limit.limitName} \xB7 ${name}`:name,limitID:limit.limitId??undefined,modelID:model.modelID,unit:"%",used:value.usedPercent,...value.windowDurationMins!==null&&{durationMinutes:value.windowDurationMins},...value.resetsAt!==null&&{resetsAt:value.resetsAt}}]:[]
242
+ function codexQuotaWindows(usage,modelID){const buckets=new Map
243
+ const legacy=usage.rateLimits
244
+ if(legacy.limitId==="codex"||!legacy.limitId&&!legacy.normalModelSlug)
245
+ buckets.set("codex",legacy)
246
+ const entries=Object.entries(usage.rateLimitsByLimitId??{})
247
+ for(const [key,limit] of entries)
248
+ buckets.set(limit.limitId??key,limit)
249
+ if(legacy.limitId&&!buckets.has(legacy.limitId))
250
+ buckets.set(legacy.limitId,legacy)
251
+ return [...buckets].flatMap(([id,limit])=>{const account=id==="codex"
252
+ if(!account&&limit.normalModelSlug!==modelID)
253
+ return []
254
+ return ["primary","secondary"].flatMap((name)=>{const value=limit[name]
255
+ if(!value)
256
+ return []
257
+ return [{id:`${id}.${name}`,label:account?`Codex account \xB7 ${name}`:`${limit.limitName ?? id} \xB7 ${name}`,limitID:id,...account?{scope:"account"}:{modelID},unit:"%",used:value.usedPercent,...value.windowDurationMins!==null&&{durationMinutes:value.windowDurationMins},...value.resetsAt!==null&&{resetsAt:value.resetsAt}}]
258
+ })
244
259
  })
245
260
  }
246
261
  function createCodexQuotaAdapter(client,validateHost){const adapter={id:"codex",name:"Codex CLI",supports:(model)=>(model.providerID==="openai"||model.hostConnection?.providerID==="openai")&&model.hostConnection?.method!=="api"&&Boolean(model.accountID)&&(!model.hostConnection||model.hostConnection.status==="ready"),...validateHost&&{validateHost},async discoverAccount(signal){const account=await client.request("account/read",{refreshToken:false},signal)
@@ -260,12 +275,11 @@ return{...base,availability:"unauthenticated"}
260
275
  const usage=await client.request("account/rateLimits/read",{supportsLunaReserve:false,excludeResetCreditDetails:false},signal)
261
276
  if(!usage.accountId||usage.accountId!==model.accountID)
262
277
  return{...base,availability:"unauthenticated"}
263
- const candidates=Object.values(usage.rateLimitsByLimitId??{})
264
- const matched=(candidates.length>0?candidates:[usage.rateLimits]).filter((limit)=>limit.normalModelSlug===model.modelID)
265
- if(matched.length===0)
266
- return{...base,accountId:usage.accountId,availability:"unsupported"}
278
+ const windows=codexQuotaWindows(usage,model.modelID)
267
279
  const creditSummary=usage.rateLimitResetCredits
268
- return normalizeProviderQuota({...base,accountId:usage.accountId,availability:"ready",ordinaryUsageAllowed:usage.ordinaryUsageAllowed??undefined,windows:matched.flatMap((limit)=>windows(limit,model)),...creditSummary&&{bankedResets:{availableCount:creditSummary.availableCount,...creditSummary.credits&&{credits:creditSummary.credits.map((credit)=>({id:credit.id,type:credit.resetType,status:credit.status,grantedAt:credit.grantedAt,...credit.expiresAt!==null&&{expiresAt:credit.expiresAt},...credit.title&&{title:credit.title},...credit.description&&{description:credit.description}}))
280
+ return normalizeProviderQuota({...base,accountId:usage.accountId,availability:windows.length>0?"ready":"unavailable",ordinaryUsageAllowed:usage.ordinaryUsageAllowed??undefined,windows,...windows.some((window)=>window.scope==="account")&&{message:"Codex account quota; shared across models, not a model-specific allowance."
281
+ },...windows.length===0&&{message:"Codex reported no applicable usage windows for this account and model."
282
+ },...creditSummary&&{bankedResets:{availableCount:creditSummary.availableCount,...creditSummary.credits&&{credits:creditSummary.credits.map((credit)=>({id:credit.id,type:credit.resetType,status:credit.status,grantedAt:credit.grantedAt,...credit.expiresAt!==null&&{expiresAt:credit.expiresAt},...credit.title&&{title:credit.title},...credit.description&&{description:credit.description}}))
269
283
  }}}})
270
284
  },async prepareResetCreditConsumption(model,creditID,signal){const snapshot=await adapter.read(model,signal)
271
285
  if(snapshot.availability==="unauthenticated"||!snapshot.accountId||snapshot.accountId!==model.accountID){throw new Error("Codex account is unavailable or changed")
@@ -348,36 +362,63 @@ return{link,busy
348
362
  import {effect as _$effect} from "@opentui/solid"
349
363
  import {insertNode as _$insertNode} from "@opentui/solid"
350
364
  import {insert as _$insert} from "@opentui/solid"
351
- import {memo as _$memo} from "@opentui/solid"
352
365
  import {setProp as _$setProp} from "@opentui/solid"
353
366
  import {createElement as _$createElement} from "@opentui/solid"
354
- function QuotaWindowRow(props){const kind=()=>props.window.kind==="rate_limit"?"API \xB7 ":props.window.kind==="configured_limit"?"Limit \xB7 ":""
355
- const measurement=()=>{if(props.window.used!==undefined)
356
- return `${props.window.used}${props.window.total === undefined ? "" : `/${props.window.total}`}${props.window.unit === "%" ? "" : " "}${props.window.unit} used`
357
- if(props.window.remaining!==undefined)
358
- return `${props.window.remaining} ${props.window.unit} remaining`
359
- return `${props.window.total} ${props.window.unit} capacity \xB7 usage unknown`
367
+ import {useIcons} from "./tui.js"
368
+ function duration(minutes){if(minutes%10080===0)
369
+ return `${minutes / 10080}w`
370
+ if(minutes%1440===0)
371
+ return `${minutes / 1440}d`
372
+ if(minutes%60===0)
373
+ return `${minutes / 60}h`
374
+ return `${minutes}m`
375
+ }
376
+ function measurement(window,remaining){if(remaining!==undefined)
377
+ return `${remaining}${window.unit === "%" ? "" : " "}${window.unit} left`
378
+ if(window.used!==undefined)
379
+ return `${window.used} ${window.unit} used`
380
+ return `${window.total} ${window.unit} capacity`
360
381
  }
361
- return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createElement("text"),_el$4=_$createElement("text")
382
+ function resetTime(seconds,now){if(seconds===undefined)
383
+ return "Reset time unknown"
384
+ const date=new Date(seconds*1000)
385
+ const clock=date.toLocaleTimeString(undefined,{hour:"2-digit",minute:"2-digit"})
386
+ if(date.toDateString()===new Date(now).toDateString())
387
+ return `Resets ${clock}`
388
+ return `Resets ${date.toLocaleDateString(undefined, { month: "short", day: "numeric" })} ${clock}`
389
+ }
390
+ function quotaPresentation(window,now=Date.now()){const total=window.unit==="%"?100:window.total
391
+ const remaining=window.remaining??(total!==undefined&&window.used!==undefined?total-window.used:undefined)
392
+ const ratio=remaining!==undefined&&total!==undefined&&total>0?Math.max(0,Math.min(1,remaining / total)):undefined
393
+ const account=window.scope==="account"?"Account":undefined
394
+ const scope=window.kind==="rate_limit"?"API":account
395
+ const period=window.durationMinutes?duration(window.durationMinutes):undefined
396
+ const context=[scope,window.kind?window.label:period??window.label,window.kind?period:undefined].filter(Boolean).join(" \xB7 ")
397
+ return{value:measurement(window,remaining),ratio,reset:`${resetTime(window.resetsAt, now)} \xB7 ${context}`}}
398
+ function QuotaWindowRow(props){const icons=useIcons()
399
+ const view=()=>quotaPresentation(props.window)
400
+ const scale=()=>{const ratio=view().ratio
401
+ if(ratio===undefined)
402
+ return " \xB7 scale unknown"
403
+ const filled=Math.round(ratio*10)
404
+ const symbols=icons.style()==="text"?["#","-"]:["\u2588","\u2591"]
405
+ return ` [${symbols[0].repeat(filled)}${symbols[1].repeat(10 - filled)}]`
406
+ }
407
+ return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createElement("span"),_el$4=_$createElement("text")
362
408
  _$insertNode(_el$,_el$2)
363
- _$insertNode(_el$,_el$3)
364
409
  _$insertNode(_el$,_el$4)
410
+ _$insertNode(_el$2,_el$3)
365
411
  _$setProp(_el$2,"wrapMode","none")
366
412
  _$setProp(_el$2,"truncate",true)
367
- _$insert(_el$2,kind,null)
368
- _$insert(_el$2,()=>props.window.label,null)
369
- _$insert(_el$2,(()=>{var _c$=_$memo(()=>props.window.durationMinutes===undefined)
370
- return()=>_c$()?"":` \xB7 ${props.window.durationMinutes} min`
371
- })(),null)
372
- _$setProp(_el$3,"wrapMode","word")
373
- _$insert(_el$3,measurement)
374
- _$setProp(_el$4,"wrapMode","word")
375
- _$insert(_el$4,(()=>{var _c$2=_$memo(()=>props.window.resetsAt===undefined)
376
- return()=>_c$2()?"Reset time unavailable":`resets ${new Date(props.window.resetsAt * 1000).toLocaleString()}`
377
- })())
378
- _$effect((_p$)=>{var _v$=props.api.theme.current.text,_v$2=props.api.theme.current.text,_v$3=props.api.theme.current.textMuted
413
+ _$insert(_el$3,()=>view().value)
414
+ _$insert(_el$2,scale,null)
415
+ _$setProp(_el$4,"wrapMode","none")
416
+ _$setProp(_el$4,"truncate",true)
417
+ _$insert(_el$4,()=>view().reset)
418
+ _$effect((_p$)=>{var _v$=props.api.theme.current.textMuted,_v$2={fg:props.api.theme.current.text
419
+ },_v$3=props.api.theme.current.textMuted
379
420
  _v$!==_p$.e&&(_p$.e=_$setProp(_el$2,"fg",_v$,_p$.e))
380
- _v$2!==_p$.t&&(_p$.t=_$setProp(_el$3,"fg",_v$2,_p$.t))
421
+ _v$2!==_p$.t&&(_p$.t=_$setProp(_el$3,"style",_v$2,_p$.t))
381
422
  _v$3!==_p$.a&&(_p$.a=_$setProp(_el$4,"fg",_v$3,_p$.a))
382
423
  return _p$
383
424
  },{e:undefined,t:undefined,a:undefined
@@ -466,49 +507,32 @@ actions.review(option.value.slice("credit:".length)||undefined)
466
507
  }})
467
508
  }
468
509
  import {createComponent as _$createComponent4} from "@opentui/solid"
469
- import {effect as _$effect3} from "@opentui/solid"
470
- import {insert as _$insert3} from "@opentui/solid"
471
- import {memo as _$memo3} from "@opentui/solid"
472
- import {setProp as _$setProp3} from "@opentui/solid"
473
- import {createElement as _$createElement3} from "@opentui/solid"
474
510
  import {For} from "solid-js"
475
511
  import {effect as _$effect2} from "@opentui/solid"
476
- import {memo as _$memo2} from "@opentui/solid"
477
- import {createTextNode as _$createTextNode} from "@opentui/solid"
478
512
  import {insertNode as _$insertNode2} from "@opentui/solid"
479
513
  import {insert as _$insert2} from "@opentui/solid"
514
+ import {memo as _$memo} from "@opentui/solid"
480
515
  import {setProp as _$setProp2} from "@opentui/solid"
481
516
  import {createElement as _$createElement2} from "@opentui/solid"
482
- function QuotaBalanceRow(props){return(()=>{var _el$=_$createElement2("box"),_el$2=_$createElement2("text"),_el$3=_$createTextNode(` \xB7 `),_el$4=_$createElement2("text")
517
+ function QuotaBalanceRow(props){return(()=>{var _el$=_$createElement2("text"),_el$2=_$createElement2("span")
483
518
  _$insertNode2(_el$,_el$2)
484
- _$insertNode2(_el$,_el$4)
485
- _$insertNode2(_el$2,_el$3)
486
- _$setProp2(_el$2,"wrapMode","word")
487
- _$insert2(_el$2,()=>props.balance.label,_el$3)
488
- _$insert2(_el$2,()=>props.balance.scope,null)
489
- _$setProp2(_el$4,"wrapMode","word")
490
- _$insert2(_el$4,(()=>{var _c$=_$memo2(()=>!!props.balance.unlimited)
519
+ _$setProp2(_el$,"wrapMode","none")
520
+ _$setProp2(_el$,"truncate",true)
521
+ _$insert2(_el$2,(()=>{var _c$=_$memo(()=>!!props.balance.unlimited)
491
522
  return()=>_c$()?"No configured cap":`${props.balance.amount} ${props.balance.unit ?? "(provider unit not specified)"}`
492
523
  })())
493
- _$effect2((_p$)=>{var _v$=props.api.theme.current.textMuted,_v$2=props.api.theme.current.text
494
- _v$!==_p$.e&&(_p$.e=_$setProp2(_el$2,"fg",_v$,_p$.e))
495
- _v$2!==_p$.t&&(_p$.t=_$setProp2(_el$4,"fg",_v$2,_p$.t))
524
+ _$insert2(_el$,()=>` \xB7 ${props.balance.scope} \xB7 ${props.balance.label}`,null)
525
+ _$effect2((_p$)=>{var _v$=props.api.theme.current.textMuted,_v$2={fg:props.api.theme.current.text
526
+ }
527
+ _v$!==_p$.e&&(_p$.e=_$setProp2(_el$,"fg",_v$,_p$.e))
528
+ _v$2!==_p$.t&&(_p$.t=_$setProp2(_el$2,"style",_v$2,_p$.t))
496
529
  return _p$
497
530
  },{e:undefined,t:undefined
498
531
  })
499
532
  return _el$
500
533
  })()
501
534
  }
502
- function QuotaSnapshotRows(props){return [(()=>{var _el$=_$createElement3("text")
503
- _$setProp3(_el$,"wrapMode","none")
504
- _$setProp3(_el$,"truncate",true)
505
- _$insert3(_el$,()=>props.snapshot.providerName,null)
506
- _$insert3(_el$,(()=>{var _c$=_$memo3(()=>!!props.snapshot.accountId)
507
- return()=>_c$()?` \xB7 ${props.snapshot.accountId}`:""
508
- })(),null)
509
- _$effect3((_$p)=>_$setProp3(_el$,"fg",props.api.theme.current.textMuted,_$p))
510
- return _el$
511
- })(),_$createComponent4(For,{get each(){return props.snapshot.windows
535
+ function QuotaSnapshotRows(props){return [_$createComponent4(For,{get each(){return props.snapshot.windows
512
536
  },children:(window)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
513
537
  },window
514
538
  })
@@ -518,37 +542,6 @@ return _el$
518
542
  })
519
543
  })]
520
544
  }
521
- import {createComponent as _$createComponent5} from "@opentui/solid"
522
- import {createSignal as createSignal4,onMount} from "solid-js"
523
- import {SelectionMenu as SelectionMenu2,useDialogs as useDialogs2} from "./tui.js"
524
- var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Codex account RPC; 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"]]
525
- var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
526
- 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"
527
- }
528
- function providerQuotaCapability(id,canonical){const key=aliases[canonical??id]??canonical??id
529
- const found=PROVIDER_QUOTA_CAPABILITIES.find((entry)=>entry.id===key)
530
- 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/"]
531
- }}
532
- function providerQuotaCatalog(providers=[]){const entries=new Map(PROVIDER_QUOTA_CAPABILITIES.map((entry)=>[entry.id,entry]))
533
- for(const provider of providers)
534
- entries.set(provider.id,{...providerQuotaCapability(provider.id,provider.canonical),id:provider.id,name:provider.name
535
- })
536
- return [...entries.values()].sort((a,b)=>a.name.localeCompare(b.name))
537
- }
538
- function ProviderSupportDialog(props){const dialogs=useDialogs2(props.api)
539
- const [entries,setEntries]=createSignal4(providerQuotaCatalog())
540
- onMount(()=>{if(props.load)
541
- props.load().then(setEntries).catch(()=>{})
542
- })
543
- return _$createComponent5(SelectionMenu2,{get api(){return props.api
544
- },get title(){return `Provider sources \xB7 ${entries().length}`
545
- },height:18,get options(){return [...entries().map((entry)=>({title:entry.name,value:entry.id,icon:entry.sources.length>0?"info":"help",description:`${entry.sources.length > 0 ? entry.sources.join(", ") : "API-dependent"} \xB7 ${entry.reason}`
546
- })),{title:"Close",value:"close",description:"Return to the sidebar",icon:"close"
547
- }]
548
- },onSelect:(option)=>{if(option.value==="close")
549
- dialogs.back()
550
- }})
551
- }
552
545
  var text={type:"string",minLength:1,maxLength:512}
553
546
  var model2={type:"object",properties:{providerID:text,modelID:text,sessionID:text,connectionID:text,variant:text,force:{type:"boolean"}},required:["providerID","modelID","sessionID"],additionalProperties:false
554
547
  }
@@ -585,36 +578,42 @@ listeners.clear()
585
578
  }}
586
579
  return{adapter,onUpdated(listener){listeners.add(listener)
587
580
  return()=>listeners.delete(listener)
588
- },async capabilities(){try{return await rpc.capabilities({})
589
- } catch{return providerQuotaCatalog(context.data.location.provider?.list?.(context.location)??[])
590
- }}}}
581
+ }}}
582
+ 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"]]
583
+ var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
584
+ 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"
585
+ }
586
+ function providerQuotaCapability(id,canonical){const key=aliases[canonical??id]??canonical??id
587
+ const found=PROVIDER_QUOTA_CAPABILITIES.find((entry)=>entry.id===key)
588
+ 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/"]
589
+ }}
590
+ import {insert as _$insert8} from "@opentui/solid"
591
591
  import {mergeProps as _$mergeProps} from "@opentui/solid"
592
- import {insert as _$insert7} from "@opentui/solid"
593
- import {setProp as _$setProp8} from "@opentui/solid"
594
- import {effect as _$effect8} from "@opentui/solid"
592
+ import {setProp as _$setProp9} from "@opentui/solid"
593
+ import {effect as _$effect9} from "@opentui/solid"
595
594
  import {createTextNode as _$createTextNode4} from "@opentui/solid"
596
- import {insertNode as _$insertNode5} from "@opentui/solid"
597
- import {createElement as _$createElement8} from "@opentui/solid"
598
- import {memo as _$memo6} from "@opentui/solid"
599
- import {createComponent as _$createComponent10} from "@opentui/solid"
600
- import {Show as Show4} from "solid-js"
601
- import {useDialogs as useDialogs4,useIcons as useIcons3} from "./tui.js"
602
- import {setProp as _$setProp5} from "@opentui/solid"
603
- import {effect as _$effect5} from "@opentui/solid"
604
- import {createTextNode as _$createTextNode2} from "@opentui/solid"
605
- import {insertNode as _$insertNode3} from "@opentui/solid"
606
- import {createElement as _$createElement5} from "@opentui/solid"
607
- import {memo as _$memo4} from "@opentui/solid"
608
- import {createComponent as _$createComponent7} from "@opentui/solid"
609
- import {Show} from "solid-js"
610
- import {useDialogs as useDialogs3,useIcons} from "./tui.js"
611
- import {createComponent as _$createComponent6} from "@opentui/solid"
612
- import {effect as _$effect4} from "@opentui/solid"
613
- import {insert as _$insert4} from "@opentui/solid"
595
+ import {insertNode as _$insertNode6} from "@opentui/solid"
596
+ import {createElement as _$createElement9} from "@opentui/solid"
597
+ import {memo as _$memo7} from "@opentui/solid"
598
+ import {createComponent as _$createComponent12} from "@opentui/solid"
599
+ import {Show as Show6} from "solid-js"
600
+ import {useDialogs as useDialogs3} from "./tui.js"
614
601
  import {setProp as _$setProp4} from "@opentui/solid"
602
+ import {effect as _$effect4} from "@opentui/solid"
603
+ import {createTextNode as _$createTextNode} from "@opentui/solid"
604
+ import {insertNode as _$insertNode3} from "@opentui/solid"
615
605
  import {createElement as _$createElement4} from "@opentui/solid"
606
+ import {memo as _$memo2} from "@opentui/solid"
607
+ import {createComponent as _$createComponent6} from "@opentui/solid"
608
+ import {Show} from "solid-js"
609
+ import {useDialogs as useDialogs2,useIcons as useIcons2} from "./tui.js"
610
+ import {createComponent as _$createComponent5} from "@opentui/solid"
611
+ import {effect as _$effect3} from "@opentui/solid"
612
+ import {insert as _$insert3} from "@opentui/solid"
613
+ import {setProp as _$setProp3} from "@opentui/solid"
614
+ import {createElement as _$createElement3} from "@opentui/solid"
616
615
  import {SelectionBox} from "./tui.js"
617
- import {createSignal as createSignal5,onCleanup} from "solid-js"
616
+ import {createSignal as createSignal4,onCleanup} from "solid-js"
618
617
  function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
619
618
  let backgroundColor=theme.backgroundPanel
620
619
  let foregroundColor=normalColor
@@ -630,7 +629,7 @@ else if(highlighted)
630
629
  foregroundColor=theme.selectedListItemText
631
630
  return{backgroundColor,foregroundColor}}
632
631
  function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
633
- const [hovered,setHovered]=createSignal5(false)
632
+ const [hovered,setHovered]=createSignal4(false)
634
633
  const disabled=()=>descriptor.disabled?.()===true
635
634
  const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
636
635
  const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
@@ -647,63 +646,170 @@ descriptor.activate()
647
646
  return true
648
647
  }}}
649
648
  function LimitsAction(props){const item=useSidebarItem(props.api,props.interaction,{id:props.id,position:()=>props.position,disabled:()=>props.disabled===true,activate:props.onActivate
650
- })
651
- return _$createComponent6(SelectionBox,{ref:(node)=>item.ref(node),get id(){return props.id
652
- },height:1,get backgroundColor(){return item.backgroundColor()
649
+ },()=>props.api.theme.current.text,props.inline?"control":"row")
650
+ return _$createComponent5(SelectionBox,{ref:(node)=>item.ref(node),get id(){return props.id
651
+ },height:1,get flexGrow(){return props.flexGrow
652
+ },get maxWidth(){return props.maxWidth
653
+ },flexShrink:1,minWidth:3,get backgroundColor(){return item.backgroundColor()
653
654
  },get onMouseOver(){return item.onMouseOver
654
655
  },get onMouseOut(){return item.onMouseOut
655
656
  },onMouseUp:(event)=>{event.stopPropagation()
656
657
  item.activate(event)
657
- },get children(){var _el$=_$createElement4("text")
658
- _$setProp4(_el$,"wrapMode","none")
659
- _$setProp4(_el$,"truncate",true)
660
- _$insert4(_el$,()=>props.label)
661
- _$effect4((_$p)=>_$setProp4(_el$,"fg",item.foregroundColor(),_$p))
658
+ },get children(){var _el$=_$createElement3("text")
659
+ _$setProp3(_el$,"wrapMode","none")
660
+ _$setProp3(_el$,"truncate",true)
661
+ _$insert3(_el$,()=>props.label)
662
+ _$effect3((_$p)=>_$setProp3(_el$,"fg",item.foregroundColor(),_$p))
662
663
  return _el$
663
664
  }})
664
665
  }
665
- function CodexAccountControls(props){const icons=useIcons()
666
+ function CodexAccountControls(props){const icons=useIcons2()
666
667
  const model=()=>props.controller.current().model
667
- const accountLink=createCodexAccountLink({api:props.api,preferences:props.preferences,adapter:props.codex,model,dialogs:useDialogs3(props.api)
668
+ const accountLink=createCodexAccountLink({api:props.api,preferences:props.preferences,adapter:props.codex,model,dialogs:useDialogs2(props.api)
668
669
  })
669
670
  const eligible=()=>(props.model.providerID==="openai"||props.model.hostConnection?.providerID==="openai")&&props.model.hostConnection?.method!=="api"
670
- const action=(id,row,label,onActivate)=>_$createComponent7(LimitsAction,{get api(){return props.api
671
+ const action=(id,row,label,onActivate)=>_$createComponent6(LimitsAction,{get api(){return props.api
671
672
  },get interaction(){return props.interaction
672
673
  },id:`${PLUGIN_ID}.limits.${id}`,get position(){return{section:props.navigationSection,row,column:0
673
674
  }},label,onActivate,get disabled(){return accountLink.busy()
674
675
  }})
675
- return _$createComponent7(Show,{get when(){return eligible()
676
- },get children(){return [_$createComponent7(Show,{get when(){return _$memo4(()=>!!!props.model.accountID)()&&(!props.model.hostConnection||props.model.hostConnection.status==="ready")
677
- },get children(){return [(()=>{var _el$=_$createElement5("text")
678
- _$insertNode3(_el$,_$createTextNode2(`Link the matching Codex account once for this connection`))
679
- _$effect5((_$p)=>_$setProp5(_el$,"fg",props.api.theme.current.textMuted,_$p))
676
+ return _$createComponent6(Show,{get when(){return eligible()
677
+ },get children(){return [_$createComponent6(Show,{get when(){return _$memo2(()=>!!!props.model.accountID)()&&(!props.model.hostConnection||props.model.hostConnection.status==="ready")
678
+ },get children(){return [(()=>{var _el$=_$createElement4("text")
679
+ _$insertNode3(_el$,_$createTextNode(`Link the matching Codex account once for this connection`))
680
+ _$effect4((_$p)=>_$setProp4(_el$,"fg",props.api.theme.current.textMuted,_$p))
680
681
  return _el$
681
- })(),_$memo4(()=>action("link",1,`${icons.icon("scope")} Link Codex CLI account`,()=>void accountLink.link()))]
682
- }}),_$createComponent7(Show,{get when(){return _$memo4(()=>!!props.model.accountID)()&&props.model.accountSource!=="host"
683
- },get children(){return [_$memo4(()=>action("unlink",4,`${icons.icon("close")} Unlink Codex account`,()=>props.preferences.setCodexConnectionBinding(props.model.providerID,props.model.hostConnection?.id))),_$createComponent7(Show,{get when(){return props.controller.snapshot()?.availability==="unauthenticated"
682
+ })(),_$memo2(()=>action("link",2,`${icons.icon("scope")} Link Codex CLI account`,()=>void accountLink.link()))]
683
+ }}),_$createComponent6(Show,{get when(){return _$memo2(()=>!!(props.model.accountID&&props.model.accountSource!=="host"))()&&props.controller.snapshot()?.availability==="unauthenticated"
684
+ },get children(){return [_$memo2(()=>action("unlink",4,`${icons.icon("close")} Unlink Codex account`,()=>props.preferences.setCodexConnectionBinding(props.model.providerID,props.model.hostConnection?.id))),_$createComponent6(Show,{get when(){return props.controller.snapshot()?.availability==="unauthenticated"
684
685
  },get children(){return action("relink",5,`${icons.icon("scope")} Relink Codex account`,()=>void accountLink.link())
685
686
  }})]
686
687
  }})]
687
688
  }})
688
689
  }
689
690
  import {createComponent as _$createComponent8} from "@opentui/solid"
690
- import {effect as _$effect6} from "@opentui/solid"
691
+ import {Show as Show2} from "solid-js"
692
+ import {useIcons as useIcons3} from "./tui.js"
693
+ import {createComponent as _$createComponent7} from "@opentui/solid"
694
+ import {effect as _$effect5} from "@opentui/solid"
695
+ import {insert as _$insert4} from "@opentui/solid"
696
+ import {setProp as _$setProp5} from "@opentui/solid"
697
+ import {createElement as _$createElement5} from "@opentui/solid"
698
+ import {memo as _$memo3} from "@opentui/solid"
699
+ import {createEffect as createEffect2,createSignal as createSignal5,on} from "solid-js"
700
+ import {SelectionBox as SelectionBox2} from "./tui.js"
701
+ var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
702
+ })
703
+ function truncateEnd(value,width){if(Bun.stringWidth(value)<=width)
704
+ return value
705
+ if(width<=0)
706
+ return ""
707
+ const ellipsis=width>=3?"...":".".repeat(width)
708
+ let result=""
709
+ for(const{segment
710
+ } of graphemes.segment(value)){if(Bun.stringWidth(result + segment)>width-ellipsis.length)
711
+ break
712
+ result += segment
713
+ }
714
+ return result + ellipsis
715
+ }
716
+ function SectionHeaderControl(props){const [width,setWidth]=createSignal5()
717
+ const control=useSidebarItem(props.api,props.interaction,{id:props.action.id,position:()=>props.position,disabled:props.action.disabled,activate:props.action.onActivate
718
+ },()=>props.api.theme.current.accent,"control")
719
+ createEffect2(on(()=>[props.action.label(),props.resetWidth],()=>setWidth(undefined)))
720
+ const label=()=>width()===undefined||props.iconOnly?props.action.label():truncateEnd(props.action.label(),width()-2)
721
+ return _$createComponent7(SelectionBox2,{ref:(node)=>control.ref(node),get id(){return props.action.id
722
+ },get iconOnly(){return props.iconOnly
723
+ },get width(){return props.iconOnly?3:undefined
724
+ },get flexShrink(){return props.iconOnly?0:1
725
+ },minWidth:3,overflow:"hidden",get alignItems(){return props.iconOnly?"center":undefined
726
+ },get backgroundColor(){return control.backgroundColor()
727
+ },onSizeChange:function(){setWidth(this.width)
728
+ },get onMouseOver(){return control.onMouseOver
729
+ },get onMouseOut(){return control.onMouseOut
730
+ },onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
731
+ control.activate(event)
732
+ },get children(){var _el$=_$createElement5("text")
733
+ _$setProp5(_el$,"wrapMode","none")
734
+ _$setProp5(_el$,"height",1)
735
+ _$insert4(_el$,label)
736
+ _$effect5((_$p)=>_$setProp5(_el$,"fg",control.foregroundColor(),_$p))
737
+ return _el$
738
+ }})
739
+ }
740
+ function LimitsHeaderControls(props){const icons=useIcons3()
741
+ return [_$createComponent8(Show2,{get when(){return props.credits
742
+ },get children(){return _$createComponent8(SectionHeaderControl,{get api(){return props.api
743
+ },get interaction(){return props.interaction
744
+ },get position(){return{section:props.navigationSection,row:0,column:1
745
+ }},get action(){return{id:`${PLUGIN_ID}.limits.credits`,label:()=>"Banked Resets",disabled:()=>false,onActivate:props.onCredits
746
+ }}})
747
+ }}),_$createComponent8(SectionHeaderControl,{get api(){return props.api
748
+ },get interaction(){return props.interaction
749
+ },iconOnly:true,get position(){return{section:props.navigationSection,row:0,column:2
750
+ }},get action(){return{id:`${PLUGIN_ID}.limits.refresh`,label:()=>icons.icon(props.refreshing?"pending":"retry"),disabled:()=>props.refreshing||!props.canRefresh,onActivate:props.onRefresh
751
+ }}})]
752
+ }
691
753
  import {insert as _$insert5} from "@opentui/solid"
754
+ import {effect as _$effect6} from "@opentui/solid"
755
+ import {createTextNode as _$createTextNode2} from "@opentui/solid"
756
+ import {insertNode as _$insertNode4} from "@opentui/solid"
757
+ import {memo as _$memo4} from "@opentui/solid"
758
+ import {createComponent as _$createComponent9} from "@opentui/solid"
692
759
  import {setProp as _$setProp6} from "@opentui/solid"
693
760
  import {createElement as _$createElement6} from "@opentui/solid"
694
- import {Show as Show2} from "solid-js"
761
+ import {Show as Show3} from "solid-js"
762
+ function LimitsModelRow(props){const names=()=>props.selection?.names(props.model)??{provider:props.model.providerID,model:props.model.modelID
763
+ }
764
+ return(()=>{var _el$=_$createElement6("box")
765
+ _$setProp6(_el$,"flexDirection","row")
766
+ _$setProp6(_el$,"minWidth",0)
767
+ _$insert5(_el$,_$createComponent9(LimitsAction,{get api(){return props.api
768
+ },get interaction(){return props.interaction
769
+ },inline:true,flexGrow:1,get maxWidth(){return Bun.stringWidth(`[${names().provider}] ${names().model}`) + 2
770
+ },id:`${PLUGIN_ID}.limits.model`,get position(){return{section:props.navigationSection,row:1,column:0
771
+ }},get label(){return `[${names().provider}] ${names().model}`
772
+ },get disabled(){return !props.selection?.canOpen("model",props.model)
773
+ },onActivate:()=>props.selection?.open("model",props.model)
774
+ }),null)
775
+ _$insert5(_el$,_$createComponent9(Show3,{get when(){return props.model.variant||props.selection?.variants(props.model).length
776
+ },get children(){return [(()=>{var _el$2=_$createElement6("text"),_el$3=_$createTextNode2(` \xB7 `)
777
+ _$insertNode4(_el$2,_el$3)
778
+ _$setProp6(_el$2,"flexShrink",0)
779
+ _$effect6((_$p)=>_$setProp6(_el$2,"fg",props.api.theme.current.textMuted,_$p))
780
+ return _el$2
781
+ })(),_$createComponent9(LimitsAction,{get api(){return props.api
782
+ },get interaction(){return props.interaction
783
+ },inline:true,id:`${PLUGIN_ID}.limits.variant`,get position(){return{section:props.navigationSection,row:1,column:1
784
+ }},get label(){return props.model.variant??"Default"
785
+ },get disabled(){return !props.selection?.canOpen("variant",props.model)
786
+ },onActivate:()=>props.selection?.open("variant",props.model)
787
+ })]
788
+ }}),null)
789
+ return _el$
790
+ })()
791
+ }
792
+ import {effect as _$effect7} from "@opentui/solid"
793
+ import {insert as _$insert6} from "@opentui/solid"
794
+ import {setProp as _$setProp7} from "@opentui/solid"
795
+ import {createElement as _$createElement7} from "@opentui/solid"
796
+ import {createComponent as _$createComponent10} from "@opentui/solid"
797
+ import {memo as _$memo5} from "@opentui/solid"
798
+ import {Show as Show4} from "solid-js"
695
799
  function LimitsStatus(props){const message=()=>{const model=props.controller.current().model
696
800
  const connection=model?.hostConnection
697
801
  const snapshot=props.controller.snapshot()
698
802
  const state=props.controller.state()
699
803
  if(state.status==="error")
700
- return snapshot?"Stale \xB7 refresh failed; retry below":"Quota read failed \xB7 retry below"
804
+ return snapshot?"Stale \xB7 refresh failed":"Quota read failed \xB7 refresh to retry"
805
+ if(snapshot?.availability==="ready"&&(snapshot.windows.length>0||snapshot.balances?.length))
806
+ return
701
807
  if(snapshot?.message)
702
808
  return snapshot.availability==="stale"?`Stale \xB7 ${snapshot.message}`:snapshot.availability==="ready"?`Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()} \xB7 ${snapshot.message}`:snapshot.message
703
809
  if(connection?.status==="loading")
704
810
  return "Checking the OpenCode account\u2026"
705
811
  if(connection?.status==="unavailable")
706
- return "OpenCode account metadata unavailable \xB7 refresh below"
812
+ return "Account metadata unavailable \xB7 refresh to retry"
707
813
  if(connection?.status==="unsupported")
708
814
  return "Codex quotas require an active ChatGPT OAuth connection"
709
815
  if(!snapshot)
@@ -716,194 +822,146 @@ return "No documented quota bucket for this model"
716
822
  if(availability==="rate_limited")
717
823
  return "Provider rate-limited this read \xB7 try again later"
718
824
  if(availability==="stale")
719
- return "Stale provider data \xB7 refresh below"
825
+ return "Stale provider data \xB7 refresh to update"
720
826
  return `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()}`
721
827
  }
722
- return _$createComponent8(Show2,{get when(){return props.controller.current().model
723
- },get children(){var _el$=_$createElement6("text")
724
- _$setProp6(_el$,"wrapMode","word")
725
- _$insert5(_el$,message)
726
- _$effect6((_$p)=>_$setProp6(_el$,"fg",props.api.theme.current.textMuted,_$p))
828
+ return _$createComponent10(Show4,{get when(){return _$memo5(()=>!!props.controller.current().model)()&&message()
829
+ },children:(value)=>(()=>{var _el$=_$createElement7("text")
830
+ _$setProp7(_el$,"wrapMode","word")
831
+ _$insert6(_el$,value)
832
+ _$effect7((_$p)=>_$setProp7(_el$,"fg",props.api.theme.current.textMuted,_$p))
727
833
  return _el$
728
- }})
729
- }
730
- import {createComponent as _$createComponent9} from "@opentui/solid"
731
- import {effect as _$effect7} from "@opentui/solid"
732
- import {createTextNode as _$createTextNode3} from "@opentui/solid"
733
- import {insertNode as _$insertNode4} from "@opentui/solid"
734
- import {memo as _$memo5} from "@opentui/solid"
735
- import {insert as _$insert6} from "@opentui/solid"
736
- import {setProp as _$setProp7} from "@opentui/solid"
737
- import {createElement as _$createElement7} from "@opentui/solid"
738
- import {Show as Show3,createEffect as createEffect2,createSignal as createSignal6} from "solid-js"
739
- import {SelectionBox as SelectionBox2,useIcons as useIcons2} from "./tui.js"
740
- var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
834
+ })()
741
835
  })
742
- function truncateEnd(value,maxWidth){if(Bun.stringWidth(value)<=maxWidth)
743
- return value
744
- if(maxWidth<=0)
745
- return ""
746
- const ellipsis=maxWidth>=3?"...":".".repeat(maxWidth)
747
- const contentWidth=maxWidth-Bun.stringWidth(ellipsis)
748
- let result=""
749
- for(const{segment
750
- } of graphemes.segment(value)){if(Bun.stringWidth(result + segment)>contentWidth)
751
- break
752
- result += segment
753
- }
754
- return result + ellipsis
755
836
  }
756
- function Section(props){const icons=useIcons2()
757
- const [actionWidth,setActionWidth]=createSignal6()
837
+ import {createComponent as _$createComponent11} from "@opentui/solid"
838
+ import {effect as _$effect8} from "@opentui/solid"
839
+ import {createTextNode as _$createTextNode3} from "@opentui/solid"
840
+ import {insertNode as _$insertNode5} from "@opentui/solid"
841
+ import {memo as _$memo6} from "@opentui/solid"
842
+ import {insert as _$insert7} from "@opentui/solid"
843
+ import {setProp as _$setProp8} from "@opentui/solid"
844
+ import {createElement as _$createElement8} from "@opentui/solid"
845
+ import {Show as Show5,createSignal as createSignal6} from "solid-js"
846
+ import {SelectionBox as SelectionBox3,useIcons as useIcons4} from "./tui.js"
847
+ function Section(props){const icons=useIcons4()
848
+ const [headerWidth,setHeaderWidth]=createSignal6(0)
758
849
  const icon=()=>props.section?icons.section(props.section):props.icon
759
850
  const theme=()=>props.api.theme.current
760
851
  const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
761
852
  }),activate:props.onToggle
762
853
  })
763
- const action=props.headerAction?useSidebarItem(props.api,props.interaction,{id:props.headerAction.id,position:()=>({section:props.navigationSection??0,row:0,column:1
764
- }),disabled:props.headerAction.disabled,activate:props.headerAction.onActivate
765
- },()=>theme().accent,"control"):undefined
766
- createEffect2(()=>{props.headerAction?.label()
767
- setActionWidth(undefined)
768
- })
769
- const actionLabel=()=>{const label=props.headerAction?.label()??""
770
- const width=actionWidth()
771
- return width===undefined?label:truncateEnd(label,width-2)
772
- }
773
- return(()=>{var _el$=_$createElement7("box")
774
- _$setProp7(_el$,"gap",1)
775
- _$insert6(_el$,_$createComponent9(SelectionBox2,{ref:(node)=>item.ref(node),get id(){return props.sectionId
854
+ return(()=>{var _el$=_$createElement8("box")
855
+ _$setProp8(_el$,"gap",1)
856
+ _$insert7(_el$,_$createComponent11(SelectionBox3,{ref:(node)=>item.ref(node),get id(){return props.sectionId
776
857
  },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return item.backgroundColor()
777
858
  },get onMouseOver(){return item.onMouseOver
778
859
  },get onMouseOut(){return item.onMouseOut
779
- },onMouseDown:(event)=>item.activate(event),onSizeChange:()=>setActionWidth(undefined),get children(){return [(()=>{var _el$2=_$createElement7("text"),_el$3=_$createElement7("span"),_el$4=_$createTextNode3(` `),_el$5=_$createElement7("span"),_el$6=_$createElement7("b")
780
- _$insertNode4(_el$2,_el$3)
781
- _$insertNode4(_el$2,_el$4)
782
- _$insertNode4(_el$2,_el$5)
783
- _$insertNode4(_el$2,_el$6)
784
- _$setProp7(_el$2,"flexShrink",0)
785
- _$setProp7(_el$2,"wrapMode","none")
786
- _$insert6(_el$3,()=>icons.icon(props.open?"expanded":"collapsed"))
787
- _$insert6(_el$5,(()=>{var _c$=_$memo5(()=>!!icon())
860
+ },onMouseDown:(event)=>item.activate(event),onSizeChange:function(){setHeaderWidth(this.width)
861
+ },get children(){return [(()=>{var _el$2=_$createElement8("text"),_el$3=_$createElement8("span"),_el$4=_$createTextNode3(` `),_el$5=_$createElement8("span"),_el$6=_$createElement8("b")
862
+ _$insertNode5(_el$2,_el$3)
863
+ _$insertNode5(_el$2,_el$4)
864
+ _$insertNode5(_el$2,_el$5)
865
+ _$insertNode5(_el$2,_el$6)
866
+ _$setProp8(_el$2,"flexShrink",0)
867
+ _$setProp8(_el$2,"wrapMode","none")
868
+ _$insert7(_el$3,()=>icons.icon(props.open?"expanded":"collapsed"))
869
+ _$insert7(_el$5,(()=>{var _c$=_$memo6(()=>!!icon())
788
870
  return()=>_c$()?`${icon()} `:""
789
871
  })())
790
- _$insert6(_el$6,()=>props.title)
791
- _$effect7((_p$)=>{var _v$=item.foregroundColor(),_v$2={fg:item.focused()?item.foregroundColor():theme().accent
872
+ _$insert7(_el$6,()=>props.title)
873
+ _$effect8((_p$)=>{var _v$=item.foregroundColor(),_v$2={fg:item.focused()?item.foregroundColor():theme().accent
792
874
  },_v$3={fg:item.focused()?item.foregroundColor():theme().accent
793
875
  }
794
- _v$!==_p$.e&&(_p$.e=_$setProp7(_el$2,"fg",_v$,_p$.e))
795
- _v$2!==_p$.t&&(_p$.t=_$setProp7(_el$3,"style",_v$2,_p$.t))
796
- _v$3!==_p$.a&&(_p$.a=_$setProp7(_el$5,"style",_v$3,_p$.a))
876
+ _v$!==_p$.e&&(_p$.e=_$setProp8(_el$2,"fg",_v$,_p$.e))
877
+ _v$2!==_p$.t&&(_p$.t=_$setProp8(_el$3,"style",_v$2,_p$.t))
878
+ _v$3!==_p$.a&&(_p$.a=_$setProp8(_el$5,"style",_v$3,_p$.a))
797
879
  return _p$
798
880
  },{e:undefined,t:undefined,a:undefined
799
881
  })
800
882
  return _el$2
801
- })(),(()=>{var _el$7=_$createElement7("box"),_el$8=_$createElement7("text")
802
- _$insertNode4(_el$7,_el$8)
803
- _$setProp7(_el$7,"flexDirection","row")
804
- _$setProp7(_el$7,"gap",1)
805
- _$setProp7(_el$7,"flexGrow",1)
806
- _$setProp7(_el$7,"minWidth",0)
807
- _$setProp7(_el$7,"justifyContent","flex-end")
808
- _$insert6(_el$7,_$createComponent9(Show3,{when:action,children:(control)=>_$createComponent9(SelectionBox2,{ref:(node)=>control().ref(node),get id(){return props.headerAction?.id
809
- },flexShrink:1,minWidth:3,overflow:"hidden",get backgroundColor(){return control().backgroundColor()
810
- },onSizeChange:function(){setActionWidth(this.width)
811
- },get onMouseOver(){return control().onMouseOver
812
- },get onMouseOut(){return control().onMouseOut
813
- },onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
814
- control().activate(event)
815
- },get children(){var _el$9=_$createElement7("text")
816
- _$setProp7(_el$9,"wrapMode","none")
817
- _$setProp7(_el$9,"height",1)
818
- _$insert6(_el$9,actionLabel)
819
- _$effect7((_$p)=>_$setProp7(_el$9,"fg",control().foregroundColor(),_$p))
820
- return _el$9
821
- }})
822
- }),_el$8)
823
- _$setProp7(_el$8,"wrapMode","none")
824
- _$setProp7(_el$8,"truncate",true)
825
- _$setProp7(_el$8,"height",1)
826
- _$insert6(_el$8,()=>props.summary)
827
- _$effect7((_p$)=>{var _v$4=item.focused()?item.foregroundColor():theme().textMuted,_v$5=props.headerAction?0:1
828
- _v$4!==_p$.e&&(_p$.e=_$setProp7(_el$8,"fg",_v$4,_p$.e))
829
- _v$5!==_p$.t&&(_p$.t=_$setProp7(_el$8,"flexShrink",_v$5,_p$.t))
883
+ })(),(()=>{var _el$7=_$createElement8("box")
884
+ _$setProp8(_el$7,"flexDirection","row")
885
+ _$setProp8(_el$7,"gap",1)
886
+ _$setProp8(_el$7,"flexGrow",1)
887
+ _$setProp8(_el$7,"minWidth",0)
888
+ _$setProp8(_el$7,"justifyContent","flex-end")
889
+ _$insert7(_el$7,()=>props.headerControls,null)
890
+ _$insert7(_el$7,_$createComponent11(Show5,{get when(){return props.headerAction
891
+ },children:(action)=>_$createComponent11(SectionHeaderControl,{get api(){return props.api
892
+ },get interaction(){return props.interaction
893
+ },get action(){return action()
894
+ },get resetWidth(){return headerWidth()
895
+ },get position(){return{section:props.navigationSection??0,row:0,column:1
896
+ }}})
897
+ }),null)
898
+ _$insert7(_el$7,_$createComponent11(Show5,{get when(){return props.summary
899
+ },get children(){var _el$8=_$createElement8("text")
900
+ _$setProp8(_el$8,"wrapMode","none")
901
+ _$setProp8(_el$8,"truncate",true)
902
+ _$setProp8(_el$8,"height",1)
903
+ _$insert7(_el$8,()=>props.summary)
904
+ _$effect8((_p$)=>{var _v$4=item.focused()?item.foregroundColor():theme().textMuted,_v$5=props.headerAction?0:1
905
+ _v$4!==_p$.e&&(_p$.e=_$setProp8(_el$8,"fg",_v$4,_p$.e))
906
+ _v$5!==_p$.t&&(_p$.t=_$setProp8(_el$8,"flexShrink",_v$5,_p$.t))
830
907
  return _p$
831
908
  },{e:undefined,t:undefined
832
909
  })
910
+ return _el$8
911
+ }}),null)
833
912
  return _el$7
834
913
  })()]
835
914
  }}),null)
836
- _$insert6(_el$,_$createComponent9(Show3,{get when(){return props.open
915
+ _$insert7(_el$,_$createComponent11(Show5,{get when(){return props.open
837
916
  },get children(){return props.children
838
917
  }}),null)
839
918
  return _el$
840
919
  })()
841
920
  }
842
- function ProviderLimitsSection(props){const icons=useIcons3()
843
- const dialogs=useDialogs4(props.api)
921
+ function ProviderLimitsSection(props){const dialogs=useDialogs3(props.api)
844
922
  const model=()=>props.controller.current().model
845
923
  const snapshot=props.controller.snapshot
846
- const count=()=>(snapshot()?.windows.length??0) + (snapshot()?.balances?.length??0)
847
- const action=(id,row,label,onActivate,disabled=false)=>_$createComponent10(LimitsAction,{get api(){return props.api
848
- },get interaction(){return props.interaction
849
- },id:`${PLUGIN_ID}.limits.${id}`,get position(){return{section:props.navigationSection,row,column:0
850
- }},label,onActivate,disabled
851
- })
852
924
  function openCredits(){const selected=model()
853
925
  if(selected)
854
- dialogs.open(()=>_$createComponent10(ResetCreditDialog,{get api(){return props.api
926
+ dialogs.open(()=>_$createComponent12(ResetCreditDialog,{get api(){return props.api
855
927
  },get preferences(){return props.preferences
856
928
  },get controller(){return props.controller
857
929
  },get adapter(){return props.codex
858
930
  },model:selected
859
931
  }))
860
932
  }
861
- return _$createComponent10(Section,{get api(){return props.api
933
+ return _$createComponent12(Section,{get api(){return props.api
862
934
  },get interaction(){return props.interaction
863
935
  },sectionId:`${PLUGIN_ID}.section.limits`,get navigationSection(){return props.navigationSection
864
- },title:"LIMITS",section:"limits",get summary(){return _$memo6(()=>!!count())()?String(count()):""
936
+ },title:"LIMITS",section:"limits",summary:"",get headerControls(){return _$createComponent12(LimitsHeaderControls,{get api(){return props.api
937
+ },get interaction(){return props.interaction
938
+ },get navigationSection(){return props.navigationSection
939
+ },get credits(){return(snapshot()?.bankedResets?.availableCount??0)>0||Boolean(props.preferences.pendingResetAttempt())
940
+ },get refreshing(){return ["loading","refreshing"].includes(props.controller.state().status)
941
+ },get canRefresh(){return Boolean(model()&&(props.controller.current().adapter||model()?.hostConnection?.status==="unavailable"))
942
+ },onCredits:openCredits,onRefresh:()=>void props.controller.refresh(true)
943
+ })
865
944
  },get open(){return props.preferences.expanded().limits
866
- },onToggle:()=>props.preferences.toggleSectionExpanded("limits"),get children(){return _$createComponent10(Show4,{get when(){return model()
867
- },get fallback(){return(()=>{var _el$=_$createElement8("text")
868
- _$insertNode5(_el$,_$createTextNode4(`Open a session to view model limits`))
869
- _$effect8((_$p)=>_$setProp8(_el$,"fg",props.api.theme.current.textMuted,_$p))
945
+ },onToggle:()=>props.preferences.toggleSectionExpanded("limits"),get children(){return _$createComponent12(Show6,{get when(){return model()
946
+ },get fallback(){return(()=>{var _el$=_$createElement9("text")
947
+ _$insertNode6(_el$,_$createTextNode4(`Open a session to view model limits`))
948
+ _$effect9((_$p)=>_$setProp9(_el$,"fg",props.api.theme.current.textMuted,_$p))
870
949
  return _el$
871
950
  })()
872
- },children:(selected)=>(()=>{var _el$3=_$createElement8("box"),_el$4=_$createElement8("text"),_el$5=_$createTextNode4(`/`)
873
- _$insertNode5(_el$3,_el$4)
874
- _$setProp8(_el$3,"gap",1)
875
- _$insertNode5(_el$4,_el$5)
876
- _$setProp8(_el$4,"wrapMode","none")
877
- _$setProp8(_el$4,"truncate",true)
878
- _$insert7(_el$4,()=>selected().providerID,_el$5)
879
- _$insert7(_el$4,()=>selected().modelID,null)
880
- _$insert7(_el$3,_$createComponent10(CodexAccountControls,_$mergeProps(props,{get model(){return selected()
951
+ },children:(selected)=>(()=>{var _el$3=_$createElement9("box")
952
+ _$insert8(_el$3,_$createComponent12(LimitsModelRow,_$mergeProps(props,{get model(){return selected()
953
+ },get selection(){return props.modelSelection
954
+ }})),null)
955
+ _$insert8(_el$3,_$createComponent12(CodexAccountControls,_$mergeProps(props,{get model(){return selected()
881
956
  }})),null)
882
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return _$memo6(()=>!!(snapshot()&&["ready","stale"].includes(snapshot().availability)))()?snapshot():undefined
883
- },children:(quota)=>_$createComponent10(QuotaSnapshotRows,{get api(){return props.api
957
+ _$insert8(_el$3,_$createComponent12(Show6,{get when(){return _$memo7(()=>!!(snapshot()&&["ready","stale"].includes(snapshot().availability)))()?snapshot():undefined
958
+ },children:(quota)=>_$createComponent12(QuotaSnapshotRows,{get api(){return props.api
884
959
  },get snapshot(){return quota()
885
960
  }})
886
961
  }),null)
887
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return snapshot()?.bankedResets
888
- },children:(credits)=>[(()=>{var _el$6=_$createElement8("text"),_el$7=_$createTextNode4(`Banked resets: `)
889
- _$insertNode5(_el$6,_el$7)
890
- _$insert7(_el$6,()=>credits().availableCount,null)
891
- _$effect8((_$p)=>_$setProp8(_el$6,"fg",props.api.theme.current.textMuted,_$p))
892
- return _el$6
893
- })(),_$createComponent10(Show4,{get when(){return credits().availableCount>0||props.preferences.pendingResetAttempt()
894
- },get children(){return action("credits",2,`${icons.icon("info")} Review reset credits`,openCredits)
895
- }})]
896
- }),null)
897
- _$insert7(_el$3,_$createComponent10(LimitsStatus,{get api(){return props.api
962
+ _$insert8(_el$3,_$createComponent12(LimitsStatus,{get api(){return props.api
898
963
  },get controller(){return props.controller
899
964
  }}),null)
900
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return props.controller.current().adapter||selected().hostConnection?.status==="unavailable"
901
- },get children(){return action("refresh",3,`${icons.icon("retry")} ${props.controller.state().status === "error" ? "Retry" : "Refresh"}`,()=>void props.controller.refresh(true),["loading","refreshing"].includes(props.controller.state().status))
902
- }}),null)
903
- _$insert7(_el$3,()=>action("sources",6,`${icons.icon("info")} Provider sources`,()=>dialogs.open(()=>_$createComponent10(ProviderSupportDialog,{get api(){return props.api
904
- },get load(){return props.capabilities
905
- }}))),null)
906
- _$effect8((_$p)=>_$setProp8(_el$4,"fg",props.api.theme.current.textMuted,_$p))
907
965
  return _el$3
908
966
  })()
909
967
  })
@@ -1002,6 +1060,26 @@ return{...model,accountID:undefined,accountSource:undefined}
1002
1060
  const accountID=connection?.accountID??preferences.codexAccountForConnection(model.providerID,connection?.id,model.modelID)
1003
1061
  const accountSource=connection?.accountID?"host":"binding"
1004
1062
  return{...model,accountID,accountSource:accountID?accountSource:undefined}}
1063
+ var commandName=(kind)=>kind==="model"?"model.list":"variant.list"
1064
+ function createLimitsModelSelection(api,current,context){const sameTarget=(model)=>{const active=current()
1065
+ return active?.sessionID===model.sessionID&&active.providerID===model.providerID&&active.modelID===model.modelID
1066
+ }
1067
+ const legacy=(model)=>api.state.provider?.find((provider)=>provider.id===model.providerID)
1068
+ const variants=(model)=>context?context.ui.model.variant?.list?.()??[]:Object.keys(legacy(model)?.models[model.modelID]?.variants??{})
1069
+ const canOpen=(kind,model)=>{const command=api.keymap.getCommands().find((entry)=>entry.name===commandName(kind))
1070
+ const enabled=typeof command?.enabled==="function"?command.enabled():command?.enabled!==false
1071
+ return Boolean(command&&enabled&&sameTarget(model)&&(kind==="model"||variants(model).length>0))
1072
+ }
1073
+ return{names(model){if(context){const location=context.location??context.data.location.default()
1074
+ const provider=context.data.location.provider?.list?.(location)?.find((entry)=>entry.id===model.providerID)
1075
+ const item=context.data.location.model?.list?.(location)?.find((entry)=>entry.providerID===model.providerID&&entry.id===model.modelID)
1076
+ return{provider:provider?.name??model.providerID,model:item?.name??model.modelID}}
1077
+ const provider=legacy(model)
1078
+ return{provider:provider?.name??model.providerID,model:provider?.models[model.modelID]?.name??model.modelID
1079
+ }},variants,canOpen,open(kind,model){if(!canOpen(kind,model))
1080
+ return false
1081
+ return api.keymap.dispatchCommand(commandName(kind)).ok
1082
+ }}}
1005
1083
  function selectedV2Model(context){const route=context.ui.router.current()
1006
1084
  const model=context.ui.model.current()
1007
1085
  if(route.type!=="session"||!model)
@@ -1021,7 +1099,7 @@ native?.onUpdated((providerID,modelID)=>{const selected=selectModel()
1021
1099
  if(selected?.providerID===providerID&&selected.modelID===modelID)
1022
1100
  controller.refresh()
1023
1101
  })
1024
- return{controller,codex,accountSource,selectModel,capabilities:native?.capabilities??(async ()=>providerQuotaCatalog())
1102
+ return{controller,codex,accountSource,selectModel,modelSelection:createLimitsModelSelection(api,selectModel,context)
1025
1103
  }}
1026
1104
  export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
1027
1105
  }