opencode-navigator 0.27.1 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,29 +61,38 @@ return reading
61
61
  }
62
62
  refreshNavigator()
63
63
  return{openCodeUpdate,navigatorUpdate,refreshNavigator,navigatorCheckState}}
64
- import {effect as _$effect3} from "@opentui/solid"
64
+ import {effect as _$effect4} from "@opentui/solid"
65
+ import {insertNode as _$insertNode4} from "@opentui/solid"
66
+ import {createComponent as _$createComponent3} from "@opentui/solid"
67
+ import {insert as _$insert4} from "@opentui/solid"
68
+ import {setProp as _$setProp4} from "@opentui/solid"
69
+ import {createElement as _$createElement4} from "@opentui/solid"
70
+ import {createTextNode as _$createTextNode3} from "@opentui/solid"
65
71
  import {insertNode as _$insertNode3} from "@opentui/solid"
66
- import {createComponent as _$createComponent2} from "@opentui/solid"
67
- import {insert as _$insert3} from "@opentui/solid"
68
72
  import {setProp as _$setProp3} from "@opentui/solid"
73
+ import {effect as _$effect3} from "@opentui/solid"
74
+ import {insert as _$insert3} from "@opentui/solid"
75
+ import {memo as _$memo2} from "@opentui/solid"
76
+ import {createComponent as _$createComponent2} from "@opentui/solid"
69
77
  import {createElement as _$createElement3} from "@opentui/solid"
70
78
  import {effect as _$effect2} from "@opentui/solid"
71
- import {insert as _$insert2} from "@opentui/solid"
72
79
  import {createTextNode as _$createTextNode2} from "@opentui/solid"
73
80
  import {insertNode as _$insertNode2} from "@opentui/solid"
74
81
  import {createComponent as _$createComponent} from "@opentui/solid"
82
+ import {insert as _$insert2} from "@opentui/solid"
83
+ import {memo as _$memo} from "@opentui/solid"
75
84
  import {setProp as _$setProp2} from "@opentui/solid"
76
85
  import {createElement as _$createElement2} from "@opentui/solid"
86
+ import {useIcons} from "./tui.js"
87
+ import {setProp as _$setProp} from "@opentui/solid"
77
88
  import {effect as _$effect} from "@opentui/solid"
78
- import {memo as _$memo} from "@opentui/solid"
79
89
  import {createTextNode as _$createTextNode} from "@opentui/solid"
80
90
  import {insertNode as _$insertNode} from "@opentui/solid"
81
91
  import {insert as _$insert} from "@opentui/solid"
82
- import {setProp as _$setProp} from "@opentui/solid"
83
92
  import {createElement as _$createElement} from "@opentui/solid"
84
- import {useIcons} from "./tui.js"
85
- import {createComponent as _$createComponent4} from "@opentui/solid"
86
- import {createSignal as createSignal2} from "solid-js"
93
+ import {createEffect,createSignal as createSignal2,onCleanup} from "solid-js"
94
+ import {createComponent as _$createComponent5} from "@opentui/solid"
95
+ import {createSignal as createSignal3} from "solid-js"
87
96
  function createManualUpdateCheck(api,status,current,onUpdate){let checking=false
88
97
  async function read(){try{return await status.refreshNavigator?.()??"unavailable"
89
98
  } catch{return "unavailable"
@@ -103,17 +112,17 @@ api.ui.toast({variant:result==="unavailable"?"warning":"info",title:"Navigator u
103
112
  })
104
113
  } finally{checking=false
105
114
  }}}}
106
- import {createComponent as _$createComponent3} from "@opentui/solid"
107
- import {onCleanup} from "solid-js"
115
+ import {createComponent as _$createComponent4} from "@opentui/solid"
116
+ import {onCleanup as onCleanup2} from "solid-js"
108
117
  import {GuardedConfirmation,SelectionMenu} from "./tui.js"
109
- function NestedVersionConfirmation(props){onCleanup(props.dismiss)
110
- return _$createComponent3(GuardedConfirmation,{get api(){return props.api
118
+ function NestedVersionConfirmation(props){onCleanup2(props.dismiss)
119
+ return _$createComponent4(GuardedConfirmation,{get api(){return props.api
111
120
  },get current(){return props.current
112
121
  },get title(){return `Update ${props.name}`
113
122
  },get message(){return `Install ${props.version} using the current installation method?`
114
123
  },get onConfirm(){return props.confirm
115
124
  },render:({confirm
116
- })=>_$createComponent3(SelectionMenu,{get api(){return props.api
125
+ })=>_$createComponent4(SelectionMenu,{get api(){return props.api
117
126
  },get title(){return `Update ${props.name}`
118
127
  },get options(){return [{title:"Cancel",value:"cancel",description:"Return without installing an update"
119
128
  },{title:`Install ${props.name} ${props.version}`,value:"install",description:"Keep the current installation method and scope; restart OpenCode afterward"
@@ -123,8 +132,9 @@ return _$createComponent3(GuardedConfirmation,{get api(){return props.api
123
132
  })
124
133
  }
125
134
  function createVersionUpdateActions(api,status,updater,rememberNavigatorVersion){const busy=new Set
126
- const [navigatorUpdating,setNavigatorUpdating]=createSignal2(false)
127
- const [navigatorInstalled,setNavigatorInstalled]=createSignal2()
135
+ const [navigatorUpdating,setNavigatorUpdating]=createSignal3(false)
136
+ const [navigatorInstalled,setNavigatorInstalled]=createSignal3()
137
+ const [progress,setProgress]=createSignal3({})
128
138
  const installed={}
129
139
  let disposed=false
130
140
  let confirmation=0
@@ -140,6 +150,8 @@ return
140
150
  }
141
151
  api.ui.dialog.clear()
142
152
  busy.add(kind)
153
+ setProgress((previous)=>({...previous,[kind]:{phase:"preparing"
154
+ }}))
143
155
  if(kind==="navigator")
144
156
  setNavigatorUpdating(true)
145
157
  api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing ${version}...`
@@ -148,7 +160,13 @@ api.ui.toast({variant:"info",title:`Updating ${label(kind)}`,message:`Installing
148
160
  await rememberNavigatorVersion?.()
149
161
  if(disposed||version!==target(kind))
150
162
  throw new Error("Update target changed; check again")
151
- await updater[kind](version)
163
+ const report=(value)=>{if(!disposed&&busy.has(kind))
164
+ setProgress((previous)=>({...previous,[kind]:value
165
+ }))
166
+ }
167
+ report({phase:"installing"
168
+ })
169
+ await updater[kind](version,report)
152
170
  })().then(()=>{installed[kind]=version
153
171
  if(kind==="navigator")
154
172
  setNavigatorInstalled(version)
@@ -161,6 +179,8 @@ return
161
179
  api.ui.toast({variant:"error",title:`Could not update ${label(kind)}`,message:error instanceof Error?error.message:"Update failed",duration:6000
162
180
  })
163
181
  }).finally(()=>{busy.delete(kind)
182
+ setProgress((previous)=>({...previous,[kind]:undefined
183
+ }))
164
184
  if(kind==="navigator")
165
185
  setNavigatorUpdating(false)
166
186
  })
@@ -177,7 +197,7 @@ return
177
197
  confirmation++
178
198
  update(kind,version)
179
199
  }
180
- if(navigation){navigation.open(()=>_$createComponent4(NestedVersionConfirmation,{api,get name(){return label(kind)
200
+ if(navigation){navigation.open(()=>_$createComponent5(NestedVersionConfirmation,{api,get name(){return label(kind)
181
201
  },version,current:()=>!disposed&&id===confirmation&&version===target(kind),confirm:onConfirm,cancel:()=>{if(id!==confirmation)
182
202
  return
183
203
  confirmation++
@@ -187,7 +207,7 @@ confirmation++
187
207
  }}))
188
208
  return
189
209
  }
190
- api.ui.dialog.replace(()=>_$createComponent4(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
210
+ api.ui.dialog.replace(()=>_$createComponent5(api.ui.DialogConfirm,{get title(){return `Update ${label(kind)}`
191
211
  },message:`Update to ${version} using the current installation method? OpenCode must be restarted afterward.`,onConfirm:()=>{if(id!==confirmation)
192
212
  return
193
213
  confirmation++
@@ -207,16 +227,47 @@ const unregister=api.keymap.registerLayer({commands:[{name:`${PLUGIN_ID}.check-u
207
227
  api.lifecycle.onDispose(()=>{disposed=true
208
228
  unregister()
209
229
  })
210
- return{openCode:(version)=>open("openCode",version),navigator:(version,navigation)=>open("navigator",version,navigation),navigatorUpdating,navigatorInstalled
230
+ return{openCode:(version)=>open("openCode",version),navigator:(version,navigation)=>open("navigator",version,navigation),navigatorUpdating,navigatorInstalled,navigatorProgress:()=>progress().navigator,openCodeProgress:()=>progress().openCode
211
231
  }}
212
232
  import {homedir} from "os"
213
233
  import {basename,dirname,isAbsolute,join} from "path"
214
234
  import {fileURLToPath} from "url"
235
+ var prefix="navigator-update-progress:"
236
+ function reportLine(line,report){if(!line.startsWith(prefix)||line.length>1024)
237
+ return
238
+ try{const data=JSON.parse(line.slice(prefix.length))
239
+ if(["preparing","downloading","installing","verifying"].includes(data.phase??""))
240
+ report({phase:data.phase})
241
+ } catch{}}
242
+ async function runSnapshotUpdate(command,report){const child=Bun.spawn([...command],{stdout:"pipe",stderr:"pipe"})
243
+ const error=new Response(child.stderr).text()
244
+ const reader=child.stdout.getReader()
245
+ const decoder=new TextDecoder
246
+ let buffer=""
247
+ const reading=(async ()=>{while(true){const{done,value}=await reader.read()
248
+ if(done)
249
+ return
250
+ buffer += decoder.decode(value,{stream:true})
251
+ const lines=buffer.split(`
252
+ `)
253
+ buffer=(lines.pop()??"").slice(-1024)
254
+ for(const line of lines)
255
+ reportLine(line,report)
256
+ }})().catch(()=>{})
257
+ const exit=await child.exited
258
+ await reader.cancel().catch(()=>{})
259
+ await reading
260
+ const message=await error
261
+ if(exit!==0)
262
+ throw new Error(message.trim()||`Snapshot installer failed (${exit})`)
263
+ }
215
264
  async function upgradeHost(current,target,execute){if(!isSameMajorVersion(current,target))
216
265
  throw new Error("OpenCode updates must stay on the current major version")
217
266
  await execute([process.execPath,"upgrade",target])
218
267
  }
219
- var run=async (command)=>{const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
268
+ var run=async (command,progress)=>{if(progress&&command[1]?.endsWith("/update-local-snapshot.mjs"))
269
+ return runSnapshotUpdate(command,progress)
270
+ const child=Bun.spawn([...command],{stdout:"ignore",stderr:"pipe"})
220
271
  const error=new Response(child.stderr).text()
221
272
  const exitCode=await child.exited
222
273
  if(exitCode===0)
@@ -227,10 +278,13 @@ throw new Error(message.trim()||`Command failed with exit code ${child.exitCode}
227
278
  var readConfig=async (path)=>{const file=Bun.file(path)
228
279
  return await file.exists()?Bun.JSONC.parse(await file.text()):undefined
229
280
  }
230
- function pinnedUpdate(wrapper,target,execute){if(basename(wrapper)!=="tui.js"||basename(dirname(wrapper))!=="opencode-navigator")
281
+ function pinnedUpdate(wrapper,target,execute,progress){if(basename(wrapper)!=="tui.js"||basename(dirname(wrapper))!=="opencode-navigator")
231
282
  throw new Error("Unrecognized local Navigator source")
232
283
  const installer=fileURLToPath(new URL("../scripts/update-local-snapshot.mjs",import.meta.url))
233
- return execute(["node",installer,wrapper,target])
284
+ const command=["node",installer,wrapper,target]
285
+ if(progress)
286
+ return execute([...command,"--progress-json"],progress)
287
+ return execute(command)
234
288
  }
235
289
  function configHasPlugin(value,spec){if(!value||typeof value!=="object")
236
290
  return false
@@ -253,8 +307,8 @@ if(configHasPlugin(await read(path),spec))
253
307
  return false
254
308
  return configHasPlugin(await read(global),spec)
255
309
  }
256
- function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>upgradeHost(api.app.version,target,execute),async navigator(target){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
257
- return pinnedUpdate(wrapper,target,execute)
310
+ function createOpenCodeV1VersionUpdater(api,meta,execute=run,read=readConfig){return{openCode:(target)=>upgradeHost(api.app.version,target,execute),async navigator(target,progress){if(meta.source==="file"){const wrapper=meta.spec.startsWith("file://")?fileURLToPath(meta.spec):meta.spec
311
+ return pinnedUpdate(wrapper,target,execute,progress)
258
312
  }
259
313
  if(meta.source!=="npm")
260
314
  throw new Error("Unrecognized local Navigator source")
@@ -263,20 +317,22 @@ const result=await api.plugins.install(`opencode-navigator@${target}`,{global:aw
263
317
  if(!result.ok)
264
318
  throw new Error(result.message)
265
319
  }}}
266
- function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>upgradeHost(context.app.version,target,execute),async navigator(target){const response=await context.client.plugin.list(context.location?{location:context.location}:{})
320
+ function createOpenCodeV2VersionUpdater(context,execute=run){return{openCode:(target)=>upgradeHost(context.app.version,target,execute),async navigator(target,progress){progress?.({phase:"preparing"})
321
+ const response=await context.client.plugin.list(context.location?{location:context.location}:{})
267
322
  const item=response.data.find((plugin)=>plugin.id===PLUGIN_ID)
268
323
  if(!item)
269
324
  throw new Error("Navigator is not in the active plugin list")
270
325
  if(item.source.type==="local")
271
- return pinnedUpdate(basename(item.source.path)==="tui.js"?item.source.path:join(item.source.path,"tui.js"),target,execute)
326
+ return pinnedUpdate(basename(item.source.path)==="tui.js"?item.source.path:join(item.source.path,"tui.js"),target,execute,progress)
272
327
  if(item.source.type!=="package")
273
328
  throw new Error("Unrecognized local Navigator source")
274
329
  if(isAbsolute(item.source.target))
275
- return pinnedUpdate(join(item.source.target,"tui.js"),target,execute)
330
+ return pinnedUpdate(join(item.source.target,"tui.js"),target,execute,progress)
276
331
  if(item.source.target.startsWith("file://"))
277
- return pinnedUpdate(join(fileURLToPath(item.source.target),"tui.js"),target,execute)
332
+ return pinnedUpdate(join(fileURLToPath(item.source.target),"tui.js"),target,execute,progress)
278
333
  if(item.source.target.startsWith("."))
279
334
  throw new Error("Unrecognized local Navigator source")
335
+ progress?.({phase:"installing"})
280
336
  await context.client.plugin.update({location:response.location,targets:[item.source.target]})
281
337
  }}}
282
338
  export{createOpenCodeV1VersionUpdater,createOpenCodeV2VersionUpdater,createVersionStatus,createVersionUpdateActions,parseChangelog2 as parseChangelog,readBundledChangelog2 as readBundledChangelog