opencode-navigator 0.21.8 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/HOST_API.md +59 -0
- package/PROVIDERS.md +11 -4
- package/README.md +118 -24
- package/dist/chunk-bv0nm1tj.js +9 -0
- package/dist/chunk-qmntexwm.js +0 -0
- package/dist/chunk-v6bz59xn.js +20 -0
- package/dist/icon-picker.js +175 -0
- package/dist/layout-preset-actions.js +8 -30
- package/dist/navigator-updates.js +12 -4
- package/dist/project-profile.js +12 -4
- package/dist/provider-limits.js +117 -17
- package/dist/quick-actions-settings.js +6 -20
- package/dist/rpc.js +1 -0
- package/dist/runtime-dependencies.json +10 -0
- package/dist/runtime-requirements.json +1 -0
- package/dist/server.js +27 -58
- package/dist/settings-import.js +4 -3
- package/dist/sidebar-settings.js +822 -0
- package/dist/sidebar-shortcuts.js +261 -0
- package/dist/toolbar-settings.js +161 -0
- package/dist/tui.js +1337 -1288
- package/package.json +14 -11
- package/scripts/snapshot-runtime.mjs +32 -0
- package/scripts/snapshot-verifier.mjs +36 -0
- package/scripts/update-local-snapshot.mjs +43 -19
- package/dist/chunk-d1bws32s.js +0 -8
package/dist/server.js
CHANGED
|
@@ -6,30 +6,6 @@ const statuses=new Set(["pending","in_progress","completed","cancelled"])
|
|
|
6
6
|
const priorities=new Set(["high","medium","low"])
|
|
7
7
|
const todos=Array.isArray(state.todos)?state.todos.filter((item)=>item&&typeof item.content==="string"&&item.content.trim().length>0&&item.content.length<=512&&statuses.has(item.status)).slice(0,100).map((item)=>({content:item.content.trim(),status:item.status,...priorities.has(item.priority??"")&&{priority:item.priority}})):[]
|
|
8
8
|
return{todos,guidance:state.guidance===true}}
|
|
9
|
-
function todoGuidance(state){const lines=state.todos.map((todo)=>`- [${todo.status}] ${todo.content}`)
|
|
10
|
-
return ["Navigator Todo: when work has multiple steps, use navigator_todo_write to keep the session Todo list current.","Mark work in progress, completed, or cancelled as it changes; avoid claiming unfinished work is done.",...lines.length>0?["Current Navigator Todo items:",...lines]:[]].join(`
|
|
11
|
-
`)
|
|
12
|
-
}
|
|
13
|
-
function key(sessionID){return `session.todo.${sessionID}`
|
|
14
|
-
}
|
|
15
|
-
function createManagedTodos(storage,onChange){const pending=new Map
|
|
16
|
-
async function list(sessionID){return normalizeTodoState(await storage.get(key(sessionID)))
|
|
17
|
-
}
|
|
18
|
-
function change(sessionID,apply){const request=(pending.get(sessionID)??Promise.resolve()).catch(()=>{}).then(async ()=>{const next=apply(await list(sessionID))
|
|
19
|
-
await storage.set(key(sessionID),next)
|
|
20
|
-
await onChange(sessionID,next)
|
|
21
|
-
return next
|
|
22
|
-
})
|
|
23
|
-
pending.set(sessionID,request)
|
|
24
|
-
const clear=()=>{if(pending.get(sessionID)===request)
|
|
25
|
-
pending.delete(sessionID)
|
|
26
|
-
}
|
|
27
|
-
request.then(clear).catch(clear)
|
|
28
|
-
return request
|
|
29
|
-
}
|
|
30
|
-
return{list,replace(sessionID,todos){return change(sessionID,(current)=>({...current,todos:normalizeTodoState({todos}).todos}))
|
|
31
|
-
},guidance(sessionID,enabled){return change(sessionID,(current)=>({...current,guidance:enabled}))
|
|
32
|
-
}}}
|
|
33
9
|
var item={type:"object",properties:{content:{type:"string",minLength:1,maxLength:512},status:{type:"string",enum:["pending","in_progress","completed","cancelled"]},priority:{type:"string",enum:["high","medium","low"]}},required:["content","status"],additionalProperties:false
|
|
34
10
|
}
|
|
35
11
|
var todos={type:"array",items:item,maxItems:100}
|
|
@@ -43,14 +19,23 @@ var NavigatorTodoRpc={id:"opencode-navigator.todo",methods:{list:{input:{type:"o
|
|
|
43
19
|
},output
|
|
44
20
|
}},events:{updated:{schema:{type:"object",properties:{sessionID,todos,guidance:{type:"boolean"}},required:["sessionID","todos","guidance"],additionalProperties:false
|
|
45
21
|
}}}};
|
|
22
|
+
var LegacyTodoArchiveRpc={id:NavigatorTodoRpc.id,methods:{list:NavigatorTodoRpc.methods.list},events:{}}
|
|
23
|
+
async function readLegacyTodoArchive(get,sessionID){return normalizeTodoState(await get(`session.todo.${sessionID}`))
|
|
24
|
+
}
|
|
46
25
|
var text={type:"string",minLength:1,maxLength:512}
|
|
47
26
|
var model={type:"object",properties:{providerID:text,modelID:text,sessionID:text,connectionID:text,variant:text,force:{type:"boolean"}},required:["providerID","modelID","sessionID"],additionalProperties:false
|
|
48
27
|
}
|
|
49
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
|
|
50
29
|
}}}};
|
|
51
|
-
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
|
|
52
35
|
}
|
|
53
|
-
|
|
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")
|
|
54
39
|
}
|
|
55
40
|
return{...window}})
|
|
56
41
|
const bankedResets=snapshot.bankedResets
|
|
@@ -60,7 +45,7 @@ return{...balance}})
|
|
|
60
45
|
if(bankedResets&&(!Number.isSafeInteger(bankedResets.availableCount)||bankedResets.availableCount<0)){throw new Error("Invalid banked reset count")
|
|
61
46
|
}
|
|
62
47
|
return{...snapshot,windows,...balances&&{balances},...bankedResets&&{bankedResets:{availableCount:bankedResets.availableCount,...bankedResets.credits&&{credits:[...bankedResets.credits]}}}}}
|
|
63
|
-
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.0/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
|
|
64
49
|
var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
|
|
65
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"
|
|
66
51
|
}
|
|
@@ -484,6 +469,10 @@ const windows=openRouterFreeWindow(body,input.model.modelID,input.apiModelID)
|
|
|
484
469
|
const id=typeof data.organization_id==="string"?data.organization_id:undefined
|
|
485
470
|
return{...balanceSnapshot(input.model,input.provider.name,balances,Date.now(),id),windows,...windows.length>0&&{availability:"ready"},message:data.is_management_key===true?undefined:"Key budget, not account balance. Reading account credits requires a management key."
|
|
486
471
|
}}
|
|
472
|
+
function credentialToken(credential){if(!credential||credential.type==="external")
|
|
473
|
+
return
|
|
474
|
+
return credential.type==="oauth"?credential.access:credential.key
|
|
475
|
+
}
|
|
487
476
|
async function nativeQuotaContext(host,model,signal,fetcher){const [provider,catalog]=await Promise.all([host.provider.get({providerID:model.providerID}),host.model.list()])
|
|
488
477
|
const selected=catalog.data.find((entry)=>entry.providerID===model.providerID&&entry.id===model.modelID&&entry.enabled)
|
|
489
478
|
if(!selected||!provider.data.integrationID||provider.data.activation==="disabled")
|
|
@@ -492,7 +481,7 @@ const connection=await host.integration.connection.active(provider.data.integrat
|
|
|
492
481
|
if(!connection)
|
|
493
482
|
return
|
|
494
483
|
const credential=await host.integration.connection.resolve(connection)
|
|
495
|
-
const token=credential
|
|
484
|
+
const token=credentialToken(credential)
|
|
496
485
|
if(!credential||!token)
|
|
497
486
|
return
|
|
498
487
|
signal.throwIfAborted()
|
|
@@ -502,7 +491,7 @@ async function sameNativeConnection(host,input){const current=input.provider.int
|
|
|
502
491
|
if(!current||connectionKey(current)!==input.connectionID)
|
|
503
492
|
return false
|
|
504
493
|
const credential=await host.integration.connection.resolve(current)
|
|
505
|
-
const token=credential
|
|
494
|
+
const token=credentialToken(credential)
|
|
506
495
|
return token===input.token
|
|
507
496
|
}
|
|
508
497
|
function requestUsesCredential(request,input){const url=new URL(request.url)
|
|
@@ -518,7 +507,7 @@ if(["x-api-key","api-key","x-goog-api-key"].some((header)=>request.headers.get(h
|
|
|
518
507
|
return true
|
|
519
508
|
return url.hostname==="generativelanguage.googleapis.com"&&url.searchParams.get("key")===input.token
|
|
520
509
|
}
|
|
521
|
-
function
|
|
510
|
+
function key(input){return JSON.stringify([input.model.providerID,input.model.modelID,input.model.variant,input.connectionID,createHash("sha256").update(input.token).digest("hex")])
|
|
522
511
|
}
|
|
523
512
|
function modelForEvent(event){return{providerID:event.model.providerID,modelID:event.model.id,sessionID:event.sessionID}}
|
|
524
513
|
function rejectedRead(model,error,input){const status=error instanceof QuotaReadError?error.status:undefined
|
|
@@ -540,7 +529,7 @@ if(input.canonical==="github-copilot")
|
|
|
540
529
|
return copilot.read(input)
|
|
541
530
|
return readNativeRest(input)
|
|
542
531
|
}
|
|
543
|
-
async function currentResult(input,force){const identity=
|
|
532
|
+
async function currentResult(input,force){const identity=key(input)
|
|
544
533
|
const headers=observed.get(identity)
|
|
545
534
|
const existing=cached.get(identity)
|
|
546
535
|
let result=!force&&existing&&Date.now()-existing.fetchedAt<30000?existing:undefined
|
|
@@ -561,13 +550,13 @@ const epoch=generation
|
|
|
561
550
|
const observation=AbortSignal.any([lifecycle,AbortSignal.timeout(1000)])
|
|
562
551
|
try{const input=await withinQuotaSignal(observation,()=>nativeQuotaContext(host,modelForEvent(event),observation,fetcher))
|
|
563
552
|
if(input&&epoch===generation&&requestUsesCredential(event.request,input)&&await withinQuotaSignal(observation,()=>sameNativeConnection(host,input)))
|
|
564
|
-
captured.set(event.request,{key:
|
|
553
|
+
captured.set(event.request,{key:key(input),generation:epoch})
|
|
565
554
|
} catch{}},async response(event){const request=captured.get(event.request)
|
|
566
555
|
if(!request||request.generation!==generation||lifecycle.aborted)
|
|
567
556
|
return false
|
|
568
557
|
const observation=AbortSignal.any([lifecycle,AbortSignal.timeout(1000)])
|
|
569
558
|
try{const input=await withinQuotaSignal(observation,()=>nativeQuotaContext(host,modelForEvent(event),observation,fetcher))
|
|
570
|
-
if(!input||
|
|
559
|
+
if(!input||key(input)!==request.key||!await withinQuotaSignal(observation,()=>sameNativeConnection(host,input)))
|
|
571
560
|
return false
|
|
572
561
|
let windows=nativeHeaderWindows(input.canonical,input.model.modelID,event.response.headers)
|
|
573
562
|
if(windows.length===0&&event.response.status===429&&["google","google-vertex"].includes(input.canonical)&&event.response.headers.get("content-type")?.includes("json"))
|
|
@@ -619,7 +608,8 @@ const request=await host.session.hook("http.request",(event)=>service.request(ev
|
|
|
619
608
|
const response=await host.session.hook("http.response",async (event)=>{if(await service.response(event))
|
|
620
609
|
await rpc.events.emit("updated",{providerID:event.model.providerID,modelID:event.model.id}).catch(()=>{})
|
|
621
610
|
});
|
|
622
|
-
(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
|
+
})
|
|
623
613
|
for await (const event of events){if(["credential.updated","credential.switched","integration.updated","provider.updated"].includes(event.type))
|
|
624
614
|
service.invalidate()
|
|
625
615
|
}})().catch(()=>{})
|
|
@@ -629,32 +619,11 @@ await response.dispose()
|
|
|
629
619
|
await rpc.dispose()
|
|
630
620
|
await service.dispose()
|
|
631
621
|
}}
|
|
632
|
-
var
|
|
633
|
-
},required:["todos"],additionalProperties:false
|
|
634
|
-
}
|
|
635
|
-
var noNotify=async ()=>{}
|
|
636
|
-
var todo_server_default=Plugin.define({id:"opencode-navigator.todo-server",async setup(host){let notify=noNotify
|
|
637
|
-
const todos=createManagedTodos({get:(key)=>host.storage.get(key),set:(key,value)=>host.storage.set(key,value)
|
|
638
|
-
},(sessionID,state)=>notify(sessionID,state))
|
|
639
|
-
const rpc=await host.rpc.register(NavigatorTodoRpc,{list:async (value)=>todos.list(value.sessionID),replace:async (value)=>{const{sessionID,todos:items}=value
|
|
640
|
-
return todos.replace(sessionID,items)
|
|
641
|
-
},guidance:async (value)=>{const{sessionID,enabled}=value
|
|
642
|
-
return todos.guidance(sessionID,enabled)
|
|
643
|
-
}})
|
|
644
|
-
notify=(sessionID,state)=>rpc.events.emit("updated",{sessionID,...state})
|
|
645
|
-
const tools=await host.tool.transform((editor)=>{editor.namespace({name:"navigator",description:"Navigator session tasks"})
|
|
646
|
-
editor.add({name:"todo_write",description:"Replace the Navigator Todo list for this session with the current tasks and their statuses.",input,options:{namespace:"navigator"},execute:async (value,context)=>{const state=await todos.replace(context.sessionID,value.todos)
|
|
647
|
-
return{content:`Navigator Todo updated: ${state.todos.length} tasks.`}}})
|
|
648
|
-
})
|
|
649
|
-
const context=await host.session.hook("context",async (event)=>{const state=await todos.list(event.sessionID)
|
|
650
|
-
if(state.guidance)
|
|
651
|
-
event.system.push({type:"text",text:todoGuidance(state)})
|
|
622
|
+
var server_plugin_default=Plugin.define({id:"opencode-navigator.todo-server",async setup(host){const archive=await host.rpc.register(LegacyTodoArchiveRpc,{list:async (value)=>readLegacyTodoArchive((key)=>host.storage.get(key),value.sessionID)
|
|
652
623
|
})
|
|
653
624
|
const quotaCleanup=typeof host.integration?.connection?.active==="function"&&typeof host.provider?.get==="function"?await setupProviderQuotaServer(host):undefined
|
|
654
625
|
return async ()=>{await quotaCleanup?.()
|
|
655
|
-
await
|
|
656
|
-
await tools.dispose()
|
|
657
|
-
await rpc.dispose()
|
|
626
|
+
await archive.dispose()
|
|
658
627
|
}}})
|
|
659
|
-
export{
|
|
628
|
+
export{server_plugin_default as default
|
|
660
629
|
}
|
package/dist/settings-import.js
CHANGED
|
@@ -10,16 +10,17 @@ _$insertNode(_el$,_$createTextNode(`Paste versioned Navigator JSON.`))
|
|
|
10
10
|
_$effect((_$p)=>_$setProp(_el$,"fg",input.api.theme.current.textMuted,_$p))
|
|
11
11
|
return _el$
|
|
12
12
|
})(),onConfirm(source){const preview=input.preferences.previewPortableSettings(source)
|
|
13
|
-
const options=[...preview.settings.
|
|
13
|
+
const options=[...preview.settings.behavior?[{title:`Toolbar overflow \xB7 ${preview.behaviorChanged ? "CHANGED" : "UNCHANGED"}`,value:"status",description:preview.settings.behavior.toolbarOverflow??"unchanged",icon:"settings"
|
|
14
|
+
}]:[],...preview.settings.layout?[{title:`Layout \xB7 ${preview.layoutChanged ? "CHANGED" : "UNCHANGED"}`,value:"status",description:`${Object.keys(preview.settings.layout.sections).length} visibility \xB7 ${Object.keys(preview.settings.layout.expanded).length} expansion values`,icon:preview.layoutChanged?"edit":"done"
|
|
14
15
|
}]:[],...preview.settings.mcp?[{title:`MCP \xB7 ${preview.mcpChanged ? "CHANGED" : "UNCHANGED"}`,value:"status",description:`${Object.keys(preview.settings.mcp).length} remembered server states`,icon:preview.mcpChanged?"edit":"done"
|
|
15
16
|
}]:[],...preview.unsupported.map((path)=>({title:"Unsupported field \xB7 SKIPPED",value:"status",description:path,icon:"error"
|
|
16
|
-
})),...preview.layoutChanged||preview.mcpChanged?[{title:"Apply imported settings",value:"apply",description:preview.target,icon:"done"
|
|
17
|
+
})),...preview.layoutChanged||preview.mcpChanged||preview.behaviorChanged?[{title:"Apply imported settings",value:"apply",description:preview.target,icon:"done"
|
|
17
18
|
}]:[],{title:"Cancel",value:"cancel",description:"return without changes",icon:"close"
|
|
18
19
|
}]
|
|
19
20
|
let applying=false
|
|
20
21
|
async function apply(){applying=true
|
|
21
22
|
try{await input.preferences.applyPortableSettings(preview)
|
|
22
|
-
input.api.ui.toast({variant:"success",title:"Portable settings",message:"
|
|
23
|
+
input.api.ui.toast({variant:"success",title:"Portable settings",message:"Supported settings imported",duration:3000
|
|
23
24
|
})
|
|
24
25
|
input.dialogs.back()
|
|
25
26
|
} catch(error){applying=false
|