opencode-navigator 0.21.1 → 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.
@@ -362,36 +362,63 @@ return{link,busy
362
362
  import {effect as _$effect} from "@opentui/solid"
363
363
  import {insertNode as _$insertNode} from "@opentui/solid"
364
364
  import {insert as _$insert} from "@opentui/solid"
365
- import {memo as _$memo} from "@opentui/solid"
366
365
  import {setProp as _$setProp} from "@opentui/solid"
367
366
  import {createElement as _$createElement} from "@opentui/solid"
368
- function QuotaWindowRow(props){const kind=()=>props.window.kind==="rate_limit"?"API \xB7 ":props.window.kind==="configured_limit"?"Limit \xB7 ":""
369
- const measurement=()=>{if(props.window.used!==undefined)
370
- return `${props.window.used}${props.window.total === undefined ? "" : `/${props.window.total}`}${props.window.unit === "%" ? "" : " "}${props.window.unit} used`
371
- if(props.window.remaining!==undefined)
372
- return `${props.window.remaining} ${props.window.unit} remaining`
373
- 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`
381
+ }
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}`
374
389
  }
375
- return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createElement("text"),_el$4=_$createElement("text")
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")
376
408
  _$insertNode(_el$,_el$2)
377
- _$insertNode(_el$,_el$3)
378
409
  _$insertNode(_el$,_el$4)
410
+ _$insertNode(_el$2,_el$3)
379
411
  _$setProp(_el$2,"wrapMode","none")
380
412
  _$setProp(_el$2,"truncate",true)
381
- _$insert(_el$2,kind,null)
382
- _$insert(_el$2,()=>props.window.label,null)
383
- _$insert(_el$2,(()=>{var _c$=_$memo(()=>props.window.durationMinutes===undefined)
384
- return()=>_c$()?"":` \xB7 ${props.window.durationMinutes} min`
385
- })(),null)
386
- _$setProp(_el$3,"wrapMode","word")
387
- _$insert(_el$3,measurement)
388
- _$setProp(_el$4,"wrapMode","word")
389
- _$insert(_el$4,(()=>{var _c$2=_$memo(()=>props.window.resetsAt===undefined)
390
- return()=>_c$2()?"Reset time unavailable":`resets ${new Date(props.window.resetsAt * 1000).toLocaleString()}`
391
- })())
392
- _$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
393
420
  _v$!==_p$.e&&(_p$.e=_$setProp(_el$2,"fg",_v$,_p$.e))
394
- _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))
395
422
  _v$3!==_p$.a&&(_p$.a=_$setProp(_el$4,"fg",_v$3,_p$.a))
396
423
  return _p$
397
424
  },{e:undefined,t:undefined,a:undefined
@@ -480,49 +507,32 @@ actions.review(option.value.slice("credit:".length)||undefined)
480
507
  }})
481
508
  }
482
509
  import {createComponent as _$createComponent4} from "@opentui/solid"
483
- import {effect as _$effect3} from "@opentui/solid"
484
- import {insert as _$insert3} from "@opentui/solid"
485
- import {memo as _$memo3} from "@opentui/solid"
486
- import {setProp as _$setProp3} from "@opentui/solid"
487
- import {createElement as _$createElement3} from "@opentui/solid"
488
510
  import {For} from "solid-js"
489
511
  import {effect as _$effect2} from "@opentui/solid"
490
- import {memo as _$memo2} from "@opentui/solid"
491
- import {createTextNode as _$createTextNode} from "@opentui/solid"
492
512
  import {insertNode as _$insertNode2} from "@opentui/solid"
493
513
  import {insert as _$insert2} from "@opentui/solid"
514
+ import {memo as _$memo} from "@opentui/solid"
494
515
  import {setProp as _$setProp2} from "@opentui/solid"
495
516
  import {createElement as _$createElement2} from "@opentui/solid"
496
- 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")
497
518
  _$insertNode2(_el$,_el$2)
498
- _$insertNode2(_el$,_el$4)
499
- _$insertNode2(_el$2,_el$3)
500
- _$setProp2(_el$2,"wrapMode","word")
501
- _$insert2(_el$2,()=>props.balance.label,_el$3)
502
- _$insert2(_el$2,()=>props.balance.scope,null)
503
- _$setProp2(_el$4,"wrapMode","word")
504
- _$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)
505
522
  return()=>_c$()?"No configured cap":`${props.balance.amount} ${props.balance.unit ?? "(provider unit not specified)"}`
506
523
  })())
507
- _$effect2((_p$)=>{var _v$=props.api.theme.current.textMuted,_v$2=props.api.theme.current.text
508
- _v$!==_p$.e&&(_p$.e=_$setProp2(_el$2,"fg",_v$,_p$.e))
509
- _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))
510
529
  return _p$
511
530
  },{e:undefined,t:undefined
512
531
  })
513
532
  return _el$
514
533
  })()
515
534
  }
516
- function QuotaSnapshotRows(props){return [(()=>{var _el$=_$createElement3("text")
517
- _$setProp3(_el$,"wrapMode","none")
518
- _$setProp3(_el$,"truncate",true)
519
- _$insert3(_el$,()=>props.snapshot.providerName,null)
520
- _$insert3(_el$,(()=>{var _c$=_$memo3(()=>!!props.snapshot.accountId)
521
- return()=>_c$()?` \xB7 ${props.snapshot.accountId}`:""
522
- })(),null)
523
- _$effect3((_$p)=>_$setProp3(_el$,"fg",props.api.theme.current.textMuted,_$p))
524
- return _el$
525
- })(),_$createComponent4(For,{get each(){return props.snapshot.windows
535
+ function QuotaSnapshotRows(props){return [_$createComponent4(For,{get each(){return props.snapshot.windows
526
536
  },children:(window)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
527
537
  },window
528
538
  })
@@ -532,37 +542,6 @@ return _el$
532
542
  })
533
543
  })]
534
544
  }
535
- import {createComponent as _$createComponent5} from "@opentui/solid"
536
- import {createSignal as createSignal4,onMount} from "solid-js"
537
- import {SelectionMenu as SelectionMenu2,useDialogs as useDialogs2} from "./tui.js"
538
- 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"]]
539
- var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
540
- 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"
541
- }
542
- function providerQuotaCapability(id,canonical){const key=aliases[canonical??id]??canonical??id
543
- const found=PROVIDER_QUOTA_CAPABILITIES.find((entry)=>entry.id===key)
544
- 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/"]
545
- }}
546
- function providerQuotaCatalog(providers=[]){const entries=new Map(PROVIDER_QUOTA_CAPABILITIES.map((entry)=>[entry.id,entry]))
547
- for(const provider of providers)
548
- entries.set(provider.id,{...providerQuotaCapability(provider.id,provider.canonical),id:provider.id,name:provider.name
549
- })
550
- return [...entries.values()].sort((a,b)=>a.name.localeCompare(b.name))
551
- }
552
- function ProviderSupportDialog(props){const dialogs=useDialogs2(props.api)
553
- const [entries,setEntries]=createSignal4(providerQuotaCatalog())
554
- onMount(()=>{if(props.load)
555
- props.load().then(setEntries).catch(()=>{})
556
- })
557
- return _$createComponent5(SelectionMenu2,{get api(){return props.api
558
- },get title(){return `Provider sources \xB7 ${entries().length}`
559
- },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}`
560
- })),{title:"Close",value:"close",description:"Return to the sidebar",icon:"close"
561
- }]
562
- },onSelect:(option)=>{if(option.value==="close")
563
- dialogs.back()
564
- }})
565
- }
566
545
  var text={type:"string",minLength:1,maxLength:512}
567
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
568
547
  }
@@ -599,36 +578,42 @@ listeners.clear()
599
578
  }}
600
579
  return{adapter,onUpdated(listener){listeners.add(listener)
601
580
  return()=>listeners.delete(listener)
602
- },async capabilities(){try{return await rpc.capabilities({})
603
- } catch{return providerQuotaCatalog(context.data.location.provider?.list?.(context.location)??[])
604
- }}}}
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"
605
591
  import {mergeProps as _$mergeProps} from "@opentui/solid"
606
- import {insert as _$insert7} from "@opentui/solid"
607
- import {setProp as _$setProp8} from "@opentui/solid"
608
- import {effect as _$effect8} from "@opentui/solid"
592
+ import {setProp as _$setProp9} from "@opentui/solid"
593
+ import {effect as _$effect9} from "@opentui/solid"
609
594
  import {createTextNode as _$createTextNode4} from "@opentui/solid"
610
- import {insertNode as _$insertNode5} from "@opentui/solid"
611
- import {createElement as _$createElement8} from "@opentui/solid"
612
- import {memo as _$memo6} from "@opentui/solid"
613
- import {createComponent as _$createComponent10} from "@opentui/solid"
614
- import {Show as Show4} from "solid-js"
615
- import {useDialogs as useDialogs4,useIcons as useIcons3} from "./tui.js"
616
- import {setProp as _$setProp5} from "@opentui/solid"
617
- import {effect as _$effect5} from "@opentui/solid"
618
- import {createTextNode as _$createTextNode2} from "@opentui/solid"
619
- import {insertNode as _$insertNode3} from "@opentui/solid"
620
- import {createElement as _$createElement5} from "@opentui/solid"
621
- import {memo as _$memo4} from "@opentui/solid"
622
- import {createComponent as _$createComponent7} from "@opentui/solid"
623
- import {Show} from "solid-js"
624
- import {useDialogs as useDialogs3,useIcons} from "./tui.js"
625
- import {createComponent as _$createComponent6} from "@opentui/solid"
626
- import {effect as _$effect4} from "@opentui/solid"
627
- 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"
628
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"
629
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"
630
615
  import {SelectionBox} from "./tui.js"
631
- import {createSignal as createSignal5,onCleanup} from "solid-js"
616
+ import {createSignal as createSignal4,onCleanup} from "solid-js"
632
617
  function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
633
618
  let backgroundColor=theme.backgroundPanel
634
619
  let foregroundColor=normalColor
@@ -644,7 +629,7 @@ else if(highlighted)
644
629
  foregroundColor=theme.selectedListItemText
645
630
  return{backgroundColor,foregroundColor}}
646
631
  function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
647
- const [hovered,setHovered]=createSignal5(false)
632
+ const [hovered,setHovered]=createSignal4(false)
648
633
  const disabled=()=>descriptor.disabled?.()===true
649
634
  const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
650
635
  const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
@@ -661,63 +646,170 @@ descriptor.activate()
661
646
  return true
662
647
  }}}
663
648
  function LimitsAction(props){const item=useSidebarItem(props.api,props.interaction,{id:props.id,position:()=>props.position,disabled:()=>props.disabled===true,activate:props.onActivate
664
- })
665
- return _$createComponent6(SelectionBox,{ref:(node)=>item.ref(node),get id(){return props.id
666
- },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()
667
654
  },get onMouseOver(){return item.onMouseOver
668
655
  },get onMouseOut(){return item.onMouseOut
669
656
  },onMouseUp:(event)=>{event.stopPropagation()
670
657
  item.activate(event)
671
- },get children(){var _el$=_$createElement4("text")
672
- _$setProp4(_el$,"wrapMode","none")
673
- _$setProp4(_el$,"truncate",true)
674
- _$insert4(_el$,()=>props.label)
675
- _$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))
676
663
  return _el$
677
664
  }})
678
665
  }
679
- function CodexAccountControls(props){const icons=useIcons()
666
+ function CodexAccountControls(props){const icons=useIcons2()
680
667
  const model=()=>props.controller.current().model
681
- 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)
682
669
  })
683
670
  const eligible=()=>(props.model.providerID==="openai"||props.model.hostConnection?.providerID==="openai")&&props.model.hostConnection?.method!=="api"
684
- 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
685
672
  },get interaction(){return props.interaction
686
673
  },id:`${PLUGIN_ID}.limits.${id}`,get position(){return{section:props.navigationSection,row,column:0
687
674
  }},label,onActivate,get disabled(){return accountLink.busy()
688
675
  }})
689
- return _$createComponent7(Show,{get when(){return eligible()
690
- },get children(){return [_$createComponent7(Show,{get when(){return _$memo4(()=>!!!props.model.accountID)()&&(!props.model.hostConnection||props.model.hostConnection.status==="ready")
691
- },get children(){return [(()=>{var _el$=_$createElement5("text")
692
- _$insertNode3(_el$,_$createTextNode2(`Link the matching Codex account once for this connection`))
693
- _$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))
694
681
  return _el$
695
- })(),_$memo4(()=>action("link",1,`${icons.icon("scope")} Link Codex CLI account`,()=>void accountLink.link()))]
696
- }}),_$createComponent7(Show,{get when(){return _$memo4(()=>!!props.model.accountID)()&&props.model.accountSource!=="host"
697
- },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"
698
685
  },get children(){return action("relink",5,`${icons.icon("scope")} Relink Codex account`,()=>void accountLink.link())
699
686
  }})]
700
687
  }})]
701
688
  }})
702
689
  }
703
690
  import {createComponent as _$createComponent8} from "@opentui/solid"
704
- 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
+ }
705
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"
706
759
  import {setProp as _$setProp6} from "@opentui/solid"
707
760
  import {createElement as _$createElement6} from "@opentui/solid"
708
- 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"
709
799
  function LimitsStatus(props){const message=()=>{const model=props.controller.current().model
710
800
  const connection=model?.hostConnection
711
801
  const snapshot=props.controller.snapshot()
712
802
  const state=props.controller.state()
713
803
  if(state.status==="error")
714
- 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
715
807
  if(snapshot?.message)
716
808
  return snapshot.availability==="stale"?`Stale \xB7 ${snapshot.message}`:snapshot.availability==="ready"?`Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()} \xB7 ${snapshot.message}`:snapshot.message
717
809
  if(connection?.status==="loading")
718
810
  return "Checking the OpenCode account\u2026"
719
811
  if(connection?.status==="unavailable")
720
- return "OpenCode account metadata unavailable \xB7 refresh below"
812
+ return "Account metadata unavailable \xB7 refresh to retry"
721
813
  if(connection?.status==="unsupported")
722
814
  return "Codex quotas require an active ChatGPT OAuth connection"
723
815
  if(!snapshot)
@@ -730,194 +822,146 @@ return "No documented quota bucket for this model"
730
822
  if(availability==="rate_limited")
731
823
  return "Provider rate-limited this read \xB7 try again later"
732
824
  if(availability==="stale")
733
- return "Stale provider data \xB7 refresh below"
825
+ return "Stale provider data \xB7 refresh to update"
734
826
  return `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()}`
735
827
  }
736
- return _$createComponent8(Show2,{get when(){return props.controller.current().model
737
- },get children(){var _el$=_$createElement6("text")
738
- _$setProp6(_el$,"wrapMode","word")
739
- _$insert5(_el$,message)
740
- _$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))
741
833
  return _el$
742
- }})
743
- }
744
- import {createComponent as _$createComponent9} from "@opentui/solid"
745
- import {effect as _$effect7} from "@opentui/solid"
746
- import {createTextNode as _$createTextNode3} from "@opentui/solid"
747
- import {insertNode as _$insertNode4} from "@opentui/solid"
748
- import {memo as _$memo5} from "@opentui/solid"
749
- import {insert as _$insert6} from "@opentui/solid"
750
- import {setProp as _$setProp7} from "@opentui/solid"
751
- import {createElement as _$createElement7} from "@opentui/solid"
752
- import {Show as Show3,createEffect as createEffect2,createSignal as createSignal6} from "solid-js"
753
- import {SelectionBox as SelectionBox2,useIcons as useIcons2} from "./tui.js"
754
- var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
834
+ })()
755
835
  })
756
- function truncateEnd(value,maxWidth){if(Bun.stringWidth(value)<=maxWidth)
757
- return value
758
- if(maxWidth<=0)
759
- return ""
760
- const ellipsis=maxWidth>=3?"...":".".repeat(maxWidth)
761
- const contentWidth=maxWidth-Bun.stringWidth(ellipsis)
762
- let result=""
763
- for(const{segment
764
- } of graphemes.segment(value)){if(Bun.stringWidth(result + segment)>contentWidth)
765
- break
766
- result += segment
767
- }
768
- return result + ellipsis
769
836
  }
770
- function Section(props){const icons=useIcons2()
771
- 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)
772
849
  const icon=()=>props.section?icons.section(props.section):props.icon
773
850
  const theme=()=>props.api.theme.current
774
851
  const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
775
852
  }),activate:props.onToggle
776
853
  })
777
- const action=props.headerAction?useSidebarItem(props.api,props.interaction,{id:props.headerAction.id,position:()=>({section:props.navigationSection??0,row:0,column:1
778
- }),disabled:props.headerAction.disabled,activate:props.headerAction.onActivate
779
- },()=>theme().accent,"control"):undefined
780
- createEffect2(()=>{props.headerAction?.label()
781
- setActionWidth(undefined)
782
- })
783
- const actionLabel=()=>{const label=props.headerAction?.label()??""
784
- const width=actionWidth()
785
- return width===undefined?label:truncateEnd(label,width-2)
786
- }
787
- return(()=>{var _el$=_$createElement7("box")
788
- _$setProp7(_el$,"gap",1)
789
- _$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
790
857
  },flexDirection:"row",height:1,justifyContent:"space-between",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return item.backgroundColor()
791
858
  },get onMouseOver(){return item.onMouseOver
792
859
  },get onMouseOut(){return item.onMouseOut
793
- },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")
794
- _$insertNode4(_el$2,_el$3)
795
- _$insertNode4(_el$2,_el$4)
796
- _$insertNode4(_el$2,_el$5)
797
- _$insertNode4(_el$2,_el$6)
798
- _$setProp7(_el$2,"flexShrink",0)
799
- _$setProp7(_el$2,"wrapMode","none")
800
- _$insert6(_el$3,()=>icons.icon(props.open?"expanded":"collapsed"))
801
- _$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())
802
870
  return()=>_c$()?`${icon()} `:""
803
871
  })())
804
- _$insert6(_el$6,()=>props.title)
805
- _$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
806
874
  },_v$3={fg:item.focused()?item.foregroundColor():theme().accent
807
875
  }
808
- _v$!==_p$.e&&(_p$.e=_$setProp7(_el$2,"fg",_v$,_p$.e))
809
- _v$2!==_p$.t&&(_p$.t=_$setProp7(_el$3,"style",_v$2,_p$.t))
810
- _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))
811
879
  return _p$
812
880
  },{e:undefined,t:undefined,a:undefined
813
881
  })
814
882
  return _el$2
815
- })(),(()=>{var _el$7=_$createElement7("box"),_el$8=_$createElement7("text")
816
- _$insertNode4(_el$7,_el$8)
817
- _$setProp7(_el$7,"flexDirection","row")
818
- _$setProp7(_el$7,"gap",1)
819
- _$setProp7(_el$7,"flexGrow",1)
820
- _$setProp7(_el$7,"minWidth",0)
821
- _$setProp7(_el$7,"justifyContent","flex-end")
822
- _$insert6(_el$7,_$createComponent9(Show3,{when:action,children:(control)=>_$createComponent9(SelectionBox2,{ref:(node)=>control().ref(node),get id(){return props.headerAction?.id
823
- },flexShrink:1,minWidth:3,overflow:"hidden",get backgroundColor(){return control().backgroundColor()
824
- },onSizeChange:function(){setActionWidth(this.width)
825
- },get onMouseOver(){return control().onMouseOver
826
- },get onMouseOut(){return control().onMouseOut
827
- },onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
828
- control().activate(event)
829
- },get children(){var _el$9=_$createElement7("text")
830
- _$setProp7(_el$9,"wrapMode","none")
831
- _$setProp7(_el$9,"height",1)
832
- _$insert6(_el$9,actionLabel)
833
- _$effect7((_$p)=>_$setProp7(_el$9,"fg",control().foregroundColor(),_$p))
834
- return _el$9
835
- }})
836
- }),_el$8)
837
- _$setProp7(_el$8,"wrapMode","none")
838
- _$setProp7(_el$8,"truncate",true)
839
- _$setProp7(_el$8,"height",1)
840
- _$insert6(_el$8,()=>props.summary)
841
- _$effect7((_p$)=>{var _v$4=item.focused()?item.foregroundColor():theme().textMuted,_v$5=props.headerAction?0:1
842
- _v$4!==_p$.e&&(_p$.e=_$setProp7(_el$8,"fg",_v$4,_p$.e))
843
- _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))
844
907
  return _p$
845
908
  },{e:undefined,t:undefined
846
909
  })
910
+ return _el$8
911
+ }}),null)
847
912
  return _el$7
848
913
  })()]
849
914
  }}),null)
850
- _$insert6(_el$,_$createComponent9(Show3,{get when(){return props.open
915
+ _$insert7(_el$,_$createComponent11(Show5,{get when(){return props.open
851
916
  },get children(){return props.children
852
917
  }}),null)
853
918
  return _el$
854
919
  })()
855
920
  }
856
- function ProviderLimitsSection(props){const icons=useIcons3()
857
- const dialogs=useDialogs4(props.api)
921
+ function ProviderLimitsSection(props){const dialogs=useDialogs3(props.api)
858
922
  const model=()=>props.controller.current().model
859
923
  const snapshot=props.controller.snapshot
860
- const count=()=>(snapshot()?.windows.length??0) + (snapshot()?.balances?.length??0)
861
- const action=(id,row,label,onActivate,disabled=false)=>_$createComponent10(LimitsAction,{get api(){return props.api
862
- },get interaction(){return props.interaction
863
- },id:`${PLUGIN_ID}.limits.${id}`,get position(){return{section:props.navigationSection,row,column:0
864
- }},label,onActivate,disabled
865
- })
866
924
  function openCredits(){const selected=model()
867
925
  if(selected)
868
- dialogs.open(()=>_$createComponent10(ResetCreditDialog,{get api(){return props.api
926
+ dialogs.open(()=>_$createComponent12(ResetCreditDialog,{get api(){return props.api
869
927
  },get preferences(){return props.preferences
870
928
  },get controller(){return props.controller
871
929
  },get adapter(){return props.codex
872
930
  },model:selected
873
931
  }))
874
932
  }
875
- return _$createComponent10(Section,{get api(){return props.api
933
+ return _$createComponent12(Section,{get api(){return props.api
876
934
  },get interaction(){return props.interaction
877
935
  },sectionId:`${PLUGIN_ID}.section.limits`,get navigationSection(){return props.navigationSection
878
- },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
+ })
879
944
  },get open(){return props.preferences.expanded().limits
880
- },onToggle:()=>props.preferences.toggleSectionExpanded("limits"),get children(){return _$createComponent10(Show4,{get when(){return model()
881
- },get fallback(){return(()=>{var _el$=_$createElement8("text")
882
- _$insertNode5(_el$,_$createTextNode4(`Open a session to view model limits`))
883
- _$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))
884
949
  return _el$
885
950
  })()
886
- },children:(selected)=>(()=>{var _el$3=_$createElement8("box"),_el$4=_$createElement8("text"),_el$5=_$createTextNode4(`/`)
887
- _$insertNode5(_el$3,_el$4)
888
- _$setProp8(_el$3,"gap",1)
889
- _$insertNode5(_el$4,_el$5)
890
- _$setProp8(_el$4,"wrapMode","none")
891
- _$setProp8(_el$4,"truncate",true)
892
- _$insert7(_el$4,()=>selected().providerID,_el$5)
893
- _$insert7(_el$4,()=>selected().modelID,null)
894
- _$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()
895
956
  }})),null)
896
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return _$memo6(()=>!!(snapshot()&&["ready","stale"].includes(snapshot().availability)))()?snapshot():undefined
897
- },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
898
959
  },get snapshot(){return quota()
899
960
  }})
900
961
  }),null)
901
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return snapshot()?.bankedResets
902
- },children:(credits)=>[(()=>{var _el$6=_$createElement8("text"),_el$7=_$createTextNode4(`Banked resets: `)
903
- _$insertNode5(_el$6,_el$7)
904
- _$insert7(_el$6,()=>credits().availableCount,null)
905
- _$effect8((_$p)=>_$setProp8(_el$6,"fg",props.api.theme.current.textMuted,_$p))
906
- return _el$6
907
- })(),_$createComponent10(Show4,{get when(){return credits().availableCount>0||props.preferences.pendingResetAttempt()
908
- },get children(){return action("credits",2,`${icons.icon("info")} Review reset credits`,openCredits)
909
- }})]
910
- }),null)
911
- _$insert7(_el$3,_$createComponent10(LimitsStatus,{get api(){return props.api
962
+ _$insert8(_el$3,_$createComponent12(LimitsStatus,{get api(){return props.api
912
963
  },get controller(){return props.controller
913
964
  }}),null)
914
- _$insert7(_el$3,_$createComponent10(Show4,{get when(){return props.controller.current().adapter||selected().hostConnection?.status==="unavailable"
915
- },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))
916
- }}),null)
917
- _$insert7(_el$3,()=>action("sources",6,`${icons.icon("info")} Provider sources`,()=>dialogs.open(()=>_$createComponent10(ProviderSupportDialog,{get api(){return props.api
918
- },get load(){return props.capabilities
919
- }}))),null)
920
- _$effect8((_$p)=>_$setProp8(_el$4,"fg",props.api.theme.current.textMuted,_$p))
921
965
  return _el$3
922
966
  })()
923
967
  })
@@ -1016,6 +1060,26 @@ return{...model,accountID:undefined,accountSource:undefined}
1016
1060
  const accountID=connection?.accountID??preferences.codexAccountForConnection(model.providerID,connection?.id,model.modelID)
1017
1061
  const accountSource=connection?.accountID?"host":"binding"
1018
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
+ }}}
1019
1083
  function selectedV2Model(context){const route=context.ui.router.current()
1020
1084
  const model=context.ui.model.current()
1021
1085
  if(route.type!=="session"||!model)
@@ -1035,7 +1099,7 @@ native?.onUpdated((providerID,modelID)=>{const selected=selectModel()
1035
1099
  if(selected?.providerID===providerID&&selected.modelID===modelID)
1036
1100
  controller.refresh()
1037
1101
  })
1038
- return{controller,codex,accountSource,selectModel,capabilities:native?.capabilities??(async ()=>providerQuotaCatalog())
1102
+ return{controller,codex,accountSource,selectModel,modelSelection:createLimitsModelSelection(api,selectModel,context)
1039
1103
  }}
1040
1104
  export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
1041
1105
  }