opencode-navigator 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/HOST_API.md +78 -0
- package/PROVIDERS.md +11 -4
- package/README.md +93 -10
- package/dist/{chunk-dz8cfnfs.js → chunk-bv0nm1tj.js} +4 -4
- package/dist/chunk-fd7394y1.js +17 -0
- package/dist/chunk-pwa1wzer.js +22 -0
- package/dist/layout-preset-actions.js +5 -11
- package/dist/navigator-updates.js +63 -25
- package/dist/provider-limits.js +127 -22
- package/dist/quick-actions-settings.js +2 -8
- package/dist/runtime-dependencies.json +5 -5
- package/dist/runtime-requirements.json +1 -0
- package/dist/server.js +12 -5
- package/dist/sidebar-settings.js +1052 -315
- package/dist/sidebar-shortcuts.js +1 -1
- package/dist/toolbar-settings.js +144 -35
- package/dist/tui.js +765 -564
- package/dist/upgrade-notes.js +87 -0
- package/package.json +20 -18
- package/scripts/snapshot-verifier.mjs +36 -0
- package/scripts/update-local-snapshot.mjs +29 -16
package/dist/provider-limits.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js";
|
|
3
3
|
function createCodexAppServerClient(input){const command=input.command??["codex","app-server","--stdio"]
|
|
4
4
|
const timeoutMs=input.timeoutMs??1e4
|
|
5
5
|
const pending=new Map
|
|
@@ -115,9 +115,15 @@ return await send(method,params,signal)
|
|
|
115
115
|
failAll(new DOMException("Codex app-server closed","AbortError"))
|
|
116
116
|
child?.kill()
|
|
117
117
|
}}}
|
|
118
|
-
function
|
|
118
|
+
function windowScopeMatches(snapshot,window){if(window.scope==="workspace")
|
|
119
|
+
return Boolean(snapshot.workspaceId)&&(window.modelID===undefined||window.modelID===snapshot.model.modelID)
|
|
120
|
+
if(window.scope==="account")
|
|
121
|
+
return Boolean(snapshot.accountId)&&window.modelID===undefined
|
|
122
|
+
return window.modelID===snapshot.model.modelID
|
|
119
123
|
}
|
|
120
|
-
|
|
124
|
+
function normalizeProviderQuota(snapshot){if(!snapshot.providerId||!snapshot.providerName||!snapshot.model.providerID||!snapshot.model.modelID||!snapshot.model.sessionID||snapshot.workspaceId!==undefined&&(typeof snapshot.workspaceId!=="string"||!snapshot.workspaceId.trim())||!Number.isFinite(snapshot.fetchedAt)){throw new Error("Invalid provider quota identity")
|
|
125
|
+
}
|
|
126
|
+
const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||window.scope!==undefined&&window.scope!=="account"&&window.scope!=="model"&&window.scope!=="workspace"||!windowScopeMatches(snapshot,window)||!window.unit||window.used!==undefined&&!Number.isFinite(window.used)||window.remaining!==undefined&&!Number.isFinite(window.remaining)||window.used===undefined&&window.remaining===undefined&&window.total===undefined||window.total!==undefined&&(!Number.isFinite(window.total)||window.total<0)||window.durationMinutes!==undefined&&(!Number.isFinite(window.durationMinutes)||window.durationMinutes<0)||window.resetsAt!==undefined&&!Number.isFinite(window.resetsAt)){throw new Error("Quota window does not match the selected model or contains invalid values")
|
|
121
127
|
}
|
|
122
128
|
return{...window}})
|
|
123
129
|
const bankedResets=snapshot.bankedResets
|
|
@@ -239,6 +245,24 @@ requests.abortAll()
|
|
|
239
245
|
for(const adapter of adapters)
|
|
240
246
|
adapter.dispose?.()
|
|
241
247
|
}}}
|
|
248
|
+
function projectWorkspaceUsage(info,model,provider,costs=[]){if(!info.workspace.id||provider.id!==model.providerID||provider.integrationID!=="opencode")
|
|
249
|
+
throw new Error("Workspace usage does not match the active provider")
|
|
250
|
+
let windows=[]
|
|
251
|
+
if(provider.id==="opencode-go"){windows=Object.entries(info.go??{}).map(([id,meter])=>{if(!Number.isFinite(meter.percent)||meter.percent<0||meter.percent>100||!Number.isFinite(meter.resetsAt))
|
|
252
|
+
throw new Error("Invalid native Go meter")
|
|
253
|
+
return{id:`go:${id}`,label:id,unit:"%",kind:"subscription",scope:"workspace",total:100,used:meter.percent,remaining:100-meter.percent,resetsAt:meter.resetsAt / 1000,limited:meter.limited
|
|
254
|
+
}})
|
|
255
|
+
} else if(!(costs.length>0&&costs.every((cost)=>cost.input===0))){windows=info.budgets.flatMap((budget)=>{const entry=budget.models.find((item)=>item.providerID===model.providerID&&item.modelID===model.modelID)
|
|
256
|
+
if(budget.coverage==="models"&&!entry)
|
|
257
|
+
return []
|
|
258
|
+
const spent=budget.per==="model"&&entry?entry.spent??0:budget.spent
|
|
259
|
+
if([spent,budget.limit,budget.resetsAt].some((value)=>!Number.isFinite(value))||spent<0||budget.limit<0)
|
|
260
|
+
throw new Error("Invalid native Console budget")
|
|
261
|
+
return [{id:`budget:${budget.id}`,label:"Workspace budget",unit:"USD",kind:"configured_limit",scope:"workspace",...budget.per==="model"&&entry&&{modelID:model.modelID},total:budget.limit,used:spent,remaining:Math.max(0,budget.limit-spent),resetsAt:budget.resetsAt / 1000,budget:{scope:budget.scope,per:budget.per,shared:budget.shared}}]
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
return{model,providerId:provider.id,providerName:provider.name,workspaceId:info.workspace.id,...info.workspace.name&&{workspaceName:info.workspace.name},fetchedAt:Date.now(),availability:"ready",windows,...windows.length===0&&{message:provider.id==="opencode-go"?"No Go allowance was reported for this workspace.":"No workspace budgets apply to this model."
|
|
265
|
+
}}}
|
|
242
266
|
function codexQuotaWindows(usage,modelID){const buckets=new Map
|
|
243
267
|
const legacy=usage.rateLimits
|
|
244
268
|
if(legacy.limitId==="codex"||!legacy.limitId&&!legacy.normalModelSlug)
|
|
@@ -565,10 +589,10 @@ _$setProp3(_el$,"width","100%")
|
|
|
565
589
|
_$setProp3(_el$,"minWidth",0)
|
|
566
590
|
_$setProp3(_el$,"gap",1)
|
|
567
591
|
_$setProp3(_el$,"overflow","hidden")
|
|
568
|
-
_$insert3(_el$,_$createComponent4(For,{get each(){return props.windows
|
|
569
|
-
},children:(
|
|
570
|
-
},window
|
|
571
|
-
})
|
|
592
|
+
_$insert3(_el$,_$createComponent4(For,{get each(){return props.windows.map((window)=>window.id)
|
|
593
|
+
},children:(_,index)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
|
|
594
|
+
},get window(){return props.windows[index()]
|
|
595
|
+
}})
|
|
572
596
|
}))
|
|
573
597
|
return _el$
|
|
574
598
|
})()
|
|
@@ -620,7 +644,7 @@ listeners.clear()
|
|
|
620
644
|
return{adapter,onUpdated(listener){listeners.add(listener)
|
|
621
645
|
return()=>listeners.delete(listener)
|
|
622
646
|
}}}
|
|
623
|
-
var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode
|
|
647
|
+
var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode Console / Go",["workspace-budget","subscription"],"OpenCode 2.2 public usage API: applicable workspace USD budgets and percent-based Go allowance; not a universal account balance API.","https://github.com/anomalyco/opencode/blob/v2.2.5/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
|
|
624
648
|
var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
|
|
625
649
|
var aliases={fireworks:"fireworks-ai",novita:"novita-ai",moonshot:"moonshotai","minimax-coding-plan":"minimax","minimax-coding-plan-cn":"minimax-cn",together:"togetherai","silicon-flow":"siliconflow","github-copilot-enterprise":"github-copilot","google-vertex-anthropic":"google-vertex","z-ai":"zai",zhipuai:"zai",dashscope:"alibaba","alibaba-cn":"alibaba",qwen:"alibaba","opencode-go":"opencode","opencode-zen":"opencode",codeium:"windsurf","azure-cognitive-services":"azure"
|
|
626
650
|
}
|
|
@@ -628,6 +652,65 @@ function providerQuotaCapability(id,canonical){const key=aliases[canonical??id]?
|
|
|
628
652
|
const found=PROVIDER_QUOTA_CAPABILITIES.find((entry)=>entry.id===key)
|
|
629
653
|
return found??{id,name:id,sources:[],reason:"No independent quota/balance API or native counter semantics are verified for this provider.",documentation:["https://opencode.ai/docs/providers/"]
|
|
630
654
|
}}
|
|
655
|
+
import {createSignal as createSignal5} from "solid-js"
|
|
656
|
+
function createOpenCodeUsageAdapter(context){const [revision,setRevision]=createSignal5(0)
|
|
657
|
+
let generation=0
|
|
658
|
+
const location=()=>context.location??context.data.location.default()
|
|
659
|
+
const provider=(model)=>context.data.location.provider?.list(location())?.find((item)=>item.id===model.providerID)
|
|
660
|
+
const connection=()=>{const item=context.data.location.integration?.list(location())?.find((entry)=>entry.id==="opencode")?.connections[0]
|
|
661
|
+
if(!item)
|
|
662
|
+
return null
|
|
663
|
+
if(item.type==="credential")
|
|
664
|
+
return [item.type,item.id,item.method,item.status]
|
|
665
|
+
return [item.type,item.name,item.status]
|
|
666
|
+
}
|
|
667
|
+
const key=()=>JSON.stringify([location(),connection(),revision()])
|
|
668
|
+
const invalidate=()=>{generation++
|
|
669
|
+
}
|
|
670
|
+
const disposers=[context.data.on("credential.updated",invalidate),context.data.on("integration.updated",(event)=>{const directory=event.location?.directory
|
|
671
|
+
if(!directory||directory===location().directory)
|
|
672
|
+
invalidate()
|
|
673
|
+
}),context.data.on("credential.switched",(event)=>{if(event.data.integrationID!=="opencode")
|
|
674
|
+
return
|
|
675
|
+
generation++
|
|
676
|
+
setRevision((value)=>value + 1)
|
|
677
|
+
}),context.data.on("server.connected",()=>{generation++
|
|
678
|
+
setRevision((value)=>value + 1)
|
|
679
|
+
})]
|
|
680
|
+
const adapter={id:"opencode-usage",name:"OpenCode workspace limits",supports:(model)=>typeof context.client.usage?.limits==="function"&&provider(model)?.integrationID==="opencode",async read(model,signal){signal.throwIfAborted()
|
|
681
|
+
const epoch=generation
|
|
682
|
+
const target=key()
|
|
683
|
+
const activeProvider=provider(model)
|
|
684
|
+
if(!activeProvider||activeProvider.integrationID!=="opencode")
|
|
685
|
+
throw new Error("Native usage provider changed")
|
|
686
|
+
try{const result=await context.client.usage.limits({location:{directory:location().directory}},{signal})
|
|
687
|
+
if(epoch!==generation||target!==key()||signal.aborted||result.location.directory!==location().directory)
|
|
688
|
+
throw new DOMException("Workspace usage target changed","AbortError")
|
|
689
|
+
if(!result.data)
|
|
690
|
+
return{model,providerId:model.providerID,providerName:activeProvider.name,fetchedAt:Date.now(),availability:"unauthenticated",windows:[],message:"Sign in to OpenCode Console to view workspace limits."
|
|
691
|
+
}
|
|
692
|
+
const costs=context.data.location.model?.list(location())?.find((item)=>item.providerID===model.providerID&&item.id===model.modelID)?.cost
|
|
693
|
+
return projectWorkspaceUsage(result.data,model,activeProvider,costs)
|
|
694
|
+
} catch(error){if(signal.aborted||epoch!==generation||target!==key()||error instanceof DOMException&&error.name==="AbortError")
|
|
695
|
+
throw error
|
|
696
|
+
const status=error?.status??error?.response?.status
|
|
697
|
+
const tag=error?._tag
|
|
698
|
+
if(status===undefined&&tag!=="UnauthorizedError"&&tag!=="ForbiddenError")
|
|
699
|
+
throw new Error("OpenCode workspace limits are unavailable. Refresh to retry the native source.",{cause:error
|
|
700
|
+
})
|
|
701
|
+
const states={401:"unauthenticated",403:"permission_required",429:"rate_limited"}
|
|
702
|
+
let availability="unavailable"
|
|
703
|
+
if(status!==undefined&&status in states)
|
|
704
|
+
availability=states[status]
|
|
705
|
+
if(tag==="UnauthorizedError")
|
|
706
|
+
availability="unauthenticated"
|
|
707
|
+
else if(tag==="ForbiddenError")
|
|
708
|
+
availability="permission_required"
|
|
709
|
+
return{model,providerId:model.providerID,providerName:activeProvider.name,fetchedAt:Date.now(),windows:[],availability,message:"OpenCode workspace limits are unavailable. Refresh to retry the native source."
|
|
710
|
+
}}},dispose(){for(const dispose of disposers)
|
|
711
|
+
dispose()
|
|
712
|
+
}}
|
|
713
|
+
return{adapter,target:(model)=>({...model,quotaContext:key()})}}
|
|
631
714
|
import {insert as _$insert9} from "@opentui/solid"
|
|
632
715
|
import {mergeProps as _$mergeProps} from "@opentui/solid"
|
|
633
716
|
import {setProp as _$setProp10} from "@opentui/solid"
|
|
@@ -654,7 +737,7 @@ import {insert as _$insert4} from "@opentui/solid"
|
|
|
654
737
|
import {setProp as _$setProp4} from "@opentui/solid"
|
|
655
738
|
import {createElement as _$createElement4} from "@opentui/solid"
|
|
656
739
|
import {SelectionBox} from "./tui.js"
|
|
657
|
-
import {createSignal as
|
|
740
|
+
import {createSignal as createSignal6,onCleanup} from "solid-js"
|
|
658
741
|
function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
|
|
659
742
|
let backgroundColor=theme.backgroundPanel
|
|
660
743
|
let foregroundColor=normalColor
|
|
@@ -670,7 +753,7 @@ else if(highlighted)
|
|
|
670
753
|
foregroundColor=theme.selectedListItemText
|
|
671
754
|
return{backgroundColor,foregroundColor}}
|
|
672
755
|
function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
|
|
673
|
-
const [hovered,setHovered]=
|
|
756
|
+
const [hovered,setHovered]=createSignal6(false)
|
|
674
757
|
const disabled=()=>descriptor.disabled?.()===true
|
|
675
758
|
const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
|
|
676
759
|
const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
|
|
@@ -737,7 +820,7 @@ import {insert as _$insert5} from "@opentui/solid"
|
|
|
737
820
|
import {setProp as _$setProp6} from "@opentui/solid"
|
|
738
821
|
import {createElement as _$createElement6} from "@opentui/solid"
|
|
739
822
|
import {memo as _$memo3} from "@opentui/solid"
|
|
740
|
-
import {createEffect as createEffect2,createSignal as
|
|
823
|
+
import {createEffect as createEffect2,createSignal as createSignal7,on} from "solid-js"
|
|
741
824
|
import {SelectionBox as SelectionBox2} from "./tui.js"
|
|
742
825
|
var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
|
|
743
826
|
})
|
|
@@ -754,7 +837,7 @@ result += segment
|
|
|
754
837
|
}
|
|
755
838
|
return result + ellipsis
|
|
756
839
|
}
|
|
757
|
-
function SectionHeaderControl(props){const [width,setWidth]=
|
|
840
|
+
function SectionHeaderControl(props){const [width,setWidth]=createSignal7()
|
|
758
841
|
const control=useSidebarItem(props.api,props.interaction,{id:props.action.id,position:()=>props.position,disabled:props.action.disabled,activate:props.action.onActivate
|
|
759
842
|
},()=>props.api.theme.current.accent,"control")
|
|
760
843
|
createEffect2(on(()=>[props.action.label(),props.resetWidth],()=>setWidth(undefined)))
|
|
@@ -883,10 +966,10 @@ import {memo as _$memo6} from "@opentui/solid"
|
|
|
883
966
|
import {insert as _$insert8} from "@opentui/solid"
|
|
884
967
|
import {setProp as _$setProp9} from "@opentui/solid"
|
|
885
968
|
import {createElement as _$createElement9} from "@opentui/solid"
|
|
886
|
-
import {Show as Show6,createSignal as
|
|
969
|
+
import {Show as Show6,createSignal as createSignal8} from "solid-js"
|
|
887
970
|
import {SelectionBox as SelectionBox3,useIcons as useIcons4} from "./tui.js"
|
|
888
971
|
function Section(props){const icons=useIcons4()
|
|
889
|
-
const [headerWidth,setHeaderWidth]=
|
|
972
|
+
const [headerWidth,setHeaderWidth]=createSignal8(0)
|
|
890
973
|
const icon=()=>props.section?icons.section(props.section):props.icon
|
|
891
974
|
const theme=()=>props.api.theme.current
|
|
892
975
|
const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
|
|
@@ -1020,7 +1103,7 @@ const model=api.state.session.get(sessionID)?.model
|
|
|
1020
1103
|
if(!model)
|
|
1021
1104
|
return
|
|
1022
1105
|
return{sessionID,providerID:model.providerID,modelID:model.id,...model.variant&&{variant:model.variant}}}
|
|
1023
|
-
import {createSignal as
|
|
1106
|
+
import {createSignal as createSignal9,untrack as untrack2} from "solid-js"
|
|
1024
1107
|
var CHATGPT_METHODS=new Set(["chatgpt-browser","chatgpt-headless"])
|
|
1025
1108
|
function providerAccountIdentity(provider,credentials){if(!provider.integrationID||provider.activation==="disabled")
|
|
1026
1109
|
return{status:"unsupported"}
|
|
@@ -1044,7 +1127,7 @@ const header=Object.entries(provider.headers??{}).find(([name])=>name.toLowerCas
|
|
|
1044
1127
|
if(accountID&&header&&header!==accountID)
|
|
1045
1128
|
return{status:"loading"}
|
|
1046
1129
|
return{status:"ready",id,method:"oauth",providerID,...accountID&&{accountID}}}
|
|
1047
|
-
function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=
|
|
1130
|
+
function createOpenCodeV2AccountSource(context,lifecycle){const [connections,setConnections]=createSignal9({})
|
|
1048
1131
|
let generation=0
|
|
1049
1132
|
let disposed=false
|
|
1050
1133
|
let lastModel
|
|
@@ -1092,8 +1175,17 @@ if(lastModel&&!disposed)
|
|
|
1092
1175
|
read(lastModel)
|
|
1093
1176
|
}
|
|
1094
1177
|
const events=["credential.switched","credential.updated","integration.updated","provider.updated","server.connected"]
|
|
1095
|
-
const unsubscribes=events.map((event)=>context.data.on(event,()=>untrack2(()=>
|
|
1096
|
-
|
|
1178
|
+
const unsubscribes=events.map((event)=>context.data.on(event,(observation)=>untrack2(()=>{const directory=observation?.location?.directory
|
|
1179
|
+
if((event==="provider.updated"||event==="integration.updated")&&directory&&directory!==location().directory)
|
|
1180
|
+
return
|
|
1181
|
+
invalidate(event!=="provider.updated")
|
|
1182
|
+
})))
|
|
1183
|
+
return{suspend(){if(!lastModel)
|
|
1184
|
+
return
|
|
1185
|
+
lastModel=undefined
|
|
1186
|
+
generation++
|
|
1187
|
+
pending=undefined
|
|
1188
|
+
},current(model){lastModel=model
|
|
1097
1189
|
return{...model,hostConnection:connections()[key(model)]??{status:"loading"}}},async refresh(model){await read(model)
|
|
1098
1190
|
},async validate(model,signal){const actual=await read(model,signal)
|
|
1099
1191
|
const expected=model.hostConnection
|
|
@@ -1137,16 +1229,29 @@ if(route.type!=="session"||!model)
|
|
|
1137
1229
|
return
|
|
1138
1230
|
return{sessionID:route.sessionID,providerID:model.providerID,modelID:model.modelID,...model.variant&&{variant:model.variant}}}
|
|
1139
1231
|
function createProviderQuotaIntegration(api,preferences,version,context){const selectModel=()=>context?selectedV2Model(context):selectedV1Model(api)
|
|
1140
|
-
const
|
|
1232
|
+
const hostAccounts=context?createOpenCodeV2AccountSource(context,api.lifecycle.signal):undefined
|
|
1233
|
+
const usage=context?createOpenCodeUsageAdapter(context):undefined
|
|
1234
|
+
const accountSource=hostAccounts&&{...hostAccounts,current(model){if(usage?.adapter.supports(model)){hostAccounts.suspend?.()
|
|
1235
|
+
return usage.target(model)
|
|
1236
|
+
}
|
|
1237
|
+
return hostAccounts.current(model)
|
|
1238
|
+
},refresh(model){if(usage?.adapter.supports(model)){hostAccounts.suspend?.()
|
|
1239
|
+
return Promise.resolve()
|
|
1240
|
+
}
|
|
1241
|
+
return hostAccounts.refresh(model)
|
|
1242
|
+
}}
|
|
1141
1243
|
const codex=createCodexQuotaAdapter(createCodexAppServerClient({version}),accountSource?.validate)
|
|
1142
1244
|
const native=context?createNativeQuotaAdapter(context):undefined
|
|
1143
|
-
const controller=createProviderLimitsController([codex,...native?[native.adapter]:[]],()=>{const model=selectModel()
|
|
1144
|
-
|
|
1245
|
+
const controller=createProviderLimitsController([...usage?[usage.adapter]:[],codex,...native?[native.adapter]:[]],()=>{const model=selectModel()
|
|
1246
|
+
if(!model)
|
|
1247
|
+
return
|
|
1248
|
+
const selected=accountSource?.current(model)??model
|
|
1249
|
+
return usage?.adapter.supports(model)?selected:bindCodexAccount(selected,preferences)
|
|
1145
1250
|
},api.lifecycle.signal,accountSource&&(async ()=>{const model=selectModel()
|
|
1146
1251
|
if(model)
|
|
1147
1252
|
await accountSource.refresh(model)
|
|
1148
1253
|
}))
|
|
1149
1254
|
return{controller,codex,accountSource,selectModel,modelSelection:createLimitsModelSelection(api,selectModel,context)
|
|
1150
1255
|
}}
|
|
1151
|
-
export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
|
|
1256
|
+
export{ProviderLimitsSection,bindCodexAccount,createCodexAppServerClient,createCodexQuotaAdapter,createOpenCodeUsageAdapter,createOpenCodeV2AccountSource,createProviderLimitsController,createProviderQuotaIntegration,selectedV1Model
|
|
1152
1257
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PLUGIN_ID
|
|
2
|
-
} from "./chunk-
|
|
2
|
+
} from "./chunk-bv0nm1tj.js";
|
|
3
3
|
import {memo as _$memo} from "@opentui/solid"
|
|
4
4
|
import {createComponent as _$createComponent} from "@opentui/solid"
|
|
5
5
|
import {use as _$use} from "@opentui/solid"
|
|
@@ -124,13 +124,7 @@ return _el$21
|
|
|
124
124
|
})()]
|
|
125
125
|
}})
|
|
126
126
|
}))
|
|
127
|
-
_$effect((
|
|
128
|
-
_v$4!==_p$.e&&(_p$.e=_$setProp(_el$0,"renderBefore",_v$4,_p$.e))
|
|
129
|
-
_v$5!==_p$.t&&(_p$.t=_$setProp(_el$0,"renderAfter",_v$5,_p$.t))
|
|
130
|
-
_v$6!==_p$.a&&(_p$.a=_$setProp(_el$0,"height",_v$6,_p$.a))
|
|
131
|
-
return _p$
|
|
132
|
-
},{e:undefined,t:undefined,a:undefined
|
|
133
|
-
})
|
|
127
|
+
_$effect((_$p)=>_$setProp(_el$0,"height",Math.min(actions().length*2-1,Math.max(3,Math.floor(dimensions().height*0.75)-8)),_$p))
|
|
134
128
|
return _el$0
|
|
135
129
|
})(),(()=>{var _el$10=_$createElement("text"),_el$11=_$createTextNode(` navigate \xB7 `),_el$12=_$createTextNode(` toggle \xB7 `),_el$13=_$createTextNode(` or `),_el$15=_$createTextNode(` reorder`)
|
|
136
130
|
_$insertNode(_el$10,_el$11)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"dependencies": {
|
|
4
|
-
"@opencode/plugin": "2.
|
|
5
|
-
"@opentui/core": "0.
|
|
6
|
-
"@opentui/keymap": "0.
|
|
7
|
-
"@opentui/solid": "0.
|
|
8
|
-
"solid-js": "1.9.
|
|
4
|
+
"@opencode/plugin": "2.2.5",
|
|
5
|
+
"@opentui/core": "0.8.0",
|
|
6
|
+
"@opentui/keymap": "0.8.0",
|
|
7
|
+
"@opentui/solid": "0.8.0",
|
|
8
|
+
"solid-js": "1.9.18"
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":1,"bun":"1.3.14","node":"26.4.0"}
|
package/dist/server.js
CHANGED
|
@@ -27,9 +27,15 @@ var model={type:"object",properties:{providerID:text,modelID:text,sessionID:text
|
|
|
27
27
|
}
|
|
28
28
|
var NavigatorQuotaRpc={id:"opencode-navigator.quotas",methods:{read:{input:model,output:{type:"object",additionalProperties:true}},capabilities:{input:{type:"object",additionalProperties:false},output:{type:"array",items:{type:"object",additionalProperties:true}}}},events:{updated:{schema:{type:"object",properties:{providerID:text,modelID:text},required:["providerID","modelID"],additionalProperties:false
|
|
29
29
|
}}}};
|
|
30
|
-
function
|
|
30
|
+
function windowScopeMatches(snapshot,window){if(window.scope==="workspace")
|
|
31
|
+
return Boolean(snapshot.workspaceId)&&(window.modelID===undefined||window.modelID===snapshot.model.modelID)
|
|
32
|
+
if(window.scope==="account")
|
|
33
|
+
return Boolean(snapshot.accountId)&&window.modelID===undefined
|
|
34
|
+
return window.modelID===snapshot.model.modelID
|
|
31
35
|
}
|
|
32
|
-
|
|
36
|
+
function normalizeProviderQuota(snapshot){if(!snapshot.providerId||!snapshot.providerName||!snapshot.model.providerID||!snapshot.model.modelID||!snapshot.model.sessionID||snapshot.workspaceId!==undefined&&(typeof snapshot.workspaceId!=="string"||!snapshot.workspaceId.trim())||!Number.isFinite(snapshot.fetchedAt)){throw new Error("Invalid provider quota identity")
|
|
37
|
+
}
|
|
38
|
+
const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||window.scope!==undefined&&window.scope!=="account"&&window.scope!=="model"&&window.scope!=="workspace"||!windowScopeMatches(snapshot,window)||!window.unit||window.used!==undefined&&!Number.isFinite(window.used)||window.remaining!==undefined&&!Number.isFinite(window.remaining)||window.used===undefined&&window.remaining===undefined&&window.total===undefined||window.total!==undefined&&(!Number.isFinite(window.total)||window.total<0)||window.durationMinutes!==undefined&&(!Number.isFinite(window.durationMinutes)||window.durationMinutes<0)||window.resetsAt!==undefined&&!Number.isFinite(window.resetsAt)){throw new Error("Quota window does not match the selected model or contains invalid values")
|
|
33
39
|
}
|
|
34
40
|
return{...window}})
|
|
35
41
|
const bankedResets=snapshot.bankedResets
|
|
@@ -39,7 +45,7 @@ return{...balance}})
|
|
|
39
45
|
if(bankedResets&&(!Number.isSafeInteger(bankedResets.availableCount)||bankedResets.availableCount<0)){throw new Error("Invalid banked reset count")
|
|
40
46
|
}
|
|
41
47
|
return{...snapshot,windows,...balances&&{balances},...bankedResets&&{bankedResets:{availableCount:bankedResets.availableCount,...bankedResets.credits&&{credits:[...bankedResets.credits]}}}}}
|
|
42
|
-
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
|
|
48
|
+
var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode Console / Go",["workspace-budget","subscription"],"OpenCode 2.2 public usage API: applicable workspace USD budgets and percent-based Go allowance; not a universal account balance API.","https://github.com/anomalyco/opencode/blob/v2.2.5/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
|
|
43
49
|
var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
|
|
44
50
|
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"
|
|
45
51
|
}
|
|
@@ -144,7 +150,7 @@ runtime.directory=directory
|
|
|
144
150
|
if(signal.aborted){await cleanup(runtime)
|
|
145
151
|
signal.throwIfAborted()
|
|
146
152
|
}
|
|
147
|
-
const client=new sdk.CopilotClient({mode:"empty",baseDirectory:directory,useLoggedInUser:false,logLevel:"none"
|
|
153
|
+
const client=new sdk.CopilotClient({mode:"empty",baseDirectory:directory,useLoggedInUser:false,logLevel:"none"
|
|
148
154
|
})
|
|
149
155
|
runtime.client=client
|
|
150
156
|
try{await client.start()
|
|
@@ -602,7 +608,8 @@ const request=await host.session.hook("http.request",(event)=>service.request(ev
|
|
|
602
608
|
const response=await host.session.hook("http.response",async (event)=>{if(await service.response(event))
|
|
603
609
|
await rpc.events.emit("updated",{providerID:event.model.providerID,modelID:event.model.id}).catch(()=>{})
|
|
604
610
|
});
|
|
605
|
-
(async ()=>{const events=host.event.subscribe({signal:lifecycle.signal
|
|
611
|
+
(async ()=>{const events=host.event.subscribe({signal:lifecycle.signal,types:["credential.updated","credential.switched","integration.updated","provider.updated"]
|
|
612
|
+
})
|
|
606
613
|
for await (const event of events){if(["credential.updated","credential.switched","integration.updated","provider.updated"].includes(event.type))
|
|
607
614
|
service.invalidate()
|
|
608
615
|
}})().catch(()=>{})
|