opencode-navigator 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,10 @@
1
1
  import {PLUGIN_ID
2
- } from "./chunk-bv0nm1tj.js";
2
+ } from "./chunk-bv0nm1tj.js"
3
+ import {parseChangelog2,readBundledChangelog2
4
+ } from "./chunk-pwa1wzer.js"
5
+ import {isNewerVersion,isSameMajorVersion
6
+ } from "./chunk-fd7394y1.js";
3
7
  import {createSignal} from "solid-js"
4
- function versionParts(value){const match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?/.exec(value)
5
- return match?{numbers:match.slice(1,4).map(Number),prerelease:match[4]||undefined}:undefined
6
- }
7
- function isNewerVersion(latest,current){const next=versionParts(latest)
8
- const installed=versionParts(current)
9
- if(!next||!installed)
10
- return false
11
- for(let index=0;index<3; index++){if(next.numbers[index]!==installed.numbers[index])
12
- return next.numbers[index]>installed.numbers[index]
13
- }
14
- return installed.prerelease!==undefined&&next.prerelease===undefined
15
- }
16
- function isSameMajorVersion(left,right){const first=versionParts(left)
17
- const second=versionParts(right)
18
- return Boolean(first&&second&&first.numbers[0]===second.numbers[0])
19
- }
20
8
  var NAVIGATOR_PACKAGE_URL="https://registry.npmjs.org/opencode-navigator/latest"
21
9
  var REQUEST_TIMEOUT_MS=5000
22
10
  async function readNavigatorVersion(request,signal){const response=await request(NAVIGATOR_PACKAGE_URL,{headers:{accept:"application/json"},signal})
@@ -29,6 +17,7 @@ return metadata.version
29
17
  }
30
18
  function createVersionStatus(api,navigatorVersion,request=fetch,timeoutMs=REQUEST_TIMEOUT_MS){const [openCodeUpdate,setOpenCodeUpdate]=createSignal()
31
19
  const [navigatorUpdate,setNavigatorUpdate]=createSignal()
20
+ const [navigatorCheckState,setNavigatorCheckState]=createSignal({status:"idle"})
32
21
  let controller
33
22
  let pending
34
23
  let disposed=false
@@ -43,6 +32,7 @@ function refreshNavigator(){if(disposed)
43
32
  return Promise.resolve("unavailable")
44
33
  if(pending)
45
34
  return pending
35
+ setNavigatorCheckState((previous)=>({...previous,status:"checking"}))
46
36
  const active=new AbortController
47
37
  controller=active
48
38
  const timeout=setTimeout(()=>active.abort(),timeoutMs)
@@ -54,8 +44,12 @@ const reading=Promise.race([readNavigatorVersion(request,active.signal),cancelle
54
44
  return "unavailable"
55
45
  const newer=isNewerVersion(version,navigatorVersion)
56
46
  setNavigatorUpdate(newer?version:undefined)
47
+ setNavigatorCheckState({status:newer?"available":"current",latest:version,checkedAt:Date.now()})
57
48
  return newer?"update":"current"
58
- }).catch(()=>"unavailable").finally(()=>{clearTimeout(timeout)
49
+ }).catch(()=>{if(!disposed)
50
+ setNavigatorCheckState((previous)=>({...previous,status:"error",checkedAt:Date.now()}))
51
+ return "unavailable"
52
+ }).finally(()=>{clearTimeout(timeout)
59
53
  active.signal.removeEventListener("abort",abort)
60
54
  if(controller===active)
61
55
  controller=undefined
@@ -66,7 +60,7 @@ pending=reading
66
60
  return reading
67
61
  }
68
62
  refreshNavigator()
69
- return{openCodeUpdate,navigatorUpdate,refreshNavigator}}
63
+ return{openCodeUpdate,navigatorUpdate,refreshNavigator,navigatorCheckState}}
70
64
  import {effect as _$effect3} from "@opentui/solid"
71
65
  import {insertNode as _$insertNode3} from "@opentui/solid"
72
66
  import {createComponent as _$createComponent2} from "@opentui/solid"
@@ -88,7 +82,8 @@ import {insert as _$insert} from "@opentui/solid"
88
82
  import {setProp as _$setProp} from "@opentui/solid"
89
83
  import {createElement as _$createElement} from "@opentui/solid"
90
84
  import {useIcons} from "./tui.js"
91
- import {createComponent as _$createComponent3} from "@opentui/solid"
85
+ import {createComponent as _$createComponent4} from "@opentui/solid"
86
+ import {createSignal as createSignal2} from "solid-js"
92
87
  function createManualUpdateCheck(api,status,current,onUpdate){let checking=false
93
88
  async function read(){try{return await status.refreshNavigator?.()??"unavailable"
94
89
  } catch{return "unavailable"
@@ -108,7 +103,28 @@ api.ui.toast({variant:result==="unavailable"?"warning":"info",title:"Navigator u
108
103
  })
109
104
  } finally{checking=false
110
105
  }}}}
106
+ import {createComponent as _$createComponent3} from "@opentui/solid"
107
+ import {onCleanup} from "solid-js"
108
+ import {GuardedConfirmation,SelectionMenu} from "./tui.js"
109
+ function NestedVersionConfirmation(props){onCleanup(props.dismiss)
110
+ return _$createComponent3(GuardedConfirmation,{get api(){return props.api
111
+ },get current(){return props.current
112
+ },get title(){return `Update ${props.name}`
113
+ },get message(){return `Install ${props.version} using the current installation method?`
114
+ },get onConfirm(){return props.confirm
115
+ },render:({confirm
116
+ })=>_$createComponent3(SelectionMenu,{get api(){return props.api
117
+ },get title(){return `Update ${props.name}`
118
+ },get options(){return [{title:"Cancel",value:"cancel",description:"Return without installing an update"
119
+ },{title:`Install ${props.name} ${props.version}`,value:"install",description:"Keep the current installation method and scope; restart OpenCode afterward"
120
+ }]
121
+ },onSelect:(option)=>option.value==="install"?confirm():props.cancel()
122
+ })
123
+ })
124
+ }
111
125
  function createVersionUpdateActions(api,status,updater,rememberNavigatorVersion){const busy=new Set
126
+ const [navigatorUpdating,setNavigatorUpdating]=createSignal2(false)
127
+ const [navigatorInstalled,setNavigatorInstalled]=createSignal2()
112
128
  const installed={}
113
129
  let disposed=false
114
130
  let confirmation=0
@@ -124,6 +140,8 @@ return
124
140
  }
125
141
  api.ui.dialog.clear()
126
142
  busy.add(kind)
143
+ if(kind==="navigator")
144
+ setNavigatorUpdating(true)
127
145
  api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing ${version}...`
128
146
  });
129
147
  (async ()=>{if(kind==="navigator")
@@ -132,6 +150,8 @@ if(disposed||version!==target(kind))
132
150
  throw new Error("Update target changed; check again")
133
151
  await updater[kind](version)
134
152
  })().then(()=>{installed[kind]=version
153
+ if(kind==="navigator")
154
+ setNavigatorInstalled(version)
135
155
  if(disposed)
136
156
  return
137
157
  api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart OpenCode to use the new version.",duration:6000
@@ -140,16 +160,34 @@ api.ui.toast({variant:"success",title:`${label(kind)} updated`,message:"Restart
140
160
  return
141
161
  api.ui.toast({variant:"error",title:`Could not update ${label(kind)}`,message:error instanceof Error?error.message:"Update failed",duration:6000
142
162
  })
143
- }).finally(()=>busy.delete(kind))
163
+ }).finally(()=>{busy.delete(kind)
164
+ if(kind==="navigator")
165
+ setNavigatorUpdating(false)
166
+ })
144
167
  }
145
- function open(kind,version=target(kind)){if(disposed||!version||version!==target(kind)||busy.has(kind))
168
+ function open(kind,version=target(kind),navigation){if(disposed||!version||version!==target(kind)||busy.has(kind))
146
169
  return
147
170
  if(installed[kind]===version){api.ui.toast({variant:"info",title:`${label(kind)} updated`,message:"Restart OpenCode to use the installed update."
148
171
  })
149
172
  return
150
173
  }
151
174
  const id=++confirmation
152
- api.ui.dialog.replace(()=>_$createComponent3(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
175
+ const onConfirm=()=>{if(id!==confirmation)
176
+ return
177
+ confirmation++
178
+ update(kind,version)
179
+ }
180
+ if(navigation){navigation.open(()=>_$createComponent4(NestedVersionConfirmation,{api,get name(){return label(kind)
181
+ },version,current:()=>!disposed&&id===confirmation&&version===target(kind),confirm:onConfirm,cancel:()=>{if(id!==confirmation)
182
+ return
183
+ confirmation++
184
+ navigation.back()
185
+ },dismiss:()=>{if(id===confirmation)
186
+ confirmation++
187
+ }}))
188
+ return
189
+ }
190
+ api.ui.dialog.replace(()=>_$createComponent4(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
153
191
  },message:`Update to ${version} using the current installation method? OpenCode must be restarted afterward.`,onConfirm:()=>{if(id!==confirmation)
154
192
  return
155
193
  confirmation++
@@ -169,7 +207,7 @@ const unregister=api.keymap.registerLayer({commands:[{name:`${PLUGIN_ID}.check-u
169
207
  api.lifecycle.onDispose(()=>{disposed=true
170
208
  unregister()
171
209
  })
172
- return{openCode:(version)=>open("openCode",version),navigator:(version)=>open("navigator",version)
210
+ return{openCode:(version)=>open("openCode",version),navigator:(version,navigation)=>open("navigator",version,navigation),navigatorUpdating,navigatorInstalled
173
211
  }}
174
212
  import {homedir} from "os"
175
213
  import {basename,dirname,isAbsolute,join} from "path"
@@ -241,5 +279,5 @@ if(item.source.target.startsWith("."))
241
279
  throw new Error("Unrecognized local Navigator source")
242
280
  await context.client.plugin.update({location:response.location,targets:[item.source.target]})
243
281
  }}}
244
- export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions
282
+ export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions,parseChangelog2 as parseChangelog,readBundledChangelog2 as readBundledChangelog
245
283
  }
@@ -0,0 +1,379 @@
1
+ import {SECTION_DEFINITIONS,PLUGIN_ID
2
+ } from "./chunk-bv0nm1tj.js"
3
+ import"./chunk-qmntexwm.js";
4
+ import {createComponent as _$createComponent3} from "@opentui/solid"
5
+ import {effect as _$effect4} from "@opentui/solid"
6
+ import {createTextNode as _$createTextNode3} from "@opentui/solid"
7
+ import {insertNode as _$insertNode3} from "@opentui/solid"
8
+ import {insert as _$insert4} from "@opentui/solid"
9
+ import {setProp as _$setProp4} from "@opentui/solid"
10
+ import {createElement as _$createElement4} from "@opentui/solid"
11
+ import {TextAttributes as TextAttributes3} from "@opentui/core"
12
+ import {Show,createSignal as createSignal2,onCleanup as onCleanup3} from "solid-js"
13
+ import {supportsSidebarSection as supportsSidebarSection2} from "./tui.js"
14
+ import {DialogSurface,useDialogs,useIcons as useIcons3} from "./tui.js"
15
+ import {hostCapabilities} from "./tui.js"
16
+ var ONBOARDING_STEPS=[{title:"A control center that stays compact",summary:"Navigator brings session state, search, and controls into OpenCode's existing sidebar.",icon:"help",features:[{icon:"sections",title:"Progressive disclosure",description:"Expand only the context you need right now."},{icon:"scope",title:"Current context",description:"Follow the active session, workspace, and worktree."},{icon:"actions",title:"Mouse and keyboard",description:"Use the same controls with either input method."}]
17
+ },{title:"Stay oriented while agents work",summary:"Keep current work visible without turning the sidebar into another dashboard.",icon:"todo",features:[{icon:"todo",title:"Todo filters",description:"Track active, finished, and cancelled tasks. Edit through chat and the built-in Todo tool."
18
+ },{icon:"subagents",title:"Live subagents",description:"See running, retrying, recent, and failed child sessions; open them directly."
19
+ },{icon:"info",title:"Provider limits",description:"Follow the selected provider and account allowance. Codex linking and banked resets require confirmation."
20
+ }]
21
+ },{title:"Find anything and launch Skills safely",summary:"Search complete source lists without expanding every sidebar section.",icon:"search",features:[{icon:"search",title:"Search Everything",description:"Search Skills, Subagents, MCP, and Actions in shared tabs."
22
+ },{icon:"search",title:"Full source lists",description:"Search beyond sidebar visibility and item limits."},{icon:"skills",title:"Skill confirmation",description:"Review a skill and its source before inserting its command."
23
+ }]
24
+ },{title:"Run actions and check language tools",summary:"Keep frequent commands and private toolbar shortcuts nearby; show only host-supported language-server status.",icon:"actions",features:[{icon:"actions",title:"Quick Actions",description:"Choose visibility and order for rename, timeline, copy, export, compaction and other available commands."
25
+ },{icon:"lsp",title:"Compact LSP status",description:"Spot connected and failed language servers at a glance."
26
+ },{icon:"settings",title:"Private pinned toolbar",description:"Pin Actions or Skills separately from favorites: zero to three icons, optional text, observed-state icons, More or wrapping."
27
+ },{icon:"settings",title:"Session permissions",description:"Toggle auto-approve with the supported OpenCode 1.x command."
28
+ }]
29
+ },{title:"Control MCP without losing context",summary:"Manage individual servers or entire working sets directly from Navigator.",icon:"mcp",features:[{icon:"mcp",title:"Live server controls",description:"Connect, disconnect, filter, and favorite servers."},{icon:"actions",title:"Bulk operations",description:"Activate a group heading to connect or disconnect its full eligible bucket; retry failures in place."
30
+ },{icon:"presets",title:"Safe presets",description:"Preview connections and disconnections before Apply."}]
31
+ },{title:"Make Navigator fit each workspace",summary:"Use the same controls with mouse or keyboard and keep only the context you need.",icon:"settings",features:[{icon:"sections",title:"Flexible layout",description:"Choose section visibility, order, expansion, and item limits."
32
+ },{icon:"presets",title:"Scoped presets",description:"Preview layouts globally or for a worktree. Reload private preferences across running sessions."
33
+ },{icon:"settings",title:"About and changelog",description:"Check versions and updates, reopen notes by version, and prepare an editable Bug report for gh or your browser."
34
+ },{icon:"info",title:"Readable controls",description:"Use Nerd Font icons with the corner font, disable multiline masks, or choose Text fallback. Open Settings with Ctrl+,."
35
+ }]
36
+ }]
37
+ function onboardingSteps(api){const capabilities=hostCapabilities(api)
38
+ return ONBOARDING_STEPS.map((step,index)=>{if(index===1&&capabilities.nativeTodo)
39
+ return{...step,features:[{icon:"todo",title:"Todo filters and enablement",description:"If native Todo is off, use Enable Todo and confirm its server-config change. Edit tasks through chat and todowrite."
40
+ },...step.features.slice(1)]
41
+ }
42
+ if(index===3)
43
+ return{...step,features:step.features.map((feature)=>{if(feature.icon==="lsp"&&!capabilities.lsp)
44
+ return{...feature,title:"Language tools on this host",description:"OpenCode 2.x does not expose LSP status or actions. Use project lint and compiler commands instead."
45
+ }
46
+ if(feature.title==="Session permissions"&&capabilities.permissionMode===false)
47
+ return{...feature,description:"The Permissions action opens host Settings; choose Session \u2192 Permissions there."
48
+ }
49
+ return feature
50
+ })
51
+ }
52
+ return step
53
+ })
54
+ }
55
+ import {effect as _$effect2} from "@opentui/solid"
56
+ import {memo as _$memo} from "@opentui/solid"
57
+ import {createComponent as _$createComponent} from "@opentui/solid"
58
+ import {createTextNode as _$createTextNode} from "@opentui/solid"
59
+ import {insertNode as _$insertNode} from "@opentui/solid"
60
+ import {insert as _$insert2} from "@opentui/solid"
61
+ import {setProp as _$setProp2} from "@opentui/solid"
62
+ import {createElement as _$createElement2} from "@opentui/solid"
63
+ import {TextAttributes} from "@opentui/core"
64
+ import {For} from "solid-js"
65
+ import {SelectionBox,useIcons} from "./tui.js"
66
+ import {useRenderer} from "@opentui/solid"
67
+ import {createSignal,onCleanup} from "solid-js"
68
+ function useWizardHeight(api){const renderer=api.renderer??useRenderer()
69
+ const [height,setHeight]=createSignal(renderer.height)
70
+ const resize=()=>setHeight(renderer.height)
71
+ renderer.on("resize",resize)
72
+ onCleanup(()=>renderer.off("resize",resize))
73
+ return height
74
+ }
75
+ import {effect as _$effect} from "@opentui/solid"
76
+ import {insert as _$insert} from "@opentui/solid"
77
+ import {use as _$use} from "@opentui/solid"
78
+ import {setProp as _$setProp} from "@opentui/solid"
79
+ import {createElement as _$createElement} from "@opentui/solid"
80
+ import {useRenderer as useRenderer2} from "@opentui/solid"
81
+ import {createEffect,onCleanup as onCleanup2} from "solid-js"
82
+ function WizardScrollBody(props){const renderer=useRenderer2()
83
+ let body
84
+ let pending=true
85
+ createEffect(()=>{pending=props.active!==undefined||props.resetKey!==undefined
86
+ })
87
+ const reveal=()=>{if(!pending||!body||body.isDestroyed||body.scrollHeight===0)
88
+ return
89
+ pending=false
90
+ if(props.active)
91
+ body.scrollChildIntoView(props.active)
92
+ else if(props.resetKey)
93
+ body.scrollTo(0)
94
+ }
95
+ renderer.on("frame",reveal)
96
+ const unregister=props.api.keymap.registerLayer({mode:"modal",priority:1001,commands:[{name:"opencode-navigator.wizard.body.up",run:()=>body?.scrollBy(-Math.max(1,props.height-1))
97
+ },{name:"opencode-navigator.wizard.body.down",run:()=>body?.scrollBy(Math.max(1,props.height-1))
98
+ }],bindings:[{key:"pageup",cmd:"opencode-navigator.wizard.body.up"
99
+ },{key:"pagedown",cmd:"opencode-navigator.wizard.body.down"
100
+ }]
101
+ })
102
+ onCleanup2(()=>{unregister()
103
+ renderer.off("frame",reveal)
104
+ })
105
+ return(()=>{var _el$=_$createElement("scrollbox")
106
+ _$use((node)=>{body=node
107
+ },_el$)
108
+ _$setProp(_el$,"id","opencode-navigator.wizard.body")
109
+ _$setProp(_el$,"flexShrink",0)
110
+ _$setProp(_el$,"scrollX",false)
111
+ _$setProp(_el$,"horizontalScrollbarOptions",{visible:false
112
+ })
113
+ _$insert(_el$,()=>props.children)
114
+ _$effect((_$p)=>_$setProp(_el$,"height",props.height,_$p))
115
+ return _el$
116
+ })()
117
+ }
118
+ function OnboardingStep(props){const icons=useIcons()
119
+ const theme=()=>props.api.theme.current
120
+ const height=useWizardHeight(props.api)
121
+ return(()=>{var _el$=_$createElement2("box"),_el$1=_$createElement2("box")
122
+ _$insertNode(_el$,_el$1)
123
+ _$setProp2(_el$,"gap",1)
124
+ _$insert2(_el$,_$createComponent(WizardScrollBody,{get api(){return props.api
125
+ },get height(){return Math.min(16,Math.max(3,height()-11))
126
+ },get resetKey(){return props.step.title
127
+ },get children(){var _el$2=_$createElement2("box"),_el$3=_$createElement2("text"),_el$4=_$createTextNode(` `),_el$5=_$createElement2("text"),_el$6=_$createElement2("box"),_el$7=_$createElement2("text"),_el$8=_$createTextNode(`Navigator: `),_el$9=_$createTextNode(` \xB7 Focus: `),_el$0=_$createTextNode(` \xB7 Settings: Ctrl+, \xB7 Icons: `)
128
+ _$insertNode(_el$2,_el$3)
129
+ _$insertNode(_el$2,_el$5)
130
+ _$insertNode(_el$2,_el$6)
131
+ _$insertNode(_el$2,_el$7)
132
+ _$setProp2(_el$2,"gap",1)
133
+ _$insertNode(_el$3,_el$4)
134
+ _$insert2(_el$3,()=>icons.icon(props.step.icon),_el$4)
135
+ _$insert2(_el$3,()=>props.step.title,null)
136
+ _$setProp2(_el$5,"wrapMode","word")
137
+ _$insert2(_el$5,()=>props.step.summary)
138
+ _$setProp2(_el$6,"border",["top","bottom"])
139
+ _$setProp2(_el$6,"paddingTop",1)
140
+ _$setProp2(_el$6,"paddingBottom",1)
141
+ _$setProp2(_el$6,"gap",1)
142
+ _$insert2(_el$6,_$createComponent(For,{get each(){return props.step.features
143
+ },children:(feature)=>(()=>{var _el$14=_$createElement2("box"),_el$15=_$createElement2("text"),_el$16=_$createElement2("text"),_el$17=_$createElement2("b"),_el$18=_$createTextNode(` \xB7 `)
144
+ _$insertNode(_el$14,_el$15)
145
+ _$insertNode(_el$14,_el$16)
146
+ _$setProp2(_el$14,"flexDirection","row")
147
+ _$setProp2(_el$14,"gap",1)
148
+ _$setProp2(_el$15,"flexShrink",0)
149
+ _$insert2(_el$15,()=>icons.icon(feature.icon))
150
+ _$insertNode(_el$16,_el$17)
151
+ _$insertNode(_el$16,_el$18)
152
+ _$setProp2(_el$16,"flexGrow",1)
153
+ _$setProp2(_el$16,"minWidth",0)
154
+ _$setProp2(_el$16,"wrapMode","word")
155
+ _$insert2(_el$17,()=>feature.title)
156
+ _$insert2(_el$16,()=>feature.description,null)
157
+ _$effect2((_p$)=>{var _v$6=theme().accent,_v$7=theme().textMuted
158
+ _v$6!==_p$.e&&(_p$.e=_$setProp2(_el$15,"fg",_v$6,_p$.e))
159
+ _v$7!==_p$.t&&(_p$.t=_$setProp2(_el$16,"fg",_v$7,_p$.t))
160
+ return _p$
161
+ },{e:undefined,t:undefined
162
+ })
163
+ return _el$14
164
+ })()
165
+ }))
166
+ _$insertNode(_el$7,_el$8)
167
+ _$insertNode(_el$7,_el$9)
168
+ _$insertNode(_el$7,_el$0)
169
+ _$setProp2(_el$7,"wrapMode","word")
170
+ _$insert2(_el$7,()=>props.preferences.toggleKey(),_el$9)
171
+ _$insert2(_el$7,()=>props.preferences.focusKey(),_el$0)
172
+ _$insert2(_el$7,()=>icons.style()==="text"?"Text fallback":"Nerd Font",null)
173
+ _$effect2((_p$)=>{var _v$=theme().accent,_v$2=TextAttributes.BOLD,_v$3=theme().textMuted,_v$4=theme().borderSubtle,_v$5=theme().textMuted
174
+ _v$!==_p$.e&&(_p$.e=_$setProp2(_el$3,"fg",_v$,_p$.e))
175
+ _v$2!==_p$.t&&(_p$.t=_$setProp2(_el$3,"attributes",_v$2,_p$.t))
176
+ _v$3!==_p$.a&&(_p$.a=_$setProp2(_el$5,"fg",_v$3,_p$.a))
177
+ _v$4!==_p$.o&&(_p$.o=_$setProp2(_el$6,"borderColor",_v$4,_p$.o))
178
+ _v$5!==_p$.i&&(_p$.i=_$setProp2(_el$7,"fg",_v$5,_p$.i))
179
+ return _p$
180
+ },{e:undefined,t:undefined,a:undefined,o:undefined,i:undefined
181
+ })
182
+ return _el$2
183
+ }}),_el$1)
184
+ _$setProp2(_el$1,"flexDirection","row")
185
+ _$setProp2(_el$1,"justifyContent","space-between")
186
+ _$insert2(_el$1,_$createComponent(SelectionBox,{id:"opencode-navigator.wizard.back",paddingLeft:1,paddingRight:1,onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
187
+ props.onBack()
188
+ },get children(){var _el$10=_$createElement2("text"),_el$11=_$createTextNode(` Back`)
189
+ _$insertNode(_el$10,_el$11)
190
+ _$insert2(_el$10,()=>icons.icon("left"),_el$11)
191
+ _$effect2((_$p)=>_$setProp2(_el$10,"fg",theme().textMuted,_$p))
192
+ return _el$10
193
+ }}),null)
194
+ _$insert2(_el$1,_$createComponent(SelectionBox,{id:"opencode-navigator.wizard.next",paddingLeft:1,paddingRight:1,get backgroundColor(){return theme().primary
195
+ },onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
196
+ props.onAdvance()
197
+ },get children(){var _el$12=_$createElement2("text"),_el$13=_$createTextNode(` `)
198
+ _$insertNode(_el$12,_el$13)
199
+ _$insert2(_el$12,()=>props.final?"Configure sidebar":"Next",_el$13)
200
+ _$insert2(_el$12,()=>icons.icon("right"),null)
201
+ _$effect2((_$p)=>_$setProp2(_el$12,"fg",theme().selectedListItemText,_$p))
202
+ return _el$12
203
+ }}),null)
204
+ return _el$
205
+ })()
206
+ }
207
+ import {effect as _$effect3} from "@opentui/solid"
208
+ import {createComponent as _$createComponent2} from "@opentui/solid"
209
+ import {memo as _$memo2} from "@opentui/solid"
210
+ import {createTextNode as _$createTextNode2} from "@opentui/solid"
211
+ import {insertNode as _$insertNode2} from "@opentui/solid"
212
+ import {insert as _$insert3} from "@opentui/solid"
213
+ import {setProp as _$setProp3} from "@opentui/solid"
214
+ import {createElement as _$createElement3} from "@opentui/solid"
215
+ import {TextAttributes as TextAttributes2} from "@opentui/core"
216
+ import {For as For2} from "solid-js"
217
+ import {supportsSidebarSection} from "./tui.js"
218
+ import {SelectionBox as SelectionBox2,useIcons as useIcons2} from "./tui.js"
219
+ function SectionConfig(props){const icons=useIcons2()
220
+ const theme=()=>props.api.theme.current
221
+ const sections=SECTION_DEFINITIONS.filter((section)=>supportsSidebarSection(props.api,section.name))
222
+ const finishIndex=sections.length
223
+ const height=useWizardHeight(props.api)
224
+ return(()=>{var _el$=_$createElement3("box"),_el$2=_$createElement3("text"),_el$3=_$createTextNode2(` Choose your sidebar sections`),_el$4=_$createElement3("text"),_el$7=_$createElement3("box")
225
+ _$insertNode2(_el$,_el$2)
226
+ _$insertNode2(_el$,_el$4)
227
+ _$insertNode2(_el$,_el$7)
228
+ _$setProp3(_el$,"gap",1)
229
+ _$insertNode2(_el$2,_el$3)
230
+ _$setProp3(_el$2,"flexShrink",0)
231
+ _$insert3(_el$2,()=>icons.icon("sections"),_el$3)
232
+ _$insertNode2(_el$4,_$createTextNode2(`Select what appears now. You can change visibility, order, limits, scope, and presets later with the gear button.`))
233
+ _$setProp3(_el$4,"wrapMode","word")
234
+ _$setProp3(_el$4,"flexShrink",0)
235
+ _$insert3(_el$,_$createComponent2(WizardScrollBody,{get api(){return props.api
236
+ },get height(){return Math.min(sections.length*2-1,Math.max(3,height()-15))
237
+ },get active(){return _$memo2(()=>props.active<sections.length)()?`opencode-navigator.wizard.section.${props.active}`:undefined
238
+ },get children(){var _el$6=_$createElement3("box")
239
+ _$setProp3(_el$6,"gap",1)
240
+ _$insert3(_el$6,_$createComponent2(For2,{each:sections,children:(section,index)=>_$createComponent2(SelectionBox2,{get id(){return `opencode-navigator.wizard.section.${index()}`
241
+ },height:1,flexShrink:0,flexDirection:"row",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return _$memo2(()=>props.active===index())()?theme().backgroundElement:undefined
242
+ },onMouseOver:()=>props.onActive(index()),onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
243
+ props.preferences.toggleSection(section.name)
244
+ },get children(){return [(()=>{var _el$10=_$createElement3("text")
245
+ _$setProp3(_el$10,"flexShrink",0)
246
+ _$insert3(_el$10,()=>icons.icon(props.preferences.sections()[section.name]?"checked":"unchecked"))
247
+ _$effect3((_$p)=>_$setProp3(_el$10,"fg",props.preferences.sections()[section.name]?theme().accent:theme().textMuted,_$p))
248
+ return _el$10
249
+ })(),(()=>{var _el$11=_$createElement3("text"),_el$12=_$createTextNode2(` `)
250
+ _$insertNode2(_el$11,_el$12)
251
+ _$insert3(_el$11,()=>icons.section(section.name),_el$12)
252
+ _$insert3(_el$11,()=>section.label,null)
253
+ _$effect3((_$p)=>_$setProp3(_el$11,"fg",props.active===index()?theme().text:theme().textMuted,_$p))
254
+ return _el$11
255
+ })()]
256
+ }})
257
+ }))
258
+ return _el$6
259
+ }}),_el$7)
260
+ _$setProp3(_el$7,"flexDirection","row")
261
+ _$setProp3(_el$7,"justifyContent","space-between")
262
+ _$setProp3(_el$7,"flexShrink",0)
263
+ _$insert3(_el$7,_$createComponent2(SelectionBox2,{id:"opencode-navigator.wizard.back",paddingLeft:1,paddingRight:1,onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
264
+ props.onBack()
265
+ },get children(){var _el$8=_$createElement3("text"),_el$9=_$createTextNode2(` Back`)
266
+ _$insertNode2(_el$8,_el$9)
267
+ _$insert3(_el$8,()=>icons.icon("left"),_el$9)
268
+ _$effect3((_$p)=>_$setProp3(_el$8,"fg",theme().textMuted,_$p))
269
+ return _el$8
270
+ }}),null)
271
+ _$insert3(_el$7,_$createComponent2(SelectionBox2,{id:"opencode-navigator.wizard.finish",paddingLeft:2,paddingRight:2,get backgroundColor(){return _$memo2(()=>props.active===finishIndex)()?theme().primary:undefined
272
+ },onMouseOver:()=>props.onActive(finishIndex),onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
273
+ props.onFinish()
274
+ },get children(){var _el$0=_$createElement3("text"),_el$1=_$createTextNode2(` Finish`)
275
+ _$insertNode2(_el$0,_el$1)
276
+ _$insert3(_el$0,()=>icons.icon("done"),_el$1)
277
+ _$effect3((_$p)=>_$setProp3(_el$0,"fg",props.active===finishIndex?theme().selectedListItemText:theme().textMuted,_$p))
278
+ return _el$0
279
+ }}),null)
280
+ _$effect3((_p$)=>{var _v$=theme().accent,_v$2=TextAttributes2.BOLD,_v$3=theme().textMuted
281
+ _v$!==_p$.e&&(_p$.e=_$setProp3(_el$2,"fg",_v$,_p$.e))
282
+ _v$2!==_p$.t&&(_p$.t=_$setProp3(_el$2,"attributes",_v$2,_p$.t))
283
+ _v$3!==_p$.a&&(_p$.a=_$setProp3(_el$4,"fg",_v$3,_p$.a))
284
+ return _p$
285
+ },{e:undefined,t:undefined,a:undefined
286
+ })
287
+ return _el$
288
+ })()
289
+ }
290
+ function FirstRunWizard(props){const icons=useIcons3()
291
+ const dialogs=useDialogs(props.api)
292
+ const [active,setActive]=createSignal2(0)
293
+ const [step,setStep]=createSignal2(0)
294
+ const theme=()=>props.api.theme.current
295
+ const sections=SECTION_DEFINITIONS.filter((section)=>supportsSidebarSection2(props.api,section.name))
296
+ const steps=onboardingSteps(props.api)
297
+ const finishIndex=sections.length
298
+ const stepCount=steps.length + 1
299
+ const configuring=()=>step()===steps.length
300
+ function finish(){dialogs.back()
301
+ }
302
+ function move(offset){if(configuring())
303
+ setActive((value)=>(value + offset + finishIndex + 1)%(finishIndex + 1))
304
+ else
305
+ setStep((value)=>Math.max(0,Math.min(steps.length,value + offset)))
306
+ }
307
+ function back(){setStep((value)=>Math.max(0,value-1))
308
+ }
309
+ function advance(){if(!configuring())
310
+ setStep((value)=>Math.min(steps.length,value + 1))
311
+ }
312
+ function select(){if(!configuring()){advance()
313
+ return
314
+ }
315
+ const index=active()
316
+ if(index===finishIndex)
317
+ finish()
318
+ else
319
+ props.preferences.toggleSection(sections[index].name)
320
+ }
321
+ const unregister=props.api.keymap.registerLayer({mode:"modal",priority:1000,commands:[{name:`${PLUGIN_ID}.wizard.previous`,run(){move(-1)
322
+ }},{name:`${PLUGIN_ID}.wizard.next`,run(){move(1)
323
+ }},{name:`${PLUGIN_ID}.wizard.select`,run:select
324
+ },{name:`${PLUGIN_ID}.wizard.back`,run:back
325
+ },{name:`${PLUGIN_ID}.wizard.forward`,run:advance
326
+ }],bindings:[{key:"up",cmd:`${PLUGIN_ID}.wizard.previous`
327
+ },{key:"down",cmd:`${PLUGIN_ID}.wizard.next`
328
+ },{key:"tab",cmd:`${PLUGIN_ID}.wizard.next`
329
+ },{key:"left",cmd:`${PLUGIN_ID}.wizard.back`
330
+ },{key:"right",cmd:`${PLUGIN_ID}.wizard.forward`
331
+ },{key:"space",cmd:`${PLUGIN_ID}.wizard.select`
332
+ },{key:"return",cmd:`${PLUGIN_ID}.wizard.select`
333
+ }]
334
+ })
335
+ onCleanup3(unregister)
336
+ return _$createComponent3(DialogSurface,{get api(){return props.api
337
+ },lift:true,get children(){return [(()=>{var _el$=_$createElement4("box"),_el$2=_$createElement4("text"),_el$3=_$createTextNode3(` Navigator setup`),_el$4=_$createElement4("text"),_el$5=_$createTextNode3(`Step `),_el$6=_$createTextNode3(`/`),_el$7=_$createTextNode3(` \xB7 `)
338
+ _$insertNode3(_el$,_el$2)
339
+ _$insertNode3(_el$,_el$4)
340
+ _$setProp4(_el$,"id","opencode-navigator.wizard.header")
341
+ _$setProp4(_el$,"flexDirection","row")
342
+ _$setProp4(_el$,"justifyContent","space-between")
343
+ _$insertNode3(_el$2,_el$3)
344
+ _$insert4(_el$2,()=>icons.icon("help"),_el$3)
345
+ _$insertNode3(_el$4,_el$5)
346
+ _$insertNode3(_el$4,_el$6)
347
+ _$insertNode3(_el$4,_el$7)
348
+ _$setProp4(_el$4,"onMouseDown",(event)=>event.stopPropagation())
349
+ _$setProp4(_el$4,"onMouseUp",(event)=>{event.stopPropagation()
350
+ finish()
351
+ })
352
+ _$insert4(_el$4,()=>step() + 1,_el$6)
353
+ _$insert4(_el$4,stepCount,_el$7)
354
+ _$insert4(_el$4,()=>icons.key("esc"),null)
355
+ _$effect4((_p$)=>{var _v$=TextAttributes3.BOLD,_v$2=theme().text,_v$3=theme().textMuted
356
+ _v$!==_p$.e&&(_p$.e=_$setProp4(_el$2,"attributes",_v$,_p$.e))
357
+ _v$2!==_p$.t&&(_p$.t=_$setProp4(_el$2,"fg",_v$2,_p$.t))
358
+ _v$3!==_p$.a&&(_p$.a=_$setProp4(_el$4,"fg",_v$3,_p$.a))
359
+ return _p$
360
+ },{e:undefined,t:undefined,a:undefined
361
+ })
362
+ return _el$
363
+ })(),_$createComponent3(Show,{get when(){return configuring()
364
+ },get fallback(){return _$createComponent3(OnboardingStep,{get api(){return props.api
365
+ },get preferences(){return props.preferences
366
+ },get step(){return steps[step()]
367
+ },get final(){return step()===steps.length-1
368
+ },onBack:back,onAdvance:advance
369
+ })
370
+ },get children(){return _$createComponent3(SectionConfig,{get api(){return props.api
371
+ },get preferences(){return props.preferences
372
+ },get active(){return active()
373
+ },onActive:setActive,onBack:back,onFinish:finish
374
+ })
375
+ }})]
376
+ }})
377
+ }
378
+ export{FirstRunWizard
379
+ }
@@ -589,10 +589,10 @@ _$setProp3(_el$,"width","100%")
589
589
  _$setProp3(_el$,"minWidth",0)
590
590
  _$setProp3(_el$,"gap",1)
591
591
  _$setProp3(_el$,"overflow","hidden")
592
- _$insert3(_el$,_$createComponent4(For,{get each(){return props.windows
593
- },children:(window)=>_$createComponent4(QuotaWindowRow,{get api(){return props.api
594
- },window
595
- })
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
+ }})
596
596
  }))
597
597
  return _el$
598
598
  })()
@@ -644,7 +644,7 @@ listeners.clear()
644
644
  return{adapter,onUpdated(listener){listeners.add(listener)
645
645
  return()=>listeners.delete(listener)
646
646
  }}}
647
- var rows=[["openai","OpenAI / Codex",["subscription","api-rate-limits"],"Verified Codex account quota shared across models; HTTP response request/token limits for API connections.","https://developers.openai.com/api/docs/guides/rate-limits"],["anthropic","Anthropic / Claude",["api-rate-limits","configured-limits"],"Model HTTP headers; eligible organization/admin credentials can read configured model limits. Claude subscription windows are separate.","https://platform.claude.com/docs/en/manage-claude/rate-limits-api"],["openrouter","OpenRouter",["key-budget","account-balance"],"Inference-key budget; account credits require a management key. Free-model request limits apply only to free variants.","https://openrouter.ai/docs/api/api-reference/api-keys/get-current-api-key"],["deepseek","DeepSeek",["account-balance"],"Provider-reported USD/CNY available, granted and topped-up balances.","https://api-docs.deepseek.com/api/get-user-balance"],["moonshotai","Moonshot / Kimi API",["account-balance"],"International USD balances; coding subscriptions are a different product.","https://platform.moonshot.ai/docs/api/balance"],["moonshotai-cn","Moonshot / Kimi API China",["account-balance"],"China CNY balances; never reuse an international key against the China endpoint.","https://platform.moonshot.cn/docs/api/balance"],["minimax","MiniMax",["subscription","account-balance"],"Token Plan model rows/explicit percentages; official SDK balance response for pay-as-you-go keys.","https://platform.minimax.io/docs/token-plan/faq"],["minimax-cn","MiniMax China",["subscription","account-balance"],"Region-specific native Token Plan and SDK balance endpoints.","https://github.com/MiniMax-AI/cli/tree/main/src/sdk/quota"],["github-copilot","GitHub Copilot",["subscription"],"Official SDK token-scoped quota/model RPC; no direct copilot_internal calls or ambient CLI login.","https://github.com/github/copilot-sdk/blob/main/docs/features/usage-and-billing.md"],["groq","Groq",["api-rate-limits"],"Native model response headers: requests per day and tokens per minute.","https://console.groq.com/docs/rate-limits"],["fireworks-ai","Fireworks",["configured-limits","api-rate-limits"],"Native model token/second capacities and account monthly spend quota; GPU quotas are excluded.","https://docs.fireworks.ai/serverless/rate-limits"],["siliconflow","SiliconFlow",["account-balance"],"Native current-user balances; retain unknown currency when the API does not report it.","https://docs.siliconflow.com/en/api-reference/userinfo/get-user-info"],["siliconflow-cn","SiliconFlow China",["account-balance"],"Region-specific current-user balances; currency is unknown when the API omits it.","https://docs.siliconflow.cn/"],["novita-ai","Novita",["account-balance"],"Native current-user balances in 1/10000 USD.","https://novita.ai/docs/api-reference/basic-get-user-balance"],["poe","Poe",["account-balance"],"API account point balance; includes plan and add-on points, not session token usage.","https://creator.poe.com/docs/resources/usage-api"],["nano-gpt","NanoGPT",["account-balance","subscription"],"Native USD/Nano balance and documented subscription token windows; null/degraded counters remain unknown.","https://docs.nano-gpt.com/api-reference/endpoint/subscription-usage"],["aihubmix","AIHubMix",["account-balance"],"Current-account balance API needs a Manage Key; inference keys do not grant management access.","https://docs.aihubmix.com/en/api/CliEndpoints/get-self"],["azure","Azure OpenAI",["api-rate-limits"],"Remaining request/token headers for the actual deployment; no ChatGPT subscription or inferred cloud balance.","https://learn.microsoft.com/en-us/azure/ai-foundry/openai/quotas-limits"],["cerebras","Cerebras",["api-rate-limits"],"Only self-describing native counters actually returned for the selected model; console tier examples are not live quota.","https://inference-docs.cerebras.ai/support/rate-limits"],["mistral","Mistral",[],"Documented remaining header lacks a verified unit/window association; no inferred subscription balance.","https://docs.mistral.ai/resources/known-limitations"],["togetherai","Together AI",[],"Dynamic model limits expose retry intervals, not remaining quota; billing UI does not establish a remaining-credit API.","https://docs.together.ai/docs/rate-limits"],["xai","xAI / Grok",[],"Management billing needs separate team credentials; no verified portable remaining API for this inference connection.","https://docs.x.ai/"],["perplexity","Perplexity",[],"No verified remaining-credit API for an inference key. API credits, consumer subscriptions and Computer credits are distinct.","https://www.perplexity.ai/help-center/en/articles/10354847-api-payment-and-billing"],["google","Google Gemini",["configured-limits"],"Native model-matched QuotaFailure responses when provided; cloud quota permissions differ from an inference key.","https://ai.google.dev/gemini-api/docs/rate-limits"],["google-vertex","Google Vertex AI",["configured-limits"],"Native model-matched quota error details; cloud project/region management and dynamic shared quotas are separate.","https://cloud.google.com/vertex-ai/generative-ai/docs/quotas"],["amazon-bedrock","Amazon Bedrock",[],"Service Quotas/CloudWatch require cloud IAM/region/model mapping not exposed by a portable inference connection.","https://docs.aws.amazon.com/bedrock/latest/userguide/quotas.html"],["cloudflare-workers-ai","Cloudflare Workers AI",[],"Account analytics do not prove remaining free allowance; separate account management capability is required.","https://developers.cloudflare.com/workers-ai/platform/limits/"],["huggingface","Hugging Face Inference Providers",[],"Hub request quota is not an inference-model quota or inference-credit balance.","https://huggingface.co/docs/inference-providers/pricing"],["zai","Z.ai / GLM Coding Plan",[],"No verified external quota contract for monitor/dashboard APIs; private endpoints are excluded.","https://docs.z.ai/devpack/overview"],["kimi-for-coding","Kimi Code subscription",[],"Keep separate from Moonshot API balance; an external subscription usage contract/model mapping must be verified.","https://www.kimi.com/code/docs/en/"],["alibaba","Alibaba Cloud / Qwen / DashScope",[],"Cloud account/project billing permissions and model mapping are required; browser OAuth state is not a quota source.","https://www.alibabacloud.com/help/en/model-studio/"],["opencode","OpenCode Console / Go",["workspace-budget","subscription"],"OpenCode 2.2 public usage API: applicable workspace USD budgets and percent-based Go allowance; not a universal account balance API.","https://github.com/anomalyco/opencode/blob/v2.2.0/packages/schema/src/usage-limits.ts"],["cursor","Cursor",[],"Consumer usage needs a supported external API; no cookie or credential-file integrations.","https://docs.cursor.com/"],["windsurf","Windsurf / Codeium",[],"Consumer plan windows need a supported external API; no private account endpoints.","https://docs.windsurf.com/"],["amp","Amp",[],"Product credits need a supported external account API; no cookie or local credential extraction.","https://ampcode.com/manual"],["ollama","Ollama",[],"Local inference has no provider subscription window; cloud products require their own account API.","https://docs.ollama.com/api"],["lmstudio","LM Studio",[],"Local inference has no provider subscription quota to read.","https://lmstudio.ai/docs/developer"],["nvidia","NVIDIA NIM / NGC",[],"Inference access does not identify cloud/trial NGC credit balance or self-hosted resource quota.","https://docs.nvidia.com/nim/"],["cohere","Cohere",[],"No verified native remaining-counter contract for this integration; static organization tier examples are not live quotas.","https://docs.cohere.com/docs/rate-limits"],["ai21","AI21",[],"No verified native remaining-counter contract or subscription balance endpoint for this integration.","https://docs.ai21.com/"],["replicate","Replicate",[],"Prediction cost is not remaining account credit; no inferred balance from execution cost.","https://replicate.com/docs/topics/billing"],["chutes","Chutes",[],"Current-user quota APIs exist, but their public schemas do not establish native units/model allowance semantics yet.","https://chutes.ai/docs/api-reference/users"],["requesty","Requesty",[],"Enterprise management key and key ID are required; dated usage reports alone are not remaining budget.","https://docs.requesty.ai/features/key-management-api"],["baseten","Baseten",[],"No independently verified portable remaining quota API for the configured inference connection.","https://docs.baseten.co/"],["modelscope","ModelScope",[],"No independently verified portable remaining quota API/model mapping for the selected connection.","https://opencode.ai/docs/providers/"],["nebius","Nebius",[],"Cloud billing/management access is separate from a plain inference credential.","https://docs.nebius.com/"],["vultr","Vultr",[],"Cloud management quota/billing permissions are separate from inference access.","https://docs.vultr.com/"],["v0","Vercel v0",[],"Consumer/product credits need their own documented external account API.","https://v0.dev/docs"]]
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"]]
648
648
  var PROVIDER_QUOTA_CAPABILITIES= rows.map(([id,name,sources,reason,url])=>({id,name,sources,reason,documentation:[url]}))
649
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"
650
650
  }
@@ -667,7 +667,10 @@ return [item.type,item.name,item.status]
667
667
  const key=()=>JSON.stringify([location(),connection(),revision()])
668
668
  const invalidate=()=>{generation++
669
669
  }
670
- const disposers=[context.data.on("credential.updated",invalidate),context.data.on("integration.updated",invalidate),context.data.on("credential.switched",(event)=>{if(event.data.integrationID!=="opencode")
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")
671
674
  return
672
675
  generation++
673
676
  setRevision((value)=>value + 1)
@@ -1172,7 +1175,11 @@ if(lastModel&&!disposed)
1172
1175
  read(lastModel)
1173
1176
  }
1174
1177
  const events=["credential.switched","credential.updated","integration.updated","provider.updated","server.connected"]
1175
- const unsubscribes=events.map((event)=>context.data.on(event,()=>untrack2(()=>invalidate(event!=="provider.updated"))))
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
+ })))
1176
1183
  return{suspend(){if(!lastModel)
1177
1184
  return
1178
1185
  lastModel=undefined