opencode-navigator 0.21.8 → 0.22.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 +9 -1
- package/README.md +93 -19
- package/dist/{chunk-d1bws32s.js → chunk-dz8cfnfs.js} +4 -3
- 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 +4 -20
- package/dist/navigator-updates.js +12 -4
- package/dist/project-profile.js +12 -4
- package/dist/provider-limits.js +3 -1
- package/dist/quick-actions-settings.js +5 -13
- package/dist/rpc.js +1 -0
- package/dist/runtime-dependencies.json +10 -0
- package/dist/server.js +16 -54
- package/dist/settings-import.js +4 -3
- package/dist/sidebar-settings.js +828 -0
- package/dist/sidebar-shortcuts.js +261 -0
- package/dist/toolbar-settings.js +161 -0
- package/dist/tui.js +949 -990
- package/package.json +8 -7
- package/scripts/snapshot-runtime.mjs +32 -0
- package/scripts/update-local-snapshot.mjs +14 -3
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,6 +19,9 @@ 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
|
}
|
|
@@ -484,6 +463,10 @@ const windows=openRouterFreeWindow(body,input.model.modelID,input.apiModelID)
|
|
|
484
463
|
const id=typeof data.organization_id==="string"?data.organization_id:undefined
|
|
485
464
|
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
465
|
}}
|
|
466
|
+
function credentialToken(credential){if(!credential||credential.type==="external")
|
|
467
|
+
return
|
|
468
|
+
return credential.type==="oauth"?credential.access:credential.key
|
|
469
|
+
}
|
|
487
470
|
async function nativeQuotaContext(host,model,signal,fetcher){const [provider,catalog]=await Promise.all([host.provider.get({providerID:model.providerID}),host.model.list()])
|
|
488
471
|
const selected=catalog.data.find((entry)=>entry.providerID===model.providerID&&entry.id===model.modelID&&entry.enabled)
|
|
489
472
|
if(!selected||!provider.data.integrationID||provider.data.activation==="disabled")
|
|
@@ -492,7 +475,7 @@ const connection=await host.integration.connection.active(provider.data.integrat
|
|
|
492
475
|
if(!connection)
|
|
493
476
|
return
|
|
494
477
|
const credential=await host.integration.connection.resolve(connection)
|
|
495
|
-
const token=credential
|
|
478
|
+
const token=credentialToken(credential)
|
|
496
479
|
if(!credential||!token)
|
|
497
480
|
return
|
|
498
481
|
signal.throwIfAborted()
|
|
@@ -502,7 +485,7 @@ async function sameNativeConnection(host,input){const current=input.provider.int
|
|
|
502
485
|
if(!current||connectionKey(current)!==input.connectionID)
|
|
503
486
|
return false
|
|
504
487
|
const credential=await host.integration.connection.resolve(current)
|
|
505
|
-
const token=credential
|
|
488
|
+
const token=credentialToken(credential)
|
|
506
489
|
return token===input.token
|
|
507
490
|
}
|
|
508
491
|
function requestUsesCredential(request,input){const url=new URL(request.url)
|
|
@@ -518,7 +501,7 @@ if(["x-api-key","api-key","x-goog-api-key"].some((header)=>request.headers.get(h
|
|
|
518
501
|
return true
|
|
519
502
|
return url.hostname==="generativelanguage.googleapis.com"&&url.searchParams.get("key")===input.token
|
|
520
503
|
}
|
|
521
|
-
function
|
|
504
|
+
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
505
|
}
|
|
523
506
|
function modelForEvent(event){return{providerID:event.model.providerID,modelID:event.model.id,sessionID:event.sessionID}}
|
|
524
507
|
function rejectedRead(model,error,input){const status=error instanceof QuotaReadError?error.status:undefined
|
|
@@ -540,7 +523,7 @@ if(input.canonical==="github-copilot")
|
|
|
540
523
|
return copilot.read(input)
|
|
541
524
|
return readNativeRest(input)
|
|
542
525
|
}
|
|
543
|
-
async function currentResult(input,force){const identity=
|
|
526
|
+
async function currentResult(input,force){const identity=key(input)
|
|
544
527
|
const headers=observed.get(identity)
|
|
545
528
|
const existing=cached.get(identity)
|
|
546
529
|
let result=!force&&existing&&Date.now()-existing.fetchedAt<30000?existing:undefined
|
|
@@ -561,13 +544,13 @@ const epoch=generation
|
|
|
561
544
|
const observation=AbortSignal.any([lifecycle,AbortSignal.timeout(1000)])
|
|
562
545
|
try{const input=await withinQuotaSignal(observation,()=>nativeQuotaContext(host,modelForEvent(event),observation,fetcher))
|
|
563
546
|
if(input&&epoch===generation&&requestUsesCredential(event.request,input)&&await withinQuotaSignal(observation,()=>sameNativeConnection(host,input)))
|
|
564
|
-
captured.set(event.request,{key:
|
|
547
|
+
captured.set(event.request,{key:key(input),generation:epoch})
|
|
565
548
|
} catch{}},async response(event){const request=captured.get(event.request)
|
|
566
549
|
if(!request||request.generation!==generation||lifecycle.aborted)
|
|
567
550
|
return false
|
|
568
551
|
const observation=AbortSignal.any([lifecycle,AbortSignal.timeout(1000)])
|
|
569
552
|
try{const input=await withinQuotaSignal(observation,()=>nativeQuotaContext(host,modelForEvent(event),observation,fetcher))
|
|
570
|
-
if(!input||
|
|
553
|
+
if(!input||key(input)!==request.key||!await withinQuotaSignal(observation,()=>sameNativeConnection(host,input)))
|
|
571
554
|
return false
|
|
572
555
|
let windows=nativeHeaderWindows(input.canonical,input.model.modelID,event.response.headers)
|
|
573
556
|
if(windows.length===0&&event.response.status===429&&["google","google-vertex"].includes(input.canonical)&&event.response.headers.get("content-type")?.includes("json"))
|
|
@@ -629,32 +612,11 @@ await response.dispose()
|
|
|
629
612
|
await rpc.dispose()
|
|
630
613
|
await service.dispose()
|
|
631
614
|
}}
|
|
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)})
|
|
615
|
+
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
616
|
})
|
|
653
617
|
const quotaCleanup=typeof host.integration?.connection?.active==="function"&&typeof host.provider?.get==="function"?await setupProviderQuotaServer(host):undefined
|
|
654
618
|
return async ()=>{await quotaCleanup?.()
|
|
655
|
-
await
|
|
656
|
-
await tools.dispose()
|
|
657
|
-
await rpc.dispose()
|
|
619
|
+
await archive.dispose()
|
|
658
620
|
}}})
|
|
659
|
-
export{
|
|
621
|
+
export{server_plugin_default as default
|
|
660
622
|
}
|
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
|