opencode-navigator 0.25.0 → 0.27.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 +14 -0
- package/HOST_API.md +125 -16
- package/LINT_COVERAGE.md +32 -29
- package/PROVIDERS.md +9 -0
- package/README.md +97 -12
- package/dist/{chunk-bv0nm1tj.js → chunk-dm1y8x49.js} +2 -1
- package/dist/gui-experimental.cjs +15526 -0
- package/dist/layout-preset-actions.js +37 -14
- package/dist/native-todo-setup.js +2 -1
- package/dist/navigator-updates.js +2 -2
- package/dist/onboarding.js +11 -4
- package/dist/project-profile.js +34 -38
- package/dist/provider-limits.js +213 -154
- package/dist/quick-actions-settings.js +1 -1
- package/dist/runtime-dependencies.json +4 -4
- package/dist/server.js +379 -0
- package/dist/sidebar-settings.js +42 -37
- package/dist/sidebar-shortcuts.js +1 -1
- package/dist/toolbar-settings.js +68 -20
- package/dist/tui.js +792 -661
- package/dist/upgrade-notes.js +1 -1
- package/package.json +30 -17
- /package/dist/{chunk-pwa1wzer.js → chunk-rrvwmnyh.js} +0 -0
package/dist/tui.js
CHANGED
|
@@ -3,7 +3,6 @@ import {insert as _$insert,setProp as _$setProp,createElement as _$createElement
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,onCleanup as onCleanup2,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup3,Show,createEffect as createEffect4,Show as Show2,splitProps as splitProps3,createEffect as createEffect5,createSignal as createSignal4,onCleanup as onCleanup4,onMount,Suspense,lazy,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup5,Show as Show3,batch,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect6,createSignal as createSignal9,onCleanup as onCleanup6,createSignal as createSignal10,createSignal as createSignal11,Suspense as Suspense2,lazy as lazy2,Index,onCleanup as onCleanup7,createSignal as createSignal12,onCleanup as onCleanup8,createEffect as createEffect8,onCleanup as onCleanup10,batch as batch2,createEffect as createEffect7,createMemo,createSignal as createSignal13,onCleanup as onCleanup9,onMount as onMount2,For as For2,For as For3,Show as Show4,createMemo as createMemo2,Suspense as Suspense3,lazy as lazy3,For as For6,createMemo as createMemo4,onMount as onMount4,For as For5,createMemo as createMemo3,onMount as onMount3,Show as Show6,createEffect as createEffect9,createSignal as createSignal14,onCleanup as onCleanup11,Show as Show5,For as For4,For as For7,createEffect as createEffect10,createMemo as createMemo5,onCleanup as onCleanup12,createEffect as createEffect11,createMemo as createMemo6,untrack as untrack2,Suspense as Suspense4,lazy as lazy4,onCleanup as onCleanup13,createEffect as createEffect12,onCleanup as onCleanup14,createSignal as createSignal15,createMemo as createMemo7,createSignal as createSignal16,createSignal as createSignal17,lazy as lazy5,Show as Show9,For as For8,Show as Show8,Show as Show7,createEffect as createEffect13,onCleanup as onCleanup15,onMount as onMount5,onCleanup as onCleanup16,onMount as onMount6,For as For9,createSignal as createSignal18,createSignal as createSignal19,onCleanup as onCleanup17,createSignal as createSignal20,Show as Show10,createSignal as createSignal22,createSignal as createSignal21,onCleanup as onCleanup18,For as For10,Show as Show13,createMemo as createMemo9,createComputed,createMemo as createMemo8,createSignal as createSignal23,untrack as untrack3,Show as Show11,createComputed as createComputed2,untrack as untrack4,Show as Show12,createSignal as createSignal25,createEffect as createEffect14,createSignal as createSignal24,on,createMemo as createMemo11,createSignal as createSignal28,createMemo as createMemo10,For as For11,Show as Show17,Show as Show14,Show as Show15,createSignal as createSignal26,createContext as createContext3,useContext as useContext3,Show as Show16,createSignal as createSignal27,onCleanup as onCleanup19,Show as Show23,createSignal as createSignal29,untrack as untrack5,createSignal as createSignal30,createSignal as createSignal31,createEffect as createEffect15,For as For13,Show as Show18,For as For12,createSignal as createSignal32,Show as Show19,Show as Show20,Show as Show21,Show as Show22,For as For14,Show as Show25,createMemo as createMemo13,Show as Show24,createMemo as createMemo12,createSignal as createSignal33,createContext as createContext4,useContext as useContext4,Show as Show26,Suspense as Suspense5,createEffect as createEffect16,onCleanup as onCleanup20,untrack as untrack6,For as For20,Match,Switch,createMemo as createMemo19,onCleanup as onCleanup26,For as For15,Show as Show28,createMemo as createMemo15,createSignal as createSignal35,createEffect as createEffect17,createMemo as createMemo14,onCleanup as onCleanup21,untrack as untrack7,Show as Show27,createEffect as createEffect18,createSignal as createSignal34,onCleanup as onCleanup22,For as For16,Show as Show30,createSignal as createSignal36,createEffect as createEffect19,createMemo as createMemo16,onCleanup as onCleanup23,untrack as untrack8,Show as Show29,For as For18,Show as Show33,createEffect as createEffect20,createMemo as createMemo17,createSignal as createSignal37,onCleanup as onCleanup24,untrack as untrack9,For as For17,Show as Show32,Show as Show31,Show as Show38,createEffect as createEffect21,createMemo as createMemo18,createSignal as createSignal38,onCleanup as onCleanup25,untrack as untrack10,Show as Show34,For as For19,Show as Show36,Show as Show35,Show as Show37,createEffect as createEffect22,createMemo as createMemo20,onCleanup as onCleanup27,untrack as untrack11,Show as Show39,createMemo as createMemo21,onCleanup as onCleanup28,createEffect as createEffect23,createMemo as createMemo22,on as on2,onCleanup as onCleanup29,createRoot,createSignal as createSignal39,getOwner,createSignal as createSignal40,onCleanup as onCleanup30,onMount as onMount7} from "solid-js"
|
|
7
6
|
var SIDEBAR_SECTIONS=["limits","todo","subagents","skills","quick_actions","lsp","mcp"]
|
|
8
7
|
var QUICK_ACTION_IDS=["session.rename","session.timeline","session.copy","session.export","session.compact","session.list","session.new","session.fork","messages.copy","session.first","session.last","session.messages_last_user","session.message.next","session.message.previous","permission.mode"]
|
|
9
8
|
function preferencesScope(path){return path.worktree||path.directory||"global"
|
|
@@ -85,6 +84,7 @@ const session=sessionID?api.state.session.get(sessionID):undefined
|
|
|
85
84
|
const directory=session?.directory??api.state.path.directory
|
|
86
85
|
const workspace=session?.workspaceID
|
|
87
86
|
return{key:JSON.stringify([directory,workspace??null]),routing:{directory,...workspace&&{workspace}}}}
|
|
87
|
+
import {createSignal as createSignal2,createSignal,createEffect,onCleanup,createContext,useContext,createContext as createContext2,createSignal as createSignal3,onCleanup as onCleanup2,useContext as useContext2,createEffect as createEffect2,splitProps,untrack,splitProps as splitProps2,For,createEffect as createEffect3,onCleanup as onCleanup3,Show,createEffect as createEffect4,Show as Show2,splitProps as splitProps3,createEffect as createEffect5,createSignal as createSignal4,onCleanup as onCleanup4,onMount,Suspense,lazy,batch,createSignal as createSignal5,createSignal as createSignal6,onCleanup as onCleanup5,Show as Show3,batch as batch2,createSignal as createSignal8,createSignal as createSignal7,createEffect as createEffect6,createSignal as createSignal9,onCleanup as onCleanup6,createSignal as createSignal10,createSignal as createSignal11,Suspense as Suspense2,lazy as lazy2,Index,onCleanup as onCleanup7,createEffect as createEffect8,onCleanup as onCleanup10,batch as batch3,createEffect as createEffect7,createMemo,createSignal as createSignal13,onCleanup as onCleanup9,onMount as onMount2,createSignal as createSignal12,onCleanup as onCleanup8,For as For2,For as For3,Show as Show4,createMemo as createMemo2,Suspense as Suspense3,lazy as lazy3,For as For6,createMemo as createMemo4,onMount as onMount4,For as For5,createMemo as createMemo3,onMount as onMount3,Show as Show6,createEffect as createEffect9,createSignal as createSignal14,onCleanup as onCleanup11,Show as Show5,For as For4,For as For7,createEffect as createEffect10,createMemo as createMemo5,onCleanup as onCleanup12,createEffect as createEffect11,createMemo as createMemo6,untrack as untrack2,Suspense as Suspense4,lazy as lazy4,onCleanup as onCleanup13,createEffect as createEffect12,onCleanup as onCleanup14,createSignal as createSignal15,createMemo as createMemo7,createSignal as createSignal16,createSignal as createSignal17,lazy as lazy5,Show as Show9,For as For8,Show as Show8,Show as Show7,createEffect as createEffect13,onCleanup as onCleanup15,onMount as onMount5,onCleanup as onCleanup16,onMount as onMount6,For as For9,createSignal as createSignal18,createSignal as createSignal19,onCleanup as onCleanup17,createSignal as createSignal20,Show as Show10,createSignal as createSignal22,createSignal as createSignal21,onCleanup as onCleanup18,For as For10,Show as Show13,createMemo as createMemo9,createComputed,createMemo as createMemo8,createSignal as createSignal23,untrack as untrack3,Show as Show11,createComputed as createComputed2,untrack as untrack4,Show as Show12,createSignal as createSignal25,createEffect as createEffect14,createSignal as createSignal24,on,createMemo as createMemo11,createSignal as createSignal28,createMemo as createMemo10,For as For11,Show as Show17,Show as Show14,Show as Show15,createSignal as createSignal26,createContext as createContext3,useContext as useContext3,Show as Show16,createSignal as createSignal27,onCleanup as onCleanup19,Show as Show23,createSignal as createSignal29,untrack as untrack5,createSignal as createSignal30,createSignal as createSignal31,createEffect as createEffect15,For as For13,Show as Show18,For as For12,createSignal as createSignal32,Show as Show19,Show as Show20,Show as Show21,Show as Show22,createSignal as createSignal33,createEffect as createEffect16,createMemo as createMemo12,on as on2,onCleanup as onCleanup20,For as For14,Show as Show25,createMemo as createMemo14,Show as Show24,createMemo as createMemo13,createSignal as createSignal34,createContext as createContext4,useContext as useContext4,Show as Show26,Suspense as Suspense5,createEffect as createEffect17,onCleanup as onCleanup21,untrack as untrack6,For as For20,Match,Switch,createMemo as createMemo20,onCleanup as onCleanup27,For as For15,Show as Show28,createMemo as createMemo16,createSignal as createSignal36,createEffect as createEffect18,createMemo as createMemo15,onCleanup as onCleanup22,untrack as untrack7,Show as Show27,createEffect as createEffect19,createSignal as createSignal35,onCleanup as onCleanup23,For as For16,Show as Show30,createSignal as createSignal37,createEffect as createEffect20,createMemo as createMemo17,onCleanup as onCleanup24,untrack as untrack8,Show as Show29,For as For18,Show as Show33,createEffect as createEffect21,createMemo as createMemo18,createSignal as createSignal38,onCleanup as onCleanup25,untrack as untrack9,For as For17,Show as Show32,Show as Show31,Show as Show38,createEffect as createEffect22,createMemo as createMemo19,createSignal as createSignal39,onCleanup as onCleanup26,untrack as untrack10,Show as Show34,For as For19,Show as Show36,Show as Show35,Show as Show37,createEffect as createEffect23,createMemo as createMemo21,onCleanup as onCleanup28,untrack as untrack11,Show as Show39,createMemo as createMemo22,onCleanup as onCleanup29,createRoot,createSignal as createSignal40,getOwner,createSignal as createSignal41,onCleanup as onCleanup30,onMount as onMount7} from "solid-js"
|
|
88
88
|
function mcpToggleAction(status){if(status==="pending")
|
|
89
89
|
return
|
|
90
90
|
return status==="connected"?"disconnect":"connect"
|
|
@@ -179,10 +179,14 @@ setStates((values)=>({...values,[target]:{status:"error",error:requestError(caus
|
|
|
179
179
|
return{state,start,abort,abortAll}}
|
|
180
180
|
function wait(delay,signal){return new Promise((resolve,reject)=>{if(signal?.aborted)
|
|
181
181
|
return reject(signal.reason??new DOMException("Aborted","AbortError"))
|
|
182
|
-
const
|
|
183
|
-
signal?.
|
|
184
|
-
reject(signal
|
|
185
|
-
}
|
|
182
|
+
const abort=()=>{clearTimeout(timer)
|
|
183
|
+
signal?.removeEventListener("abort",abort)
|
|
184
|
+
reject(signal?.reason??new DOMException("Aborted","AbortError"))
|
|
185
|
+
}
|
|
186
|
+
const timer=setTimeout(()=>{signal?.removeEventListener("abort",abort)
|
|
187
|
+
resolve()
|
|
188
|
+
},delay)
|
|
189
|
+
signal?.addEventListener("abort",abort,{once:true})
|
|
186
190
|
})
|
|
187
191
|
}
|
|
188
192
|
async function retryBackgroundRefresh(refresh,options={}){const delays=options.delays??[250,1000]
|
|
@@ -212,6 +216,8 @@ const rightRank=BUCKET_RANK[right.bucket]??1
|
|
|
212
216
|
return leftRank-rightRank||left.bucket.localeCompare(right.bucket)||left.name.localeCompare(right.name)
|
|
213
217
|
})
|
|
214
218
|
}
|
|
219
|
+
function serverKey(current,name){return JSON.stringify([current.key,name])
|
|
220
|
+
}
|
|
215
221
|
function bulkDescription(operation,action){if(operation.action==="preset")
|
|
216
222
|
return `apply ${operation.preset} MCP preset`
|
|
217
223
|
if(operation.group)
|
|
@@ -224,12 +230,8 @@ return Object.keys(presets).sort((left,right)=>left.localeCompare(right)).find((
|
|
|
224
230
|
return Object.keys(states).length===items.length&&items.every((item)=>item.status==="connected"&&states[item.name]==="enabled"||item.status==="disabled"&&states[item.name]==="disabled")
|
|
225
231
|
})
|
|
226
232
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
const error=status.error
|
|
230
|
-
return typeof error==="string"?error:undefined
|
|
231
|
-
}
|
|
232
|
-
function createMcpController(api,persist,preferences,source){const [snapshots,setSnapshots]=createSignal2({})
|
|
233
|
+
function createMcpModel(options,persist,preferences){const{source}=options
|
|
234
|
+
const [snapshots,setSnapshots]=createSignal2({})
|
|
233
235
|
const [mutationCounts,setMutationCounts]=createSignal2({})
|
|
234
236
|
const [bulkStates,setBulkStates]=createSignal2({})
|
|
235
237
|
const [selectedPresets,setSelectedPresets]=createSignal2({})
|
|
@@ -239,8 +241,8 @@ const mutationRetries=new Map
|
|
|
239
241
|
const bulkGenerations=new Map
|
|
240
242
|
const bulkRetries=new Map
|
|
241
243
|
const selectedPresetStates=new Map
|
|
242
|
-
const requests=createRequestState(
|
|
243
|
-
const serverRequests=createRequestState(
|
|
244
|
+
const requests=createRequestState(options.signal)
|
|
245
|
+
const serverRequests=createRequestState(options.signal)
|
|
244
246
|
let activation=0
|
|
245
247
|
let activeTarget
|
|
246
248
|
function changeMutationCount(current,offset){setMutationCounts((values)=>{const next=Math.max(0,(values[current.key]??0) + offset)
|
|
@@ -264,10 +266,7 @@ serverRequests.abortAll()
|
|
|
264
266
|
refreshing.clear()
|
|
265
267
|
activeTarget=current.key
|
|
266
268
|
}
|
|
267
|
-
function target(){
|
|
268
|
-
return{key:location.key,scope:preferencesScope({directory:location.routing.directory,worktree:api.state.path.worktree}),routing:location.routing
|
|
269
|
-
}}
|
|
270
|
-
function serverKey(current,name){return JSON.stringify([current.key,name])
|
|
269
|
+
function target(){return options.target()
|
|
271
270
|
}
|
|
272
271
|
function mutating(current=target()){return(mutationCounts()[current.key]??0)>0
|
|
273
272
|
}
|
|
@@ -302,7 +301,7 @@ const failed=state.failed.filter((candidate)=>candidate!==name)
|
|
|
302
301
|
bulkRetries.set(current.key,(bulkRetries.get(current.key)??[]).filter((candidate)=>candidate.name!==name))
|
|
303
302
|
return{...values,[current.key]:{...state,status:failed.length>0?"error":"ready",failed}}})
|
|
304
303
|
}
|
|
305
|
-
function list(current=target()){return source?source.list(current):snapshots()[current.key]??
|
|
304
|
+
function list(current=target()){return options.cache==="source"?source.list(current):snapshots()[current.key]??source.list(current)
|
|
306
305
|
}
|
|
307
306
|
async function refresh(current=target(),force=false){const pending=refreshing.get(current.key)
|
|
308
307
|
if(pending&&!force)
|
|
@@ -310,7 +309,7 @@ return pending
|
|
|
310
309
|
const requestState=requests.start(current.key,"refresh MCP servers",current.key in snapshots(),force)
|
|
311
310
|
if(!requestState)
|
|
312
311
|
return pending
|
|
313
|
-
const request=
|
|
312
|
+
const request=source.refresh(current,force,requestState.signal).then((items)=>{if(options.cache==="model"&&requestState.isCurrent())
|
|
314
313
|
setSnapshots((value)=>({...value,[current.key]:items}))
|
|
315
314
|
if(requestState.isCurrent())
|
|
316
315
|
reconcileSelectedPreset(current,items)
|
|
@@ -333,7 +332,7 @@ const requestState=serverRequests.start(key,operation,false)
|
|
|
333
332
|
if(!requestState)
|
|
334
333
|
return mutations.get(key)
|
|
335
334
|
changeMutationCount(current,1)
|
|
336
|
-
const request=
|
|
335
|
+
const request=options.change(current,name,action,requestState.signal).then(()=>{requestState.succeed()
|
|
337
336
|
mutationRetries.delete(key)
|
|
338
337
|
clearBulkFailure(current,name)
|
|
339
338
|
}).catch((error)=>{requestState.fail(error)
|
|
@@ -381,14 +380,14 @@ if(desired==="disabled"&&action==="disconnect"){await changeServer(current,item.
|
|
|
381
380
|
}
|
|
382
381
|
if(desired==="enabled"&&action==="connect"){await changeServer(current,item.name,"connect","restore MCP preference",false)
|
|
383
382
|
}}))
|
|
384
|
-
const failed=
|
|
385
|
-
if(failed.length>0){
|
|
383
|
+
const failed=options.signal?.aborted?[]:results.flatMap((result,index)=>result.status==="rejected"&&!isAbortError(result.reason)?[items[index].name]:[])
|
|
384
|
+
if(failed.length>0){options.notifications.show({variant:"warning",title:"MCP preferences",message:`Could not restore: ${failed.join(", ")}`
|
|
386
385
|
})
|
|
387
386
|
}
|
|
388
387
|
if(generation===activation&&target().key===current.key)
|
|
389
388
|
await refresh(current,true)
|
|
390
389
|
}
|
|
391
|
-
function reconnect(current=target()){return retryBackgroundRefresh(()=>activate(current),{signal:
|
|
390
|
+
function reconnect(current=target()){return retryBackgroundRefresh(()=>activate(current),{signal:options.signal})
|
|
392
391
|
}
|
|
393
392
|
function bulkState(current=target()){return bulkStates()[current.key]??{action:"connect",status:"idle",completed:0,total:0,failed:[]
|
|
394
393
|
}}
|
|
@@ -426,7 +425,7 @@ setBulkStates((values)=>({...values,[current.key]:{...operation,status:failed.le
|
|
|
426
425
|
if(failed.length>0&&activeTarget===current.key){let action=operation.action==="preset"?`apply ${operation.preset}`:operation.action
|
|
427
426
|
if(operation.group)
|
|
428
427
|
action += ` ${operation.group}`
|
|
429
|
-
|
|
428
|
+
options.notifications.show({variant:"warning",title:"MCP servers",message:`Could not ${action}: ${failed.join(", ")}`
|
|
430
429
|
})
|
|
431
430
|
}}
|
|
432
431
|
return{list,refresh,reconnect,activate,deactivate(current){if(activeTarget!==current.key)
|
|
@@ -493,6 +492,14 @@ return
|
|
|
493
492
|
const changes=bulkRetries.get(current.key)??[]
|
|
494
493
|
return changeMany({action:state.action,...state.preset&&{preset:state.preset},...state.group&&{group:state.group}},changes,current)
|
|
495
494
|
}}}
|
|
495
|
+
function createMcpController(api,persist,preferences,source){return createMcpModel({target(){const location=currentLocation(api)
|
|
496
|
+
return{...location,scope:preferencesScope({directory:location.routing.directory,worktree:api.state.path.worktree})
|
|
497
|
+
}},signal:api.lifecycle?.signal,notifications:{show:(input)=>api.ui.toast({...input,duration:4000})},cache:source?"source":"model",source:source??{list:()=>api.state.mcp(),async refresh(target,_force,signal){const result=await api.client.mcp.status(target.routing,{throwOnError:true,signal})
|
|
498
|
+
return Object.entries(result.data??{}).map(([name,status])=>({name,status:status.status,error:"error" in status?status.error:undefined
|
|
499
|
+
})).sort((left,right)=>left.name.localeCompare(right.name))
|
|
500
|
+
}},async change(target,name,action,signal){await api.client.mcp[action]({name,...target.routing},{throwOnError:true,signal})
|
|
501
|
+
}},persist,preferences)
|
|
502
|
+
}
|
|
496
503
|
|
|
497
504
|
|
|
498
505
|
|
|
@@ -1191,12 +1198,16 @@ _$insert(_el$,_$createComponent(Suspense,{get children(){return _$createComponen
|
|
|
1191
1198
|
return _el$
|
|
1192
1199
|
})()
|
|
1193
1200
|
}
|
|
1201
|
+
var ICON_CHOICE_IDS=["plus","check","circle","star","pin","bolt","book","search","code","terminal","gear","shield","lock","unlock","eye","clock","refresh","history","copy","export","edit","fork","list","play","pause","stop","rocket","bug","beaker","wand","cloud","server","folder","file","heart","lightbulb","arrow-up","arrow-down","warning","close"]
|
|
1202
|
+
var choices=new Set(ICON_CHOICE_IDS)
|
|
1203
|
+
function iconChoiceLabel(id){return id.replaceAll("-"," ")
|
|
1204
|
+
}
|
|
1194
1205
|
var glyphs={plus:["\uEA60","+"],check:["\uEAB2","v"],circle:["\uEAEE","o"],star:["\uEA6A","*"],pin:["\uEBAB","^"],bolt:["\uEA86","!"],book:["\uEAA4","B"],search:["\uEA6D","?"],code:["\uEAC4","#"],terminal:["\uEA85",">"],gear:["\uEB51","G"],shield:["\uEBB3","S"],lock:["\uEA75","L"],unlock:["\uEB74","U"],eye:["\uEA70","E"],clock:["\uEA82","T"],refresh:["\uEAF5","R"],history:["\uEA82","H"],copy:["\uEBCC","C"],export:["\uEBAC","X"],edit:["\uEA73","/"],fork:["\uEB9E","F"],list:["\uEBEB","="],play:["\uEB2C",">"],pause:["\uEAD1","|"],stop:["\uEAD7","s"],rocket:["\uEB44","r"],bug:["\uEAAF","b"],beaker:["\uEA79","t"],wand:["\uEAB6","w"],cloud:["\uEABA","c"],server:["\uEB50","s"],folder:["\uEA83","D"],file:["\uEA7B","f"],heart:["\uEB05","h"],lightbulb:["\uEA61","i"],"arrow-up":["\uEAA1","^"],"arrow-down":["\uEA9A","v"],warning:["\uEA6C","!"],close:["\uEA76","x"]
|
|
1195
1206
|
}
|
|
1196
|
-
var terminalIconIDs=
|
|
1207
|
+
var terminalIconIDs=[...ICON_CHOICE_IDS]
|
|
1197
1208
|
function terminalIcon(id,style){return(glyphs[id]??glyphs.bolt)[style==="nerd"?0:1]
|
|
1198
1209
|
}
|
|
1199
|
-
function terminalIconLabel(id){return id
|
|
1210
|
+
function terminalIconLabel(id){return iconChoiceLabel(id)
|
|
1200
1211
|
}
|
|
1201
1212
|
function McpErrorDialog(props){return _$createComponent(SelectionMenu,{get api(){return props.api
|
|
1202
1213
|
},get title(){return `MCP error \xB7 ${props.name}`
|
|
@@ -1205,6 +1216,9 @@ function McpErrorDialog(props){return _$createComponent(SelectionMenu,{get api()
|
|
|
1205
1216
|
},onSelect:()=>{}})
|
|
1206
1217
|
}
|
|
1207
1218
|
|
|
1219
|
+
function builtInPreferences(defaults){return{behavior:{toggleKey:defaults.toggleKey,focusKey:defaults.focusKey,searchKey:defaults.searchKey,persistMcp:defaults.persistMcp,limitsRefreshMinutes:defaults.limitsRefreshMinutes,startInChat:defaults.startInChat,showSessionTitle:defaults.showSessionTitle,showSessionDate:defaults.showSessionDate,cornerFont:defaults.cornerFont,lspIconStyle:defaults.lspIconStyle,rowDensity:defaults.rowDensity,toolbarOverflow:defaults.toolbarOverflow,sectionItemLimits:defaults.sectionItemLimits,quickActionOrder:defaults.quickActionOrder,quickActionVisibility:defaults.quickActionVisibility
|
|
1220
|
+
},layout:{sections:defaults.sections,expanded:DEFAULT_SECTION_EXPANSION,order:defaults.sectionOrder??[...SIDEBAR_SECTIONS]
|
|
1221
|
+
},desiredMcpStates:{}}}
|
|
1208
1222
|
function codexBindingKey(target){return JSON.stringify([target.providerID,target.connectionID?{connectionID:target.connectionID}:null])
|
|
1209
1223
|
}
|
|
1210
1224
|
function legacyKey(key){try{const value=JSON.parse(key)
|
|
@@ -1329,6 +1343,83 @@ function serializePortableSettings(layout,mcp,behavior){return `${JSON.stringify
|
|
|
1329
1343
|
}, undefined, 2)}
|
|
1330
1344
|
`
|
|
1331
1345
|
}
|
|
1346
|
+
function normalizePresetName(value){const name=value.trim()
|
|
1347
|
+
if(!name)
|
|
1348
|
+
throw new Error("Enter a preset name")
|
|
1349
|
+
if(name.length>64)
|
|
1350
|
+
throw new Error("Preset names can contain at most 64 characters")
|
|
1351
|
+
return name
|
|
1352
|
+
}
|
|
1353
|
+
function createPreferencePresetActions(input){return{saveLayoutPreset(value){input.load()
|
|
1354
|
+
const name=normalizePresetName(value)
|
|
1355
|
+
if(Object.keys(input.layoutPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase()))
|
|
1356
|
+
throw new Error("A preset with this name already exists")
|
|
1357
|
+
if(Object.keys(input.layoutPresets()).length>=50)
|
|
1358
|
+
throw new Error("You can save at most 50 layout presets")
|
|
1359
|
+
const layout=input.currentLayoutPreset()
|
|
1360
|
+
input.persistLayoutPresets({...input.layoutPresets(),[name]:layout},{name,layout})
|
|
1361
|
+
return name
|
|
1362
|
+
},updateLayoutPreset(name){if(!input.layoutPresets()[name])
|
|
1363
|
+
return false
|
|
1364
|
+
const layout=input.currentLayoutPreset()
|
|
1365
|
+
input.persistLayoutPresets({...input.layoutPresets(),[name]:layout},{name,layout})
|
|
1366
|
+
return true
|
|
1367
|
+
},renameLayoutPreset(current,value){const name=normalizePresetName(value)
|
|
1368
|
+
const preset=input.layoutPresets()[current]
|
|
1369
|
+
if(!preset)
|
|
1370
|
+
throw new Error("Layout preset no longer exists")
|
|
1371
|
+
if(name.toLocaleLowerCase()!==current.toLocaleLowerCase()&&Object.keys(input.layoutPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase()))
|
|
1372
|
+
throw new Error("A preset with this name already exists")
|
|
1373
|
+
const next={...input.layoutPresets()}
|
|
1374
|
+
delete next[current]
|
|
1375
|
+
next[name]=preset
|
|
1376
|
+
input.persistLayoutPresets(next,{name,layout:preset,previousName:current})
|
|
1377
|
+
return name
|
|
1378
|
+
},deleteLayoutPreset(name){if(!input.layoutPresets()[name])
|
|
1379
|
+
return false
|
|
1380
|
+
const next={...input.layoutPresets()}
|
|
1381
|
+
delete next[name]
|
|
1382
|
+
input.persistLayoutPresets(next,{name})
|
|
1383
|
+
return true
|
|
1384
|
+
},saveMcpPreset(value,states){input.requireHydration()
|
|
1385
|
+
if(Object.keys(states).length===0)
|
|
1386
|
+
throw new Error("There are no MCP servers to save")
|
|
1387
|
+
const name=normalizePresetName(value)
|
|
1388
|
+
if(Object.keys(input.mcpPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase()))
|
|
1389
|
+
throw new Error("A preset with this name already exists")
|
|
1390
|
+
if(Object.keys(input.mcpPresets()).length>=50)
|
|
1391
|
+
throw new Error("You can save at most 50 MCP presets")
|
|
1392
|
+
const preset={...states}
|
|
1393
|
+
input.persistMcpPresets({...input.mcpPresets(),[name]:preset},{operation:"save",name,states:preset})
|
|
1394
|
+
return name
|
|
1395
|
+
},updateMcpPreset(name,states){input.requireHydration()
|
|
1396
|
+
if(!input.mcpPresets()[name])
|
|
1397
|
+
return false
|
|
1398
|
+
if(Object.keys(states).length===0)
|
|
1399
|
+
throw new Error("There are no MCP servers to save")
|
|
1400
|
+
const preset={...states}
|
|
1401
|
+
input.persistMcpPresets({...input.mcpPresets(),[name]:preset},{operation:"update",name,states:preset})
|
|
1402
|
+
return true
|
|
1403
|
+
},renameMcpPreset(current,value){input.requireHydration()
|
|
1404
|
+
const name=normalizePresetName(value)
|
|
1405
|
+
const preset=input.mcpPresets()[current]
|
|
1406
|
+
if(!preset)
|
|
1407
|
+
throw new Error("MCP preset no longer exists")
|
|
1408
|
+
if(name.toLocaleLowerCase()!==current.toLocaleLowerCase()&&Object.keys(input.mcpPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase()))
|
|
1409
|
+
throw new Error("A preset with this name already exists")
|
|
1410
|
+
const next={...input.mcpPresets()}
|
|
1411
|
+
delete next[current]
|
|
1412
|
+
next[name]=preset
|
|
1413
|
+
input.persistMcpPresets(next,{operation:"rename",name,previousName:current})
|
|
1414
|
+
return name
|
|
1415
|
+
},deleteMcpPreset(name){input.requireHydration()
|
|
1416
|
+
if(!input.mcpPresets()[name])
|
|
1417
|
+
return false
|
|
1418
|
+
const next={...input.mcpPresets()}
|
|
1419
|
+
delete next[name]
|
|
1420
|
+
input.persistMcpPresets(next,{operation:"delete",name})
|
|
1421
|
+
return true
|
|
1422
|
+
}}}
|
|
1332
1423
|
function toolbarPinKey(pin){return pin.kind==="action"?`action:${pin.id}`:`skill:${pin.location}`
|
|
1333
1424
|
}
|
|
1334
1425
|
function identifier(value){return typeof value==="string"&&/^[a-z][a-z\d-]{0,47}$/u.test(value)
|
|
@@ -1565,14 +1656,6 @@ order=[...layer.layout.order]
|
|
|
1565
1656
|
Object.assign(desiredMcpStates,layer.desiredMcpStates)
|
|
1566
1657
|
}
|
|
1567
1658
|
return{behavior,layout:{sections,expanded,order},desiredMcpStates}}
|
|
1568
|
-
import {randomUUID} from "crypto"
|
|
1569
|
-
import {link,mkdir,readFile,readdir,rename,rm,unlink,writeFile,readFile as readFile2} from "fs/promises"
|
|
1570
|
-
import {join,dirname,join as join2,join as join3} from "path"
|
|
1571
|
-
var LOCK_TIMEOUT_MS=3000
|
|
1572
|
-
function errno(error,code){return error instanceof Error&&"code" in error&&error.code===code
|
|
1573
|
-
}
|
|
1574
|
-
function delay(milliseconds){return new Promise((resolve)=>setTimeout(resolve,milliseconds))
|
|
1575
|
-
}
|
|
1576
1659
|
function parseSkillConfirmations(value){if(!Array.isArray(value))
|
|
1577
1660
|
return []
|
|
1578
1661
|
return [...new Set(value.filter((item)=>typeof item==="string"&&item.length>0))].sort()
|
|
@@ -1604,7 +1687,7 @@ behavior.quickActionVisibility=quickActionVisibility
|
|
|
1604
1687
|
const mcpUpdate={...update.mcp?.states,...update.mcp?.name&&update.mcp.state&&{[update.mcp.name]:update.mcp.state}}
|
|
1605
1688
|
const mcp=parseDesiredMcpStates({...!update.clearMcp&&existing.mcp,...mcpUpdate
|
|
1606
1689
|
})
|
|
1607
|
-
const scope={...Object.keys(behavior).length>0&&{behavior},...(update.layout
|
|
1690
|
+
const scope={...Object.keys(behavior).length>0&&{behavior},...(update.layout??(!update.clearLayout&&existing.layout))&&{layout:parseSectionLayout(update.layout??existing.layout)
|
|
1608
1691
|
},...Object.keys(mcp).length>0&&{mcp}}
|
|
1609
1692
|
const worktrees={...current.worktrees}
|
|
1610
1693
|
if(target.kind==="worktree"){if(isEmptyScope(scope))
|
|
@@ -1719,143 +1802,8 @@ return{global:target.kind==="global"?scope:current.global,worktrees,user:{...ski
|
|
|
1719
1802
|
},...pendingResetAttempt&&{pendingResetAttempt},...typeof (update.user?.autoApprovePermissions??current.user.autoApprovePermissions)==="boolean"&&{autoApprovePermissions:update.user?.autoApprovePermissions??current.user.autoApprovePermissions
|
|
1720
1803
|
}}}}
|
|
1721
1804
|
function targetForLegacyScope(scope){return scope==="global"?{kind:"global"}:{kind:"worktree",key:scope}}
|
|
1722
|
-
function
|
|
1723
|
-
const file=join(directory,"preferences.json")
|
|
1724
|
-
const lock=join(directory,"preferences.lock")
|
|
1725
|
-
let writes=Promise.resolve()
|
|
1726
|
-
async function readLockOwner(){const source=await readFile(lock,"utf8").catch((error)=>{if(errno(error,"ENOENT"))
|
|
1727
|
-
return
|
|
1728
|
-
throw error
|
|
1729
|
-
})
|
|
1730
|
-
if(source===undefined)
|
|
1731
|
-
return
|
|
1732
|
-
try{const value=JSON.parse(source)
|
|
1733
|
-
if(typeof value.token!=="string"||typeof value.pid!=="number")
|
|
1734
|
-
return
|
|
1735
|
-
return{token:value.token,pid:value.pid}} catch{return
|
|
1736
|
-
}}
|
|
1737
|
-
function processIsAlive(pid){try{process.kill(pid,0)
|
|
1738
|
-
return true
|
|
1739
|
-
} catch(error){return !errno(error,"ESRCH")
|
|
1740
|
-
}}
|
|
1741
|
-
async function moveLockToTombstone(suffix){const tombstone=`${lock}.${suffix}.${randomUUID()}`
|
|
1742
|
-
try{await rename(lock,tombstone)
|
|
1743
|
-
} catch(error){if(errno(error,"ENOENT"))
|
|
1744
|
-
return false
|
|
1745
|
-
throw error
|
|
1746
|
-
}
|
|
1747
|
-
await rm(tombstone,{recursive:true,force:true})
|
|
1748
|
-
return true
|
|
1749
|
-
}
|
|
1750
|
-
async function liveRecoveryMarkers(prefix="preferences.lock.recover."){const entries=await readdir(directory)
|
|
1751
|
-
const markers=[]
|
|
1752
|
-
for(const entry of entries){if(!entry.startsWith(prefix)||entry.endsWith(".candidate"))
|
|
1753
|
-
continue
|
|
1754
|
-
const path=join(directory,entry)
|
|
1755
|
-
const markerOwner=await readFile(path,"utf8").then((source)=>JSON.parse(source)).catch(()=>{})
|
|
1756
|
-
if(typeof markerOwner?.pid==="number"&&processIsAlive(markerOwner.pid))
|
|
1757
|
-
markers.push(path)
|
|
1758
|
-
else
|
|
1759
|
-
await unlink(path).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
1760
|
-
throw error
|
|
1761
|
-
})
|
|
1762
|
-
}
|
|
1763
|
-
return markers.sort()
|
|
1764
|
-
}
|
|
1765
|
-
async function reapStaleLock(owner){const token=Buffer.from(owner.token).toString("base64url")
|
|
1766
|
-
const prefix=`preferences.lock.recover.${token}.`
|
|
1767
|
-
const recovery=join(directory,`${prefix}${Date.now().toString().padStart(13, "0")}.${process.pid}.${randomUUID()}`)
|
|
1768
|
-
const markers=await liveRecoveryMarkers(prefix)
|
|
1769
|
-
if(markers.length>0)
|
|
1770
|
-
return false
|
|
1771
|
-
const candidate=`${recovery}.candidate`
|
|
1772
|
-
await writeFile(candidate,JSON.stringify({token:owner.token,pid:process.pid}),{flag:"wx",mode:384})
|
|
1773
|
-
try{await rename(candidate,recovery)
|
|
1774
|
-
await delay(10)
|
|
1775
|
-
const liveMarkers=await liveRecoveryMarkers(prefix)
|
|
1776
|
-
if(liveMarkers[0]!==recovery)
|
|
1777
|
-
return false
|
|
1778
|
-
const current=await readLockOwner()
|
|
1779
|
-
if(current?.token!==owner.token||processIsAlive(current.pid))
|
|
1780
|
-
return false
|
|
1781
|
-
return moveLockToTombstone("stale")
|
|
1782
|
-
} finally{await unlink(candidate).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
1783
|
-
throw error
|
|
1784
|
-
})
|
|
1785
|
-
await unlink(recovery).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
1786
|
-
throw error
|
|
1787
|
-
})
|
|
1788
|
-
}}
|
|
1789
|
-
async function tryAcquireLock(){const token=randomUUID()
|
|
1790
|
-
const candidate=`${lock}.${token}.candidate`
|
|
1791
|
-
await writeFile(candidate,JSON.stringify({token,pid:process.pid}),{flag:"wx",mode:384})
|
|
1792
|
-
try{try{await link(candidate,lock)
|
|
1793
|
-
} catch(error){if(!errno(error,"EEXIST"))
|
|
1794
|
-
throw error
|
|
1795
|
-
const owner=await readLockOwner()
|
|
1796
|
-
if(!owner||processIsAlive(owner.pid))
|
|
1797
|
-
return
|
|
1798
|
-
await reapStaleLock(owner)
|
|
1799
|
-
return
|
|
1800
|
-
}
|
|
1801
|
-
while(true){const markers=await liveRecoveryMarkers()
|
|
1802
|
-
if(markers.length===0)
|
|
1803
|
-
break
|
|
1804
|
-
await delay(10)
|
|
1805
|
-
}
|
|
1806
|
-
const currentOwner=await readLockOwner()
|
|
1807
|
-
if(currentOwner?.token!==token)
|
|
1808
|
-
return
|
|
1809
|
-
return async ()=>{const current=await readLockOwner()
|
|
1810
|
-
if(current?.token!==token)
|
|
1811
|
-
throw new Error("Refusing to release a preferences lock owned by another process")
|
|
1812
|
-
await moveLockToTombstone("released")
|
|
1813
|
-
}} finally{await unlink(candidate).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
1814
|
-
throw error
|
|
1815
|
-
})
|
|
1816
|
-
}}
|
|
1817
|
-
async function acquireLock(){await mkdir(directory,{recursive:true,mode:448})
|
|
1818
|
-
const deadline=Date.now() + LOCK_TIMEOUT_MS
|
|
1819
|
-
while(true){const release=await tryAcquireLock()
|
|
1820
|
-
if(release)
|
|
1821
|
-
return release
|
|
1822
|
-
if(Date.now()>=deadline)
|
|
1823
|
-
throw new Error(`Timed out waiting for preferences lock: ${lock}`)
|
|
1824
|
-
await delay(10)
|
|
1825
|
-
}}
|
|
1826
|
-
async function withLock(operation){const release=await acquireLock()
|
|
1827
|
-
try{return await operation()
|
|
1828
|
-
} finally{await release()
|
|
1829
|
-
}}
|
|
1830
|
-
async function read(){const source=await readFile(file,"utf8").catch((error)=>{if(errno(error,"ENOENT"))
|
|
1831
|
-
return
|
|
1832
|
-
throw error
|
|
1833
|
-
})
|
|
1834
|
-
if(source===undefined)
|
|
1835
|
-
return
|
|
1836
|
-
try{return parsePreferencesDocument(JSON.parse(source))
|
|
1837
|
-
} catch(error){if(error instanceof SyntaxError)
|
|
1838
|
-
throw new Error(`Malformed preferences JSON: ${file}`,{cause:error})
|
|
1839
|
-
throw error
|
|
1840
|
-
}}
|
|
1841
|
-
async function write(value){const temporary=`${file}.${process.pid}.${randomUUID()}.tmp`
|
|
1842
|
-
try{await writeFile(temporary,`${JSON.stringify(value)}
|
|
1843
|
-
`,{mode:384})
|
|
1844
|
-
await rename(temporary,file)
|
|
1845
|
-
} finally{await unlink(temporary).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
1846
|
-
throw error
|
|
1847
|
-
})
|
|
1848
|
-
}}
|
|
1849
|
-
function enqueue(operation){const result=writes.then(operation)
|
|
1850
|
-
writes=result.catch(()=>{})
|
|
1851
|
-
return result
|
|
1805
|
+
function skillKey(skill){return skill.location
|
|
1852
1806
|
}
|
|
1853
|
-
return{load(){return withLock(async ()=>await read()??emptyPreferencesDocument())
|
|
1854
|
-
},update(update){return enqueue(()=>withLock(async ()=>{const current=await read()??emptyPreferencesDocument()
|
|
1855
|
-
await write(applyPreferencesUpdate(current,update))
|
|
1856
|
-
}))
|
|
1857
|
-
},async flush(){await writes
|
|
1858
|
-
}}}
|
|
1859
1807
|
function swap(items,first,second){const value=items[first]
|
|
1860
1808
|
items[first]=items[second]
|
|
1861
1809
|
items[second]=value
|
|
@@ -1869,9 +1817,8 @@ return{behavior:scope.behavior,layout:scope.layout,desiredMcpStates:scope.mcp
|
|
|
1869
1817
|
}}
|
|
1870
1818
|
function mergeSessionLayout(base,session){const order=session.order??base?.order
|
|
1871
1819
|
return{sections:{...base?.sections,...session.sections},expanded:{...base?.expanded,...session.expanded},...order&&{order:[...order]}}}
|
|
1872
|
-
function
|
|
1873
|
-
|
|
1874
|
-
},desiredMcpStates:{}}
|
|
1820
|
+
function createPreferencesModel(ports,defaults,store,configuredInput={}){let configured=configuredInput
|
|
1821
|
+
const builtIns=builtInPreferences(defaults)
|
|
1875
1822
|
const [resolved,setResolved]=createSignal5(resolvePreferences({builtIns,pluginOptions:configured}))
|
|
1876
1823
|
const [skippedSkills,setSkippedSkills]=createSignal5(new Set)
|
|
1877
1824
|
const [onboardingCompleted,setOnboardingCompleted]=createSignal5(false)
|
|
@@ -1901,17 +1848,15 @@ let mcpPresetReconciliation=Promise.resolve()
|
|
|
1901
1848
|
let updateRevision=0
|
|
1902
1849
|
let reloading
|
|
1903
1850
|
let isPersistenceWarningShown=false
|
|
1904
|
-
function skillKey(skill){return skill.location
|
|
1905
|
-
}
|
|
1906
1851
|
function showPersistenceWarning(cause){if(isPersistenceWarningShown)
|
|
1907
1852
|
return
|
|
1908
1853
|
isPersistenceWarningShown=true
|
|
1909
|
-
|
|
1854
|
+
ports.notifications.show({variant:"warning",title:"Navigator settings",message:cause instanceof Error?cause.message:"Navigator settings could not be saved"
|
|
1910
1855
|
})
|
|
1911
1856
|
}
|
|
1912
1857
|
function persist(request){request.catch(showPersistenceWarning)
|
|
1913
1858
|
}
|
|
1914
|
-
function validKey(value){try{return
|
|
1859
|
+
function validKey(value){try{return ports.validKey(value)
|
|
1915
1860
|
} catch{return false
|
|
1916
1861
|
}}
|
|
1917
1862
|
function resolveTarget(target,session=true){const worktree=target.kind==="worktree"?document.worktrees[target.key]:undefined
|
|
@@ -1937,13 +1882,16 @@ return{...configured.workspaceProfiles,...document.user.workspaceProfiles}}
|
|
|
1937
1882
|
function scopeSnapshot(target=selectedTarget()){const scope=target.kind==="global"?document.global:document.worktrees[target.key]
|
|
1938
1883
|
return JSON.stringify({layout:scope?.layout,mcp:scope?.mcp,behavior:scope?.behavior})
|
|
1939
1884
|
}
|
|
1940
|
-
function update(change){updateRevision++
|
|
1885
|
+
function update(change,wait=false){updateRevision++
|
|
1941
1886
|
document=applyPreferencesUpdate(document,change)
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1887
|
+
let request
|
|
1888
|
+
if(isHydrated){request=store.update(change)
|
|
1889
|
+
persist(request)
|
|
1890
|
+
} else
|
|
1945
1891
|
pendingUpdates.push(change)
|
|
1946
1892
|
refreshResolved()
|
|
1893
|
+
if(wait)
|
|
1894
|
+
return request??Promise.resolve()
|
|
1947
1895
|
}
|
|
1948
1896
|
function applyDocument(value){document=value
|
|
1949
1897
|
setSkippedSkills(new Set(value.user.skippedSkillConfirmations))
|
|
@@ -2014,13 +1962,6 @@ function setSessionLayout(layout){setTargetSessionLayout(activeTarget(),layout)
|
|
|
2014
1962
|
}
|
|
2015
1963
|
function setSelectedSessionLayout(layout,replace=false){setTargetSessionLayout(selectedTarget(),layout,replace)
|
|
2016
1964
|
}
|
|
2017
|
-
function normalizePresetName(value){const name=value.trim()
|
|
2018
|
-
if(!name)
|
|
2019
|
-
throw new Error("Enter a preset name")
|
|
2020
|
-
if(name.length>64)
|
|
2021
|
-
throw new Error("Preset names can contain at most 64 characters")
|
|
2022
|
-
return name
|
|
2023
|
-
}
|
|
2024
1965
|
function currentLayoutPreset(){const layout=selectedLayoutResolved().layout
|
|
2025
1966
|
return{sections:{...layout.sections},expanded:{...layout.expanded},order:[...layout.order]
|
|
2026
1967
|
}}
|
|
@@ -2044,7 +1985,7 @@ isApplied=!name
|
|
|
2044
1985
|
else if(mcpPreset.operation==="rename"){isApplied=name===mcpPreset.name&&(mcpPreset.previousName===mcpPreset.name||!actual[mcpPreset.previousName])
|
|
2045
1986
|
} else{isApplied=Boolean(name)&&Object.keys(actual[name]).length===Object.keys(mcpPreset.states).length&&Object.entries(mcpPreset.states).every(([server,state])=>actual[name][server]===state)
|
|
2046
1987
|
}
|
|
2047
|
-
if(!isApplied){
|
|
1988
|
+
if(!isApplied){ports.notifications.show({variant:"warning",title:"MCP presets",message:"Preset changed in another OpenCode instance; reloaded saved presets"
|
|
2048
1989
|
})
|
|
2049
1990
|
}}).catch(showPersistenceWarning)
|
|
2050
1991
|
}
|
|
@@ -2080,13 +2021,14 @@ return
|
|
|
2080
2021
|
setAutoApprovePermissions(value)
|
|
2081
2022
|
update({user:{autoApprovePermissions:value}})
|
|
2082
2023
|
}
|
|
2024
|
+
function codexBindingUpdate(providerID,connectionID,accountID){setCodexAccountBindings((bindings)=>changeCodexBinding(bindings,{providerID,connectionID},accountID))
|
|
2025
|
+
return{user:{codexAccountBinding:{providerID,connectionID,accountID}}}}
|
|
2083
2026
|
function changeCodexConnectionBinding(providerID,connectionID,accountID){if(!isHydrated){load().then(()=>changeCodexConnectionBinding(providerID,connectionID,accountID))
|
|
2084
2027
|
return
|
|
2085
2028
|
}
|
|
2086
2029
|
if(!providerID)
|
|
2087
2030
|
return
|
|
2088
|
-
|
|
2089
|
-
update({user:{codexAccountBinding:{providerID,connectionID,accountID}}})
|
|
2031
|
+
update(codexBindingUpdate(providerID,connectionID,accountID))
|
|
2090
2032
|
}
|
|
2091
2033
|
function toggleFavoriteMcpServer(name){if(!isHydrated){load().then(()=>toggleFavoriteMcpServer(name))
|
|
2092
2034
|
return
|
|
@@ -2112,10 +2054,18 @@ next.add(id)
|
|
|
2112
2054
|
setFavoriteQuickActions(next)
|
|
2113
2055
|
update({user:{favoriteQuickAction:{id,favorite:next.has(id)}}})
|
|
2114
2056
|
}
|
|
2115
|
-
return{
|
|
2057
|
+
return{setConfiguredDefaults(next){batch(()=>{configured=next
|
|
2058
|
+
setLayoutPresets({...configured.layoutPresets,...document.user.layoutPresets})
|
|
2059
|
+
setMcpPresets({...configured.mcpPresets,...document.user.mcpPresets})
|
|
2060
|
+
setMcpServerGroups({...configured.mcpServerGroups,...document.user.mcpServerGroups})
|
|
2061
|
+
refreshResolved()
|
|
2062
|
+
})
|
|
2063
|
+
},sections:()=>resolved().layout.sections,toolbarPins(){revision()
|
|
2116
2064
|
return document.user.toolbarPins??[]
|
|
2117
2065
|
},changeToolbarPin(change){requireHydration()
|
|
2118
2066
|
update({user:{toolbarPin:change}})
|
|
2067
|
+
},saveToolbarPin(change){requireHydration()
|
|
2068
|
+
return update({user:{toolbarPin:change}},true)
|
|
2119
2069
|
},expanded:()=>resolved().layout.expanded,sectionOrder:()=>resolved().layout.order,toggleKey:()=>resolved().behavior.toggleKey,focusKey:()=>resolved().behavior.focusKey,searchKey:()=>resolved().behavior.searchKey,persistMcp:()=>resolved().behavior.persistMcp,limitsRefreshMinutes:()=>resolved().behavior.limitsRefreshMinutes,showSessionTitle:()=>resolved().behavior.showSessionTitle,showSessionDate:()=>resolved().behavior.showSessionDate,startInChatForScope:(scope)=>{revision()
|
|
2120
2070
|
return resolveTarget(targetForScope(scope),false).behavior.startInChat
|
|
2121
2071
|
},cornerFont:()=>resolved().behavior.cornerFont,lspIconStyle:()=>resolved().behavior.lspIconStyle,rowDensity:()=>resolved().behavior.rowDensity,toolbarOverflow:()=>resolved().behavior.toolbarOverflow,selectedToolbarOverflow:()=>selectedResolved().behavior.toolbarOverflow,toggleToolbarOverflow(){load()
|
|
@@ -2169,7 +2119,16 @@ throw new Error("MCP preset no longer exists")
|
|
|
2169
2119
|
update({user:{workspaceProfile:{layout,mcp}}})
|
|
2170
2120
|
},favoriteSkills,favoriteMcpServers,favoriteQuickActions,toggleFavoriteMcpServer,toggleFavoriteQuickAction,recentSkills,recentQuickActions,mcpServerGroups,setMcpServerGroup(name,value){setMcpServerGroups(assignGroup(mcpServerGroups(),name,value))
|
|
2171
2121
|
update({user:{mcpServerGroup:{name,group:value}}})
|
|
2172
|
-
},skillGroups,setSkillGroup:changeSkillGroup,autoApprovePermissions,setAutoApprovePermissions:changeAutoApprovePermissions,codexAccountForModel:(providerID,modelID)=>codexBindingAccount(codexAccountBindings(),{providerID},modelID),setCodexAccountBinding:(providerID,_modelID,accountID)=>changeCodexConnectionBinding(providerID,undefined,accountID),codexAccountForConnection:(providerID,connectionID,modelID)=>codexBindingAccount(codexAccountBindings(),{providerID,connectionID},modelID),setCodexConnectionBinding:changeCodexConnectionBinding,
|
|
2122
|
+
},skillGroups,setSkillGroup:changeSkillGroup,autoApprovePermissions,setAutoApprovePermissions:changeAutoApprovePermissions,codexAccountForModel:(providerID,modelID)=>codexBindingAccount(codexAccountBindings(),{providerID},modelID),setCodexAccountBinding:(providerID,_modelID,accountID)=>changeCodexConnectionBinding(providerID,undefined,accountID),codexAccountForConnection:(providerID,connectionID,modelID)=>codexBindingAccount(codexAccountBindings(),{providerID,connectionID},modelID),setCodexConnectionBinding:changeCodexConnectionBinding,async saveCodexConnectionBinding(providerID,connectionID,accountID){requireHydration()
|
|
2123
|
+
if(!providerID||!accountID)
|
|
2124
|
+
throw new Error("A provider and verified account are required")
|
|
2125
|
+
updateRevision++
|
|
2126
|
+
const request=store.update({user:{codexAccountBinding:{providerID,connectionID,accountID}}})
|
|
2127
|
+
persist(request)
|
|
2128
|
+
await request
|
|
2129
|
+
document=applyPreferencesUpdate(document,codexBindingUpdate(providerID,connectionID,accountID))
|
|
2130
|
+
refreshResolved()
|
|
2131
|
+
},pendingResetAttempt,async beginResetAttempt(attempt){await load()
|
|
2173
2132
|
await store.update({user:{pendingResetAttempt:attempt}})
|
|
2174
2133
|
const current=await store.load()
|
|
2175
2134
|
setPendingResetAttempt(current.user.pendingResetAttempt)
|
|
@@ -2222,6 +2181,9 @@ setSessionLayout({order})
|
|
|
2222
2181
|
},toggleSelectedSection(name){load()
|
|
2223
2182
|
const layout=resolveTarget(selectedTarget()).layout
|
|
2224
2183
|
setSelectedSessionLayout({sections:{[name]:!layout.sections[name]}})
|
|
2184
|
+
},toggleSelectedSectionExpanded(name){load()
|
|
2185
|
+
const layout=resolveTarget(selectedTarget()).layout
|
|
2186
|
+
setSelectedSessionLayout({expanded:{[name]:!layout.expanded[name]}})
|
|
2225
2187
|
},moveSelectedSection(name,direction){load()
|
|
2226
2188
|
const layout=resolveTarget(selectedTarget()).layout
|
|
2227
2189
|
const order=[...layout.order]
|
|
@@ -2234,76 +2196,8 @@ setSelectedSessionLayout({order})
|
|
|
2234
2196
|
},previewLayoutPreset(preset){return layoutPresetPreview(selectedLayoutResolved().layout,preset)
|
|
2235
2197
|
},applyLayoutPreset(preset){load()
|
|
2236
2198
|
setSelectedSessionLayout(layoutPresetPreview(selectedLayoutResolved().layout,preset).next,true)
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
if(Object.keys(layoutPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase())){throw new Error("A preset with this name already exists")
|
|
2240
|
-
}
|
|
2241
|
-
if(Object.keys(layoutPresets()).length>=50)
|
|
2242
|
-
throw new Error("You can save at most 50 layout presets")
|
|
2243
|
-
const layout=currentLayoutPreset()
|
|
2244
|
-
persistLayoutPresets({...layoutPresets(),[name]:layout},{name,layout})
|
|
2245
|
-
return name
|
|
2246
|
-
},updateLayoutPreset(name){if(!layoutPresets()[name])
|
|
2247
|
-
return false
|
|
2248
|
-
const layout=currentLayoutPreset()
|
|
2249
|
-
persistLayoutPresets({...layoutPresets(),[name]:layout},{name,layout})
|
|
2250
|
-
return true
|
|
2251
|
-
},renameLayoutPreset(current,value){const name=normalizePresetName(value)
|
|
2252
|
-
const preset=layoutPresets()[current]
|
|
2253
|
-
if(!preset)
|
|
2254
|
-
throw new Error("Layout preset no longer exists")
|
|
2255
|
-
if(name.toLocaleLowerCase()!==current.toLocaleLowerCase()&&Object.keys(layoutPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase())){throw new Error("A preset with this name already exists")
|
|
2256
|
-
}
|
|
2257
|
-
const next={...layoutPresets()}
|
|
2258
|
-
delete next[current]
|
|
2259
|
-
next[name]=preset
|
|
2260
|
-
persistLayoutPresets(next,{name,layout:preset,previousName:current})
|
|
2261
|
-
return name
|
|
2262
|
-
},deleteLayoutPreset(name){if(!layoutPresets()[name])
|
|
2263
|
-
return false
|
|
2264
|
-
const next={...layoutPresets()}
|
|
2265
|
-
delete next[name]
|
|
2266
|
-
persistLayoutPresets(next,{name})
|
|
2267
|
-
return true
|
|
2268
|
-
},saveMcpPreset(value,states){requireHydration()
|
|
2269
|
-
if(Object.keys(states).length===0)
|
|
2270
|
-
throw new Error("There are no MCP servers to save")
|
|
2271
|
-
const name=normalizePresetName(value)
|
|
2272
|
-
if(Object.keys(mcpPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase())){throw new Error("A preset with this name already exists")
|
|
2273
|
-
}
|
|
2274
|
-
if(Object.keys(mcpPresets()).length>=50)
|
|
2275
|
-
throw new Error("You can save at most 50 MCP presets")
|
|
2276
|
-
const preset={...states}
|
|
2277
|
-
persistMcpPresets({...mcpPresets(),[name]:preset},{operation:"save",name,states:preset})
|
|
2278
|
-
return name
|
|
2279
|
-
},updateMcpPreset(name,states){requireHydration()
|
|
2280
|
-
if(!mcpPresets()[name])
|
|
2281
|
-
return false
|
|
2282
|
-
if(Object.keys(states).length===0)
|
|
2283
|
-
throw new Error("There are no MCP servers to save")
|
|
2284
|
-
const preset={...states}
|
|
2285
|
-
persistMcpPresets({...mcpPresets(),[name]:preset},{operation:"update",name,states:preset})
|
|
2286
|
-
return true
|
|
2287
|
-
},renameMcpPreset(current,value){requireHydration()
|
|
2288
|
-
const name=normalizePresetName(value)
|
|
2289
|
-
const preset=mcpPresets()[current]
|
|
2290
|
-
if(!preset)
|
|
2291
|
-
throw new Error("MCP preset no longer exists")
|
|
2292
|
-
if(name.toLocaleLowerCase()!==current.toLocaleLowerCase()&&Object.keys(mcpPresets()).some((candidate)=>candidate.toLocaleLowerCase()===name.toLocaleLowerCase())){throw new Error("A preset with this name already exists")
|
|
2293
|
-
}
|
|
2294
|
-
const next={...mcpPresets()}
|
|
2295
|
-
delete next[current]
|
|
2296
|
-
next[name]=preset
|
|
2297
|
-
persistMcpPresets(next,{operation:"rename",name,previousName:current})
|
|
2298
|
-
return name
|
|
2299
|
-
},deleteMcpPreset(name){requireHydration()
|
|
2300
|
-
if(!mcpPresets()[name])
|
|
2301
|
-
return false
|
|
2302
|
-
const next={...mcpPresets()}
|
|
2303
|
-
delete next[name]
|
|
2304
|
-
persistMcpPresets(next,{operation:"delete",name})
|
|
2305
|
-
return true
|
|
2306
|
-
},async saveLayoutAsDefault(){await load()
|
|
2199
|
+
},...createPreferencePresetActions({load,requireHydration,layoutPresets,mcpPresets,currentLayoutPreset,persistLayoutPresets,persistMcpPresets
|
|
2200
|
+
}),async saveLayoutAsDefault(){await load()
|
|
2307
2201
|
const target=selectedTarget()
|
|
2308
2202
|
const layout=resolveTarget(target).layout
|
|
2309
2203
|
updateRevision++
|
|
@@ -2366,6 +2260,9 @@ update({target:targetForScope(scope),mcp:{name,state}})
|
|
|
2366
2260
|
},setDesiredMcpStates(scope,states){load()
|
|
2367
2261
|
update({target:targetForScope(scope),mcp:{states}})
|
|
2368
2262
|
}}}
|
|
2263
|
+
function createPreferencesController(api,defaults,store,configured={}){return createPreferencesModel({notifications:{show:(input)=>api.ui.toast({...input,duration:4000})},validKey:(value)=>api.keymap?.parseKeySequence?api.keymap.parseKeySequence(value).length>0:Boolean(value.trim())
|
|
2264
|
+
},defaults,store,configured)
|
|
2265
|
+
}
|
|
2369
2266
|
function record2(value){if(!value||typeof value!=="object"||Array.isArray(value))
|
|
2370
2267
|
return
|
|
2371
2268
|
return value
|
|
@@ -2400,17 +2297,173 @@ function legacyDefaults(config){const{sections,sectionOrder,...behavior}=config
|
|
|
2400
2297
|
return{behavior,layout:{sections,expanded:{},...sectionOrder&&{order:sectionOrder}}}}
|
|
2401
2298
|
function configuredDefaultsFromPluginOptions(options){return mergeConfiguredDefaults(legacyDefaults(pluginConfig(options)),parseConfiguredDefaults(options??{}))
|
|
2402
2299
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2300
|
+
import {randomUUID} from "crypto"
|
|
2301
|
+
import {link,mkdir,readFile,readdir,rename,rm,unlink,writeFile,readFile as readFile2} from "fs/promises"
|
|
2302
|
+
import {join,dirname,join as join2,join as join3} from "path"
|
|
2303
|
+
var LOCK_TIMEOUT_MS=3000
|
|
2304
|
+
function errno(error,code){return error instanceof Error&&"code" in error&&error.code===code
|
|
2305
|
+
}
|
|
2306
|
+
function delay(milliseconds){return new Promise((resolve)=>setTimeout(resolve,milliseconds))
|
|
2307
|
+
}
|
|
2308
|
+
function createPreferencesStore(stateDirectory){const directory=join(stateDirectory,LEGACY_PLUGIN_ID)
|
|
2309
|
+
const file=join(directory,"preferences.json")
|
|
2310
|
+
const lock=join(directory,"preferences.lock")
|
|
2311
|
+
let writes=Promise.resolve()
|
|
2312
|
+
async function readLockOwner(){const source=await readFile(lock,"utf8").catch((error)=>{if(errno(error,"ENOENT"))
|
|
2313
|
+
return
|
|
2314
|
+
throw error
|
|
2315
|
+
})
|
|
2316
|
+
if(source===undefined)
|
|
2317
|
+
return
|
|
2318
|
+
try{const value=JSON.parse(source)
|
|
2319
|
+
if(typeof value.token!=="string"||typeof value.pid!=="number")
|
|
2320
|
+
return
|
|
2321
|
+
return{token:value.token,pid:value.pid}} catch{return
|
|
2322
|
+
}}
|
|
2323
|
+
function processIsAlive(pid){try{process.kill(pid,0)
|
|
2324
|
+
return true
|
|
2325
|
+
} catch(error){return !errno(error,"ESRCH")
|
|
2326
|
+
}}
|
|
2327
|
+
async function moveLockToTombstone(suffix){const tombstone=`${lock}.${suffix}.${randomUUID()}`
|
|
2328
|
+
try{await rename(lock,tombstone)
|
|
2329
|
+
} catch(error){if(errno(error,"ENOENT"))
|
|
2330
|
+
return false
|
|
2331
|
+
throw error
|
|
2332
|
+
}
|
|
2333
|
+
await rm(tombstone,{recursive:true,force:true})
|
|
2334
|
+
return true
|
|
2335
|
+
}
|
|
2336
|
+
async function liveRecoveryMarkers(prefix="preferences.lock.recover."){const entries=await readdir(directory)
|
|
2337
|
+
const markers=[]
|
|
2338
|
+
for(const entry of entries){if(!entry.startsWith(prefix)||entry.endsWith(".candidate"))
|
|
2339
|
+
continue
|
|
2340
|
+
const path=join(directory,entry)
|
|
2341
|
+
const markerOwner=await readFile(path,"utf8").then((source)=>JSON.parse(source)).catch(()=>{})
|
|
2342
|
+
if(typeof markerOwner?.pid==="number"&&processIsAlive(markerOwner.pid))
|
|
2343
|
+
markers.push(path)
|
|
2344
|
+
else
|
|
2345
|
+
await unlink(path).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
2346
|
+
throw error
|
|
2347
|
+
})
|
|
2348
|
+
}
|
|
2349
|
+
return markers.sort()
|
|
2350
|
+
}
|
|
2351
|
+
async function reapStaleLock(owner){const token=Buffer.from(owner.token).toString("base64url")
|
|
2352
|
+
const prefix=`preferences.lock.recover.${token}.`
|
|
2353
|
+
const recovery=join(directory,`${prefix}${Date.now().toString().padStart(13, "0")}.${process.pid}.${randomUUID()}`)
|
|
2354
|
+
const markers=await liveRecoveryMarkers(prefix)
|
|
2355
|
+
if(markers.length>0)
|
|
2356
|
+
return false
|
|
2357
|
+
const candidate=`${recovery}.candidate`
|
|
2358
|
+
await writeFile(candidate,JSON.stringify({token:owner.token,pid:process.pid}),{flag:"wx",mode:384})
|
|
2359
|
+
try{await rename(candidate,recovery)
|
|
2360
|
+
await delay(10)
|
|
2361
|
+
const liveMarkers=await liveRecoveryMarkers(prefix)
|
|
2362
|
+
if(liveMarkers[0]!==recovery)
|
|
2363
|
+
return false
|
|
2364
|
+
const current=await readLockOwner()
|
|
2365
|
+
if(current?.token!==owner.token||processIsAlive(current.pid))
|
|
2366
|
+
return false
|
|
2367
|
+
return moveLockToTombstone("stale")
|
|
2368
|
+
} finally{await unlink(candidate).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
2369
|
+
throw error
|
|
2370
|
+
})
|
|
2371
|
+
await unlink(recovery).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
2372
|
+
throw error
|
|
2373
|
+
})
|
|
2374
|
+
}}
|
|
2375
|
+
async function tryAcquireLock(deadline){const token=randomUUID()
|
|
2376
|
+
const candidate=`${lock}.${token}.candidate`
|
|
2377
|
+
let acquired=false
|
|
2378
|
+
let handedOff=false
|
|
2379
|
+
await writeFile(candidate,JSON.stringify({token,pid:process.pid}),{flag:"wx",mode:384})
|
|
2380
|
+
try{try{await link(candidate,lock)
|
|
2381
|
+
acquired=true
|
|
2382
|
+
} catch(error){if(!errno(error,"EEXIST"))
|
|
2383
|
+
throw error
|
|
2384
|
+
const owner=await readLockOwner()
|
|
2385
|
+
if(!owner||processIsAlive(owner.pid))
|
|
2386
|
+
return
|
|
2387
|
+
await reapStaleLock(owner)
|
|
2388
|
+
return
|
|
2389
|
+
}
|
|
2390
|
+
while(true){const markers=await liveRecoveryMarkers()
|
|
2391
|
+
if(markers.length===0)
|
|
2392
|
+
break
|
|
2393
|
+
if(Date.now()>=deadline)
|
|
2394
|
+
return
|
|
2395
|
+
await delay(10)
|
|
2396
|
+
}
|
|
2397
|
+
const currentOwner=await readLockOwner()
|
|
2398
|
+
if(currentOwner?.token!==token)
|
|
2399
|
+
return
|
|
2400
|
+
handedOff=true
|
|
2401
|
+
return async ()=>{const current=await readLockOwner()
|
|
2402
|
+
if(current?.token!==token)
|
|
2403
|
+
throw new Error("Refusing to release a preferences lock owned by another process")
|
|
2404
|
+
await moveLockToTombstone("released")
|
|
2405
|
+
}} finally{if(acquired&&!handedOff){const current=await readLockOwner()
|
|
2406
|
+
if(current?.token===token)
|
|
2407
|
+
await moveLockToTombstone("released")
|
|
2408
|
+
}
|
|
2409
|
+
await unlink(candidate).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
2410
|
+
throw error
|
|
2411
|
+
})
|
|
2412
|
+
}}
|
|
2413
|
+
async function acquireLock(){await mkdir(directory,{recursive:true,mode:448})
|
|
2414
|
+
const deadline=Date.now() + LOCK_TIMEOUT_MS
|
|
2415
|
+
while(true){const release=await tryAcquireLock(deadline)
|
|
2416
|
+
if(release)
|
|
2417
|
+
return release
|
|
2418
|
+
if(Date.now()>=deadline)
|
|
2419
|
+
throw new Error(`Timed out waiting for preferences lock: ${lock}`)
|
|
2420
|
+
await delay(10)
|
|
2421
|
+
}}
|
|
2422
|
+
async function withLock(operation){const release=await acquireLock()
|
|
2423
|
+
try{return await operation()
|
|
2424
|
+
} finally{await release()
|
|
2425
|
+
}}
|
|
2426
|
+
async function read(){const source=await readFile(file,"utf8").catch((error)=>{if(errno(error,"ENOENT"))
|
|
2427
|
+
return
|
|
2428
|
+
throw error
|
|
2429
|
+
})
|
|
2430
|
+
if(source===undefined)
|
|
2431
|
+
return
|
|
2432
|
+
try{return parsePreferencesDocument(JSON.parse(source))
|
|
2433
|
+
} catch(error){if(error instanceof SyntaxError)
|
|
2434
|
+
throw new Error(`Malformed preferences JSON: ${file}`,{cause:error})
|
|
2435
|
+
throw error
|
|
2436
|
+
}}
|
|
2437
|
+
async function write(value){const temporary=`${file}.${process.pid}.${randomUUID()}.tmp`
|
|
2438
|
+
try{await writeFile(temporary,`${JSON.stringify(value)}
|
|
2439
|
+
`,{mode:384})
|
|
2440
|
+
await rename(temporary,file)
|
|
2441
|
+
} finally{await unlink(temporary).catch((error)=>{if(!errno(error,"ENOENT"))
|
|
2442
|
+
throw error
|
|
2443
|
+
})
|
|
2444
|
+
}}
|
|
2445
|
+
function enqueue(operation){const result=writes.then(operation)
|
|
2446
|
+
writes=result.catch(()=>{})
|
|
2447
|
+
return result
|
|
2448
|
+
}
|
|
2449
|
+
return{load(){return withLock(async ()=>await read()??emptyPreferencesDocument())
|
|
2450
|
+
},update(update){return enqueue(()=>withLock(async ()=>{const current=await read()??emptyPreferencesDocument()
|
|
2451
|
+
await write(applyPreferencesUpdate(current,update))
|
|
2452
|
+
}))
|
|
2453
|
+
},async flush(){await writes
|
|
2454
|
+
}}}
|
|
2455
|
+
|
|
2456
|
+
function createSkillModel(options){const{source,lifecycle}=options
|
|
2457
|
+
const [skills,setSkills]=createSignal6({})
|
|
2458
|
+
const refreshing=new Map
|
|
2459
|
+
const targets=new Map
|
|
2460
|
+
const requests=createRequestState(lifecycle.signal)
|
|
2461
|
+
const promptRequests=createRequestState(lifecycle.signal)
|
|
2462
|
+
let activeTarget
|
|
2410
2463
|
let leases=new Set
|
|
2411
|
-
function target(){return
|
|
2464
|
+
function target(){return options.target()
|
|
2412
2465
|
}
|
|
2413
|
-
function list(current=target()){return source?source.list(current):skills()[current.key]??[]
|
|
2466
|
+
function list(current=target()){return options.cache==="source"?source.list(current):skills()[current.key]??[]
|
|
2414
2467
|
}
|
|
2415
2468
|
function error(current=target()){return requests.state(current.key).error?.message
|
|
2416
2469
|
}
|
|
@@ -2421,8 +2474,8 @@ targets.set(current.key,current)
|
|
|
2421
2474
|
const requestState=requests.start(current.key,"refresh skills",current.key in skills(),force)
|
|
2422
2475
|
if(!requestState)
|
|
2423
2476
|
return pending
|
|
2424
|
-
const request=
|
|
2425
|
-
if(
|
|
2477
|
+
const request=source.refresh(current,force,requestState.signal).then((data)=>{const items=[...data].sort((a,b)=>a.name.localeCompare(b.name))
|
|
2478
|
+
if(options.cache==="model"&&requestState.isCurrent())
|
|
2426
2479
|
setSkills((value)=>({...value,[current.key]:items}))
|
|
2427
2480
|
requestState.succeed()
|
|
2428
2481
|
return items
|
|
@@ -2435,9 +2488,9 @@ refreshing.delete(current.key)
|
|
|
2435
2488
|
refreshing.set(current.key,request)
|
|
2436
2489
|
return request
|
|
2437
2490
|
}
|
|
2438
|
-
const unsubscribe=
|
|
2491
|
+
const unsubscribe=options.onReconnect(()=>{for(const current of targets.values()){retryBackgroundRefresh(()=>refresh(current),{signal:lifecycle.signal}).catch(()=>{})
|
|
2439
2492
|
}})
|
|
2440
|
-
|
|
2493
|
+
lifecycle.onDispose(unsubscribe)
|
|
2441
2494
|
return{list,error,refresh,target,state(current=target()){return requests.state(current.key)
|
|
2442
2495
|
},retry(current=target()){return refresh(current,true)
|
|
2443
2496
|
},activate(current=target()){targets.set(current.key,current)
|
|
@@ -2463,15 +2516,24 @@ throw new DOMException("Sidebar context changed","AbortError")
|
|
|
2463
2516
|
const request=promptRequests.start(current.key,"insert skill",false,true)
|
|
2464
2517
|
if(!request)
|
|
2465
2518
|
return false
|
|
2466
|
-
try{const result=
|
|
2519
|
+
try{const result=selected?await source.use(current,selected,request.signal):await options.useNamed?.(current,name,request.signal)
|
|
2467
2520
|
if(!request.isCurrent())
|
|
2468
2521
|
return false
|
|
2469
2522
|
request.succeed()
|
|
2470
|
-
return result
|
|
2523
|
+
return result??false
|
|
2471
2524
|
} catch(error_){request.fail(error_)
|
|
2472
2525
|
throw error_
|
|
2473
2526
|
} finally{request.finish()
|
|
2474
2527
|
}}}}
|
|
2528
|
+
function createSkillController(api,source){const useNamed=async (target,name,signal)=>{const result=await api.client.tui.appendPrompt({...target.routing,text:`/${name} `},{throwOnError:true,signal})
|
|
2529
|
+
return result.data??false
|
|
2530
|
+
}
|
|
2531
|
+
return createSkillModel({lifecycle:api.lifecycle,target:()=>currentLocation(api),cache:source?"source":"model",source:source??{list:()=>[],async refresh(target,_force,signal){const result=await api.client.app.skills(target.routing,{throwOnError:true,signal})
|
|
2532
|
+
return result.data??[]
|
|
2533
|
+
},use:(target,skill,signal)=>useNamed(target,skill.name,signal)
|
|
2534
|
+
},onReconnect:(listener)=>api.event.on("server.connected",listener),useNamed
|
|
2535
|
+
})
|
|
2536
|
+
}
|
|
2475
2537
|
var BUCKET_RANK2={Favorites:0,Ungrouped:2}
|
|
2476
2538
|
function buildSkillGroupedView(items,favorites,groups,recent){return items.map((item)=>({...item,assignedGroup:groups[item.location],bucket:favorites.has(item.location)?"Favorites":groups[item.location]??"Ungrouped"
|
|
2477
2539
|
})).sort((left,right)=>{const rank=(BUCKET_RANK2[left.bucket]??1)-(BUCKET_RANK2[right.bucket]??1)
|
|
@@ -2725,34 +2787,40 @@ return "Skill source changed"
|
|
|
2725
2787
|
return "Multiple sources share this skill name"
|
|
2726
2788
|
return
|
|
2727
2789
|
}
|
|
2728
|
-
function
|
|
2729
|
-
}}
|
|
2790
|
+
function createSkillActivation(input){const skill={...input.skill}
|
|
2730
2791
|
const target=input.controller.target()
|
|
2731
|
-
|
|
2732
|
-
const current=()=>target.key===input.controller.target().key&&route===JSON.stringify(input.api.route.current)
|
|
2733
|
-
const report=(error)=>{if(!isAbortError(error))
|
|
2734
|
-
input.api.ui.toast({variant:"error",title:"Skills",message:String(error),duration:5000
|
|
2735
|
-
})
|
|
2736
|
-
}
|
|
2737
|
-
function check(){if(!current())
|
|
2792
|
+
function check(){if(!input.current()||target.key!==input.controller.target().key)
|
|
2738
2793
|
throw new DOMException("Skill target changed; choose it again","AbortError")
|
|
2739
|
-
const reason=skillUnavailable(input.controller,
|
|
2794
|
+
const reason=skillUnavailable(input.controller,skill)
|
|
2740
2795
|
if(reason)
|
|
2741
2796
|
throw new Error(reason)
|
|
2742
2797
|
}
|
|
2743
|
-
async
|
|
2798
|
+
return{skill,check,shouldConfirm:()=>input.access.shouldConfirmSkill(skill),async run(skip=false){check()
|
|
2744
2799
|
if(skip)
|
|
2745
|
-
input.access.skipSkillConfirmation(
|
|
2800
|
+
input.access.skipSkillConfirmation(skill)
|
|
2746
2801
|
input.beforeInsert?.()
|
|
2747
|
-
|
|
2748
|
-
|
|
2802
|
+
const inserted=await input.controller.use(target,skill.name,skill)
|
|
2803
|
+
if(inserted)
|
|
2804
|
+
await input.access.recordSkillUse?.(skill)
|
|
2805
|
+
return inserted
|
|
2806
|
+
}}}
|
|
2807
|
+
function activateSkill(input){input={...input,skill:{...input.skill
|
|
2808
|
+
}}
|
|
2809
|
+
const route=JSON.stringify(input.api.route.current)
|
|
2810
|
+
const activation=createSkillActivation({...input,current:()=>route===JSON.stringify(input.api.route.current)
|
|
2811
|
+
})
|
|
2812
|
+
const report=(error)=>{if(!isAbortError(error))
|
|
2813
|
+
input.api.ui.toast({variant:"error",title:"Skills",message:String(error),duration:5000
|
|
2814
|
+
})
|
|
2815
|
+
}
|
|
2816
|
+
async function insert(skip=false){try{await activation.run(skip)
|
|
2749
2817
|
} catch(error){report(error)
|
|
2750
2818
|
}}
|
|
2751
|
-
try{check()
|
|
2819
|
+
try{activation.check()
|
|
2752
2820
|
} catch(error){report(error)
|
|
2753
2821
|
return
|
|
2754
2822
|
}
|
|
2755
|
-
if(!
|
|
2823
|
+
if(!activation.shouldConfirm()){insert()
|
|
2756
2824
|
return
|
|
2757
2825
|
}
|
|
2758
2826
|
input.dialogs.open(()=>_$createComponent(SkillDialog,{get api(){return input.api
|
|
@@ -2775,7 +2843,7 @@ return value
|
|
|
2775
2843
|
}
|
|
2776
2844
|
function changed(){setRevision((value)=>value + 1)
|
|
2777
2845
|
}
|
|
2778
|
-
return{observeStatus(key,sessionID,status,at=now()){const value=state(key)
|
|
2846
|
+
return{observeStatus(key,sessionID,status,at=now(),nativeStart){const value=state(key)
|
|
2779
2847
|
const run=value.active.get(sessionID)
|
|
2780
2848
|
const isKnown=value.known.has(sessionID)
|
|
2781
2849
|
value.known.add(sessionID)
|
|
@@ -2783,10 +2851,17 @@ if(status.type==="idle"){if(!run)
|
|
|
2783
2851
|
return
|
|
2784
2852
|
value.active.delete(sessionID)
|
|
2785
2853
|
value.recent=[{...run,finishedAt:Math.max(at,run.startedAt),outcome:run.outcome??"finished"},...value.recent].sort((a,b)=>b.finishedAt-a.finishedAt||a.sessionID.localeCompare(b.sessionID)).slice(0,10)
|
|
2786
|
-
} else{
|
|
2854
|
+
} else{const exact=typeof nativeStart==="number"&&Number.isFinite(nativeStart)&&nativeStart>0&&nativeStart<=at?nativeStart:undefined
|
|
2855
|
+
if(run){if(exact===undefined||run.nativeStartedAt!==undefined&&exact<=run.nativeStartedAt)
|
|
2856
|
+
return
|
|
2857
|
+
const next=exact>run.startedAt?{sessionID}:run
|
|
2858
|
+
value.active.set(sessionID,{...next,startedAt:exact,nativeStartedAt:exact,startedBeforeObservation:false
|
|
2859
|
+
})
|
|
2860
|
+
changed()
|
|
2787
2861
|
return
|
|
2862
|
+
}
|
|
2788
2863
|
value.recent=value.recent.filter((item)=>item.sessionID!==sessionID)
|
|
2789
|
-
value.active.set(sessionID,{sessionID,startedAt:at,startedBeforeObservation
|
|
2864
|
+
value.active.set(sessionID,{sessionID,startedAt:exact??at,startedBeforeObservation:exact===undefined&&!isKnown,...exact!==undefined&&{nativeStartedAt:exact}})
|
|
2790
2865
|
}
|
|
2791
2866
|
changed()
|
|
2792
2867
|
},observeError(key,sessionID,error,at=now()){if(!sessionID)
|
|
@@ -2798,7 +2873,7 @@ return
|
|
|
2798
2873
|
const isCancelled=error?.name==="MessageAbortedError"
|
|
2799
2874
|
const data=error&&"data" in error?error.data:undefined
|
|
2800
2875
|
const fallbackMessage=isCancelled?"Session aborted":"Session error"
|
|
2801
|
-
const message=data&&"message" in data&&typeof data.message==="string"?data.message:fallbackMessage
|
|
2876
|
+
const message=data&&typeof data==="object"&&"message" in data&&typeof data.message==="string"?data.message:fallbackMessage
|
|
2802
2877
|
const next={...run,startedAt:Math.min(run.startedAt,at),outcome:isCancelled?"cancelled":"error",errorMessage:message
|
|
2803
2878
|
}
|
|
2804
2879
|
if(value.active.has(sessionID))
|
|
@@ -2821,7 +2896,7 @@ return targets.get(key)?.recent??[]
|
|
|
2821
2896
|
var POLL_INTERVAL_MS=5000
|
|
2822
2897
|
function activeStatus(status){return status?.type==="busy"||status?.type==="retry"
|
|
2823
2898
|
}
|
|
2824
|
-
function
|
|
2899
|
+
function createSubagentModel(options){const source=options.source
|
|
2825
2900
|
const now=options.now??Date.now
|
|
2826
2901
|
const history=createSubagentHistory({now})
|
|
2827
2902
|
const [children,setChildren]=createSignal8({})
|
|
@@ -2832,14 +2907,13 @@ const [unavailable,setUnavailable]=createSignal8({})
|
|
|
2832
2907
|
const targets=new Map
|
|
2833
2908
|
const remoteFailures=new Map
|
|
2834
2909
|
const remoteRetryAt=new Map
|
|
2835
|
-
const requests=createRequestState(
|
|
2910
|
+
const requests=createRequestState(options.lifecycle.signal)
|
|
2836
2911
|
let activeTarget
|
|
2912
|
+
let leases=new Set
|
|
2837
2913
|
let pollTimer
|
|
2838
|
-
const snapshots=()=>source?Object.fromEntries([...targets].map(([key,current])=>[key,source.list(current.parentID)])):children()
|
|
2839
|
-
function target(parentID){
|
|
2840
|
-
|
|
2841
|
-
const workspace=parent?.workspaceID
|
|
2842
|
-
return{key:JSON.stringify([parentID,directory,workspace??null]),parentID,routing:{directory,...workspace&&{workspace}}}}
|
|
2914
|
+
const snapshots=()=>options.cache==="source"?Object.fromEntries([...targets].map(([key,current])=>[key,source.list(current.parentID)])):children()
|
|
2915
|
+
function target(parentID){return options.target(parentID)
|
|
2916
|
+
}
|
|
2843
2917
|
function applySession(items,parentID,mutation){const next=items.filter((item)=>item.id!==(mutation.type==="upsert"?mutation.info.id:mutation.sessionID))
|
|
2844
2918
|
if(mutation.type==="upsert"&&mutation.info.parentID===parentID)
|
|
2845
2919
|
next.push(mutation.info)
|
|
@@ -2854,18 +2928,19 @@ history.remove(key,sessionID)
|
|
|
2854
2928
|
}
|
|
2855
2929
|
for(const journal of journals)
|
|
2856
2930
|
journal.sessions.push(mutation)
|
|
2857
|
-
if(
|
|
2931
|
+
if(options.cache==="model")
|
|
2858
2932
|
setChildren((current)=>Object.fromEntries(Object.entries(current).map(([key,items])=>[key,applySession(items,targets.get(key).parentID,mutation)])))
|
|
2859
2933
|
}
|
|
2860
|
-
function recordStatus(input){const mutation={...input,at:now(),observed:new Set
|
|
2934
|
+
function recordStatus(input){const mutation={...input,at:now(),startedAt:source?.startedAt?.(input.sessionID),observed:new Set
|
|
2935
|
+
}
|
|
2861
2936
|
for(const journal of journals)
|
|
2862
2937
|
journal.statuses.push(mutation)
|
|
2863
|
-
|
|
2938
|
+
batch2(()=>{for(const [key,items] of Object.entries(snapshots())){if(items.every((item)=>item.id!==mutation.sessionID))
|
|
2864
2939
|
continue
|
|
2865
|
-
history.observeStatus(key,mutation.sessionID,mutation.status,mutation.at)
|
|
2940
|
+
history.observeStatus(key,mutation.sessionID,mutation.status,mutation.at,mutation.startedAt)
|
|
2866
2941
|
mutation.observed.add(key)
|
|
2867
2942
|
}
|
|
2868
|
-
if(
|
|
2943
|
+
if(options.cache==="model")
|
|
2869
2944
|
setStatuses((current)=>Object.fromEntries(Object.entries(current).map(([key,value])=>[key,applyStatus(value,mutation)])))
|
|
2870
2945
|
})
|
|
2871
2946
|
}
|
|
@@ -2891,14 +2966,13 @@ if(!requestState){journals.delete(journal)
|
|
|
2891
2966
|
return pending
|
|
2892
2967
|
}
|
|
2893
2968
|
const signal=AbortSignal.any([requestState.signal,AbortSignal.timeout(5000)])
|
|
2894
|
-
const request=
|
|
2895
|
-
}]):Promise.all([api.client.session.children({sessionID:current.parentID,...current.routing},{throwOnError:true,signal}),api.client.session.status(current.routing,{throwOnError:true,signal})])).then(async ([childResult,statusResult])=>{let nextChildren=childResult.data??[]
|
|
2969
|
+
const request=options.snapshot(current,signal).then(async (snapshot)=>{let nextChildren=snapshot.sessions
|
|
2896
2970
|
for(const mutation of journal.sessions)
|
|
2897
2971
|
nextChildren=applySession(nextChildren,current.parentID,mutation)
|
|
2898
|
-
let nextStatuses={...
|
|
2972
|
+
let nextStatuses={...snapshot.statuses}
|
|
2899
2973
|
const missing={}
|
|
2900
|
-
const remoteStatuses=
|
|
2901
|
-
return(remoteRetryAt.get(key)??0)<=now()?
|
|
2974
|
+
const remoteStatuses=options.remoteStatus?await Promise.all(nextChildren.map(async (session)=>{const key=JSON.stringify([current.key,session.id])
|
|
2975
|
+
return(remoteRetryAt.get(key)??0)<=now()?options.remoteStatus(session,requestState.signal):{sessionID:session.id,failed:true,cooldown:true,status:undefined}})):[]
|
|
2902
2976
|
if(!requestState.isCurrent())
|
|
2903
2977
|
return
|
|
2904
2978
|
for(const result of remoteStatuses){if(!result)
|
|
@@ -2924,17 +2998,17 @@ for(const mutation of journal.sessions)
|
|
|
2924
2998
|
nextChildren=applySession(nextChildren,current.parentID,mutation)
|
|
2925
2999
|
for(const mutation of journal.statuses)
|
|
2926
3000
|
nextStatuses=applyStatus(nextStatuses,mutation)
|
|
2927
|
-
if(requestState.isCurrent()){
|
|
3001
|
+
if(requestState.isCurrent()){batch2(()=>{for(const session of nextChildren){const mutations=journal.statuses.filter((mutation)=>mutation.sessionID===session.id)
|
|
2928
3002
|
if(mutations.length>0){for(const mutation of mutations){if(!mutation.observed.has(current.key))
|
|
2929
|
-
history.observeStatus(current.key,session.id,mutation.status,mutation.at)
|
|
3003
|
+
history.observeStatus(current.key,session.id,mutation.status,mutation.at,mutation.startedAt)
|
|
2930
3004
|
}} else if(!missing[session.id]){const status=nextStatuses[session.id]??{type:"idle"}
|
|
2931
3005
|
nextStatuses[session.id]=status
|
|
2932
|
-
history.observeStatus(current.key,session.id,status)
|
|
3006
|
+
history.observeStatus(current.key,session.id,status,now(),source?.startedAt?.(session.id))
|
|
2933
3007
|
}
|
|
2934
3008
|
for(const mutation of journal.errors){if(mutation.sessionID===session.id&&!mutation.observed.has(current.key))
|
|
2935
3009
|
history.observeError(current.key,session.id,mutation.error,mutation.at)
|
|
2936
3010
|
}}
|
|
2937
|
-
if(
|
|
3011
|
+
if(options.cache==="model"){setChildren((value)=>({...value,[current.key]:nextChildren}))
|
|
2938
3012
|
setStatuses((value)=>({...value,[current.key]:nextStatuses}))
|
|
2939
3013
|
}
|
|
2940
3014
|
setUnavailable((value)=>({...value,[current.key]:missing}))
|
|
@@ -2954,8 +3028,8 @@ return request
|
|
|
2954
3028
|
function refresh(parentID,force=false){return refreshTarget(target(parentID),force)
|
|
2955
3029
|
}
|
|
2956
3030
|
function list(parentID){const current=target(parentID)
|
|
2957
|
-
const currentStatuses=source?Object.fromEntries(source.list(parentID).map((item)=>[item.id,source.status(item.id)])):statuses()[current.key]??{}
|
|
2958
|
-
return(snapshots()[current.key]??[]).map((session)=>({session,status:currentStatuses[session.id]??(unavailable()[current.key]?.[session.id]?undefined:
|
|
3031
|
+
const currentStatuses=options.cache==="source"?Object.fromEntries(source.list(parentID).map((item)=>[item.id,source.status(item.id)])):statuses()[current.key]??{}
|
|
3032
|
+
return(snapshots()[current.key]??[]).map((session)=>({session,status:currentStatuses[session.id]??(unavailable()[current.key]?.[session.id]?undefined:source.status(session.id))
|
|
2959
3033
|
})).filter((item)=>activeStatus(item.status)).map((item)=>({...item,run:history.active(current.key,item.session.id),unavailable:unavailable()[current.key]?.[item.session.id]
|
|
2960
3034
|
}))
|
|
2961
3035
|
}
|
|
@@ -2965,14 +3039,25 @@ return history.recent(current.key).flatMap((run)=>{const session=sessions.get(ru
|
|
|
2965
3039
|
return session?[{session,status:{type:"idle"},run,unavailable:false}]:[]
|
|
2966
3040
|
})
|
|
2967
3041
|
}
|
|
2968
|
-
const unsubscribe=
|
|
2969
|
-
|
|
2970
|
-
}
|
|
3042
|
+
const unsubscribe=options.subscribe((event)=>{switch(event.type){case "upsert":{recordSession(event)
|
|
3043
|
+
break
|
|
3044
|
+
}
|
|
3045
|
+
case "remove":{recordSession(event)
|
|
3046
|
+
recordStatus({sessionID:event.sessionID,status:{type:"idle"}})
|
|
3047
|
+
break
|
|
3048
|
+
}
|
|
3049
|
+
case "status":{recordStatus(event)
|
|
3050
|
+
break
|
|
3051
|
+
}
|
|
3052
|
+
case "error":{recordError(event)
|
|
3053
|
+
break
|
|
3054
|
+
}
|
|
3055
|
+
case "reconnect":{const current=activeTarget?targets.get(activeTarget):undefined
|
|
2971
3056
|
if(current)
|
|
2972
|
-
retryBackgroundRefresh(()=>refreshTarget(current),{signal:
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
3057
|
+
retryBackgroundRefresh(()=>refreshTarget(current),{signal:options.lifecycle.signal}).catch(()=>{})
|
|
3058
|
+
break
|
|
3059
|
+
}}})
|
|
3060
|
+
options.lifecycle.onDispose(()=>{unsubscribe()
|
|
2976
3061
|
if(pollTimer)
|
|
2977
3062
|
clearTimeout(pollTimer)
|
|
2978
3063
|
})
|
|
@@ -2985,15 +3070,18 @@ clearTimeout(pollTimer)
|
|
|
2985
3070
|
requests.abortAll()
|
|
2986
3071
|
refreshing.clear()
|
|
2987
3072
|
activeTarget=current.key
|
|
2988
|
-
|
|
3073
|
+
leases=new Set
|
|
3074
|
+
const poll=()=>{if(activeTarget!==current.key||options.lifecycle.signal.aborted)
|
|
2989
3075
|
return
|
|
2990
3076
|
refreshTarget(current).catch(()=>{})
|
|
2991
3077
|
pollTimer=setTimeout(poll,POLL_INTERVAL_MS)
|
|
2992
3078
|
}
|
|
2993
|
-
if(
|
|
3079
|
+
if(options.cache==="model")
|
|
2994
3080
|
pollTimer=setTimeout(poll,POLL_INTERVAL_MS)
|
|
2995
3081
|
}
|
|
2996
|
-
|
|
3082
|
+
const lease=Symbol(current.key)
|
|
3083
|
+
leases.add(lease)
|
|
3084
|
+
return()=>{if(activeTarget!==current.key||!leases.delete(lease)||leases.size>0)
|
|
2997
3085
|
return
|
|
2998
3086
|
activeTarget=undefined
|
|
2999
3087
|
if(pollTimer)
|
|
@@ -3001,7 +3089,7 @@ clearTimeout(pollTimer)
|
|
|
3001
3089
|
pollTimer=undefined
|
|
3002
3090
|
requests.abortAll()
|
|
3003
3091
|
refreshing.clear()
|
|
3004
|
-
}},open(sessionID){
|
|
3092
|
+
}},open(sessionID){options.open(sessionID)
|
|
3005
3093
|
}}}
|
|
3006
3094
|
async function fetchDevTeamStatus(session,signal){const metadata=session.metadata
|
|
3007
3095
|
const devTeam=metadata&&typeof metadata.devTeam==="object"?metadata.devTeam:undefined
|
|
@@ -3022,10 +3110,26 @@ return{sessionID:session.id,failed:true}
|
|
|
3022
3110
|
const status=statuses[session.id]
|
|
3023
3111
|
if(status!==undefined&&(!status||typeof status!=="object"||!("type" in status)||!["idle","busy","retry"].includes(String(status.type))))
|
|
3024
3112
|
return{sessionID:session.id,failed:true}
|
|
3025
|
-
if(status&&typeof status==="object"&&"type" in status&&status.type==="retry"&&(!("next" in status)||typeof status.next!=="number"||!Number.isFinite(status.next)||!("attempt" in status)||typeof status.attempt!=="number"||!("message" in status)||typeof status.message!=="string"))
|
|
3113
|
+
if(status&&typeof status==="object"&&"type" in status&&status.type==="retry"&&(!("next" in status)||typeof status.next!=="number"||!Number.isFinite(status.next)||!("attempt" in status)||typeof status.attempt!=="number"||!("message" in status)||typeof status.message!=="string"))
|
|
3114
|
+
return{sessionID:session.id,failed:true}
|
|
3026
3115
|
return{sessionID:session.id,status}} catch{if(signal.aborted)
|
|
3027
3116
|
return
|
|
3028
3117
|
return{sessionID:session.id,failed:true}}}
|
|
3118
|
+
function createSubagentController(api,options={}){const source=options.source
|
|
3119
|
+
return createSubagentModel({now:options.now,lifecycle:api.lifecycle,cache:source?"source":"model",source:source??{list:()=>[],status:(id)=>api.state.session.status(id)??{type:"idle"},refresh:async ()=>{}},target(parentID){const parent=api.state.session.get(parentID)
|
|
3120
|
+
const directory=parent?.directory??api.state.path.directory
|
|
3121
|
+
const workspace=parent?.workspaceID
|
|
3122
|
+
return{key:JSON.stringify([parentID,directory,workspace??null]),parentID,routing:{directory,...workspace&&{workspace}}}},async snapshot(target,signal){if(source){await source.refresh(target.parentID,signal)
|
|
3123
|
+
const sessions=source.list(target.parentID)
|
|
3124
|
+
return{sessions,statuses:Object.fromEntries(sessions.map((session)=>[session.id,source.status(session.id)]))
|
|
3125
|
+
}}
|
|
3126
|
+
const [children,statuses]=await Promise.all([api.client.session.children({sessionID:target.parentID,...target.routing},{throwOnError:true,signal}),api.client.session.status(target.routing,{throwOnError:true,signal})])
|
|
3127
|
+
return{sessions:children.data??[],statuses:statuses.data??{}}},remoteStatus:source?undefined:fetchDevTeamStatus,subscribe(listener){const unsubscribe=[api.event.on("session.created",(event)=>listener({type:"upsert",info:event.properties.info})),api.event.on("session.updated",(event)=>listener({type:"upsert",info:event.properties.info})),api.event.on("session.deleted",(event)=>listener({type:"remove",sessionID:event.properties.sessionID})),api.event.on("session.status",(event)=>listener({type:"status",...event.properties})),api.event.on("session.error",(event)=>listener({type:"error",...event.properties})),api.event.on("session.idle",(event)=>listener({type:"status",sessionID:event.properties.sessionID,status:{type:"idle"}})),api.event.on("server.connected",()=>listener({type:"reconnect"}))]
|
|
3128
|
+
return()=>{for(const dispose of unsubscribe)
|
|
3129
|
+
dispose()
|
|
3130
|
+
}},open:(sessionID)=>api.route.navigate("session",{sessionID})
|
|
3131
|
+
})
|
|
3132
|
+
}
|
|
3029
3133
|
|
|
3030
3134
|
function filterSubagentView(rows,mode,query){const needle=query.trim().toLocaleLowerCase()
|
|
3031
3135
|
return rows.filter((row)=>{if(mode==="active"&&row.group!=="Active")
|
|
@@ -3063,30 +3167,32 @@ onCleanup6(()=>clearInterval(timer))
|
|
|
3063
3167
|
return now
|
|
3064
3168
|
}
|
|
3065
3169
|
|
|
3066
|
-
function
|
|
3170
|
+
function createTodoModel(options){const [sessions,setSessions]=createSignal10({})
|
|
3067
3171
|
const refreshing=new Map
|
|
3068
3172
|
const revisions=new Map
|
|
3069
|
-
const requests=createRequestState(
|
|
3173
|
+
const requests=createRequestState(options.lifecycle.signal)
|
|
3070
3174
|
let activeTarget
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
return{key:JSON.stringify([sessionID,directory,workspace??null]),sessionID,routing:{directory,...workspace&&{workspace}}}}
|
|
3175
|
+
let leases=new Set
|
|
3176
|
+
function target(sessionID){return options.target(sessionID)
|
|
3177
|
+
}
|
|
3075
3178
|
function set(current,todos){setSessions((value)=>({...value,[current.key]:todos}))
|
|
3076
3179
|
}
|
|
3077
3180
|
function list(sessionID){const current=target(sessionID)
|
|
3078
|
-
return sessions()[current.key]??
|
|
3181
|
+
return options.cache==="source"?options.source.list(sessionID):sessions()[current.key]??options.source.list(sessionID)
|
|
3079
3182
|
}
|
|
3080
|
-
|
|
3183
|
+
function refresh(sessionID,force=false){if(options.lifecycle.signal.aborted)
|
|
3184
|
+
return Promise.resolve([])
|
|
3185
|
+
const current=target(sessionID)
|
|
3081
3186
|
const pending=refreshing.get(current.key)
|
|
3082
3187
|
if(pending&&!force)
|
|
3083
3188
|
return pending
|
|
3084
|
-
const revision=revisions.get(
|
|
3085
|
-
const requestState=requests.start(current.key,"refresh todos",current.key in sessions(),force)
|
|
3189
|
+
const revision=revisions.get(current.key)??0
|
|
3190
|
+
const requestState=requests.start(current.key,"refresh todos",current.key in sessions()||Boolean(options.hasValue?.(sessionID)),force)
|
|
3086
3191
|
if(!requestState)
|
|
3087
3192
|
return pending
|
|
3088
|
-
const request=
|
|
3089
|
-
|
|
3193
|
+
const request=Promise.try(()=>options.source.refresh(sessionID,requestState.signal)).then((todos)=>{if(!requestState.isCurrent())
|
|
3194
|
+
return []
|
|
3195
|
+
if(options.cache==="model"&&(revisions.get(current.key)??0)===revision)
|
|
3090
3196
|
set(current,todos)
|
|
3091
3197
|
requestState.succeed()
|
|
3092
3198
|
return todos
|
|
@@ -3099,50 +3205,134 @@ refreshing.delete(current.key)
|
|
|
3099
3205
|
refreshing.set(current.key,request)
|
|
3100
3206
|
return request
|
|
3101
3207
|
}
|
|
3102
|
-
const unsubscribe=
|
|
3103
|
-
revisions.set(
|
|
3104
|
-
set(
|
|
3208
|
+
const unsubscribe=options.source.subscribe?.((sessionID,todos)=>{const current=target(sessionID)
|
|
3209
|
+
revisions.set(current.key,(revisions.get(current.key)??0) + 1)
|
|
3210
|
+
set(current,todos)
|
|
3211
|
+
})
|
|
3212
|
+
if(unsubscribe)
|
|
3213
|
+
options.lifecycle.onDispose(unsubscribe)
|
|
3214
|
+
return{target,list,refresh,state(sessionID){return requests.state(target(sessionID).key)
|
|
3215
|
+
},retry(sessionID){return refresh(sessionID,true)
|
|
3216
|
+
},activate(sessionID){const key=target(sessionID).key
|
|
3217
|
+
if(activeTarget!==key){requests.abortAll()
|
|
3218
|
+
refreshing.clear()
|
|
3219
|
+
activeTarget=key
|
|
3220
|
+
leases=new Set
|
|
3221
|
+
}
|
|
3222
|
+
const lease=Symbol(key)
|
|
3223
|
+
leases.add(lease)
|
|
3224
|
+
return()=>{if(activeTarget!==key||!leases.delete(lease)||leases.size>0)
|
|
3225
|
+
return
|
|
3226
|
+
activeTarget=undefined
|
|
3227
|
+
requests.abortAll()
|
|
3228
|
+
refreshing.clear()
|
|
3229
|
+
}}}}
|
|
3230
|
+
function createTodoController(api){const target=(sessionID)=>{const session=api.state.session.get(sessionID)
|
|
3231
|
+
const directory=session?.directory??api.state.path.directory
|
|
3232
|
+
const workspace=session?.workspaceID
|
|
3233
|
+
return{key:JSON.stringify([sessionID,directory,workspace??null]),sessionID,routing:{directory,...workspace&&{workspace}}}}
|
|
3234
|
+
return createTodoModel({lifecycle:api.lifecycle,target,cache:"model",source:{list:(sessionID)=>api.state.session.todo(sessionID),async refresh(sessionID,signal){const result=await api.client.session.todo({sessionID,...target(sessionID).routing},{throwOnError:true,signal})
|
|
3235
|
+
return result.data??[]
|
|
3236
|
+
},subscribe:(listener)=>api.event.on("todo.updated",(event)=>listener(event.properties.sessionID,event.properties.todos))
|
|
3237
|
+
}})
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
function buildTodoView(todos,mode){const active=[...todos.filter((item)=>item.status==="in_progress"),...todos.filter((item)=>item.status==="pending")]
|
|
3241
|
+
const completed=todos.filter((item)=>item.status==="completed")
|
|
3242
|
+
const cancelled=todos.filter((item)=>item.status==="cancelled")
|
|
3243
|
+
const other=todos.filter((item)=>!["in_progress","pending","completed","cancelled"].includes(item.status))
|
|
3244
|
+
const groups=[{title:"Active",items:mode==="finished"?[]:active},{title:"Completed",items:mode==="active"?[]:completed},{title:"Cancelled",items:mode==="active"?[]:cancelled},{title:"Other",items:mode==="all"?other:[]}].filter((group)=>group.items.length)
|
|
3245
|
+
return{groups,rows:groups.flatMap((group)=>group.items.map((item)=>({item,group:group.title}))),counts:{all:todos.length,active:active.length,finished:completed.length + cancelled.length,completed:completed.length
|
|
3246
|
+
}}}
|
|
3247
|
+
|
|
3248
|
+
|
|
3249
|
+
|
|
3250
|
+
|
|
3251
|
+
|
|
3252
|
+
var HelpView=lazy2(async ()=>{const path=import.meta.url.includes("/dist/tui.js")?"./sidebar-shortcuts.js":"../../../sidebar-shortcuts"
|
|
3253
|
+
const module=await import(path)
|
|
3254
|
+
return{default:module.KeyboardHelpDialog
|
|
3255
|
+
}})
|
|
3256
|
+
function KeyboardHelpDialog(props){return(()=>{var _el$=_$createElement("box")
|
|
3257
|
+
_$insert(_el$,_$createComponent(Suspense2,{get children(){return _$createComponent(HelpView,props)
|
|
3258
|
+
}}))
|
|
3259
|
+
return _el$
|
|
3260
|
+
})()
|
|
3261
|
+
}
|
|
3262
|
+
function openKeyboardHelp(api,iconStyle=()=>"nerd",multilineCorners=()=>true,shortcuts=[]){createDialogStack(api,iconStyle,()=>true,multilineCorners).open(()=>_$createComponent(KeyboardHelpDialog,{api,shortcuts
|
|
3263
|
+
}))
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
|
|
3267
|
+
|
|
3268
|
+
|
|
3269
|
+
|
|
3270
|
+
|
|
3271
|
+
|
|
3272
|
+
|
|
3273
|
+
|
|
3274
|
+
|
|
3275
|
+
|
|
3276
|
+
var QUICK_ACTION_METADATA=[["\uEA73","Rename"],["\uEA82","Timeline"],["\uEBCC","Copy transcript"],["\uEBAC","Export"],["\uEAF5","Compact"],["\uEBEB","Switch session"],["\uEA60","New session"],["\uEB9E","Fork session"],["\uEBCC","Copy last response"],["\uEAA1","First message"],["\uEA9A","Last message"],["\uEA82","Last user message"],["\uEA9A","Next message"],["\uEAA1","Previous message"],["\uEBB3","Auto-approve permissions"]]
|
|
3277
|
+
var QUICK_ACTIONS=QUICK_ACTION_IDS.map((command,index)=>({command,icon:QUICK_ACTION_METADATA[index][0],label:QUICK_ACTION_METADATA[index][1]
|
|
3278
|
+
}))
|
|
3279
|
+
function quickActionRequiresSession(command){return command!=="session.list"&&command!=="session.new"&&command!=="permission.mode"
|
|
3280
|
+
}
|
|
3281
|
+
function orderQuickActionIds(order,favorites){return [...order.filter((id)=>favorites.has(id)),...order.filter((id)=>!favorites.has(id))]
|
|
3282
|
+
}
|
|
3283
|
+
var SEARCH_GROUPS=["Skills","Subagents","MCP","Actions"]
|
|
3284
|
+
var SEARCH_SECTIONS={Skills:"skills",Subagents:"subagents",MCP:"mcp",Actions:"quick_actions"
|
|
3285
|
+
}
|
|
3286
|
+
function buildSearchCandidates(input){const recent=new Map(input.recentSkills.map((location,index)=>[location,index]))
|
|
3287
|
+
const skills=[...input.skills].sort((a,b)=>Number(input.favoriteSkills.has(b.location))-Number(input.favoriteSkills.has(a.location))||(recent.get(a.location)??Infinity)-(recent.get(b.location)??Infinity)||a.name.localeCompare(b.name))
|
|
3288
|
+
const mcp=[...input.mcp].sort((a,b)=>Number(input.favoriteMcp.has(b.name))-Number(input.favoriteMcp.has(a.name))||a.name.localeCompare(b.name))
|
|
3289
|
+
const results=[...skills.map((skill)=>({id:`skill:${skill.location || skill.name}`,group:"Skills",title:skill.name,description:[input.skillGroups?.[skill.location],skill.description?.trim()||skill.location].filter(Boolean).join(" \xB7 "),keywords:`${skill.location} ${input.skillGroups?.[skill.location] ?? ""}`,skill
|
|
3290
|
+
})),...input.subagents.map((item)=>({id:`subagent:${item.session.id}`,group:"Subagents",title:item.session.title,description:[item.run?.outcome??item.status.type,item.run?.errorMessage].filter(Boolean).join(" \xB7 "),keywords:item.session.id,sessionID:item.session.id
|
|
3291
|
+
})),...mcp.map((item)=>({id:`mcp:${item.name}`,group:"MCP",title:item.name,description:[input.mcpGroups?.[item.name],mcpToggleAction2(item.status)==="disconnect"?"Disconnect":"Connect",item.status,input.mcpErrorMode==="details"?undefined:item.error].filter(Boolean).join(" \xB7 "),keywords:input.mcpGroups?.[item.name],disabled:input.mcpBusy||!mcpToggleAction2(item.status)?"MCP operation in progress":undefined,server:item.name,...input.mcpErrorMode==="details"&&item.error&&{information:{title:`MCP \xB7 ${item.name}`,body:`Status: ${item.status}
|
|
3292
|
+
|
|
3293
|
+
${item.error}`}}})),...orderQuickActionIds(input.actionOrder,input.favoriteQuickActions??new Set).flatMap((id)=>{const action=QUICK_ACTIONS.find((item)=>item.command===id)
|
|
3294
|
+
return action?[{id:`action:${id}`,group:"Actions",title:input.actionLabel?.(action)??action.label,description:id,command:id,disabled:input.actionDisabledReason?.(action)??(quickActionRequiresSession(action.command)&&!input.hasSession?"Requires an open session":undefined)
|
|
3295
|
+
}]:[]
|
|
3296
|
+
})]
|
|
3297
|
+
return [...new Map(results.map((item)=>[item.id,item])).values()]
|
|
3298
|
+
}
|
|
3299
|
+
function fuzzyScore(needle,value){const text=value.toLocaleLowerCase()
|
|
3300
|
+
const position=text.indexOf(needle)
|
|
3301
|
+
if(text===needle)
|
|
3302
|
+
return 2000
|
|
3303
|
+
if(position===0)
|
|
3304
|
+
return 1500
|
|
3305
|
+
if(position!==-1)
|
|
3306
|
+
return 1000-position
|
|
3307
|
+
let cursor=0
|
|
3308
|
+
let gaps=0
|
|
3309
|
+
for(const char of needle){const index=text.indexOf(char,cursor)
|
|
3310
|
+
if(index===-1)
|
|
3311
|
+
return -1
|
|
3312
|
+
gaps += index-cursor
|
|
3313
|
+
cursor=index + char.length
|
|
3314
|
+
}
|
|
3315
|
+
return Math.max(0,500-gaps)
|
|
3316
|
+
}
|
|
3317
|
+
function searchResults(candidates,query){const terms=query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean)
|
|
3318
|
+
const scored=candidates.flatMap((item,index)=>{let score=0
|
|
3319
|
+
for(const term of terms){const title=fuzzyScore(term,item.title)
|
|
3320
|
+
const detail=fuzzyScore(term,`${item.description} ${item.keywords ?? ""}`)
|
|
3321
|
+
if(title<0&&detail<0)
|
|
3322
|
+
return []
|
|
3323
|
+
score += Math.max(title>=0?title:-Infinity,detail>=0?detail-250:-Infinity)
|
|
3324
|
+
}
|
|
3325
|
+
return [{item,score,index}]
|
|
3105
3326
|
})
|
|
3106
|
-
|
|
3107
|
-
return
|
|
3108
|
-
},retry(sessionID){return refresh(sessionID,true)
|
|
3109
|
-
},activate(sessionID){const key=target(sessionID).key
|
|
3110
|
-
if(activeTarget!==key){requests.abortAll()
|
|
3111
|
-
refreshing.clear()
|
|
3112
|
-
activeTarget=key
|
|
3327
|
+
scored.sort((a,b)=>SEARCH_GROUPS.indexOf(a.item.group)-SEARCH_GROUPS.indexOf(b.item.group)||b.score-a.score||a.index-b.index)
|
|
3328
|
+
return scored.map(({item})=>item)
|
|
3113
3329
|
}
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
refreshing.clear()
|
|
3119
|
-
}}}}
|
|
3120
|
-
|
|
3121
|
-
function buildTodoView(todos,mode){const active=[...todos.filter((item)=>item.status==="in_progress"),...todos.filter((item)=>item.status==="pending")]
|
|
3122
|
-
const completed=todos.filter((item)=>item.status==="completed")
|
|
3123
|
-
const cancelled=todos.filter((item)=>item.status==="cancelled")
|
|
3124
|
-
const other=todos.filter((item)=>!["in_progress","pending","completed","cancelled"].includes(item.status))
|
|
3125
|
-
const groups=[{title:"Active",items:mode==="finished"?[]:active},{title:"Completed",items:mode==="active"?[]:completed},{title:"Cancelled",items:mode==="active"?[]:cancelled},{title:"Other",items:mode==="all"?other:[]}].filter((group)=>group.items.length)
|
|
3126
|
-
return{groups,rows:groups.flatMap((group)=>group.items.map((item)=>({item,group:group.title}))),counts:{all:todos.length,active:active.length,finished:completed.length + cancelled.length,completed:completed.length
|
|
3127
|
-
}}}
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3330
|
+
function searchContext(api){const route=api.route.current
|
|
3331
|
+
const sessionID=route.name==="session"&&"params" in route&&typeof route.params?.sessionID==="string"?route.params.sessionID:undefined
|
|
3332
|
+
const location=currentLocation(api)
|
|
3333
|
+
return{sessionID,location,key:JSON.stringify([route.name,sessionID??null,location.key])}}
|
|
3131
3334
|
|
|
3132
3335
|
|
|
3133
|
-
var HelpView=lazy2(async ()=>{const path=import.meta.url.includes("/dist/tui.js")?"./sidebar-shortcuts.js":"../../../sidebar-shortcuts"
|
|
3134
|
-
const module=await import(path)
|
|
3135
|
-
return{default:module.KeyboardHelpDialog
|
|
3136
|
-
}})
|
|
3137
|
-
function KeyboardHelpDialog(props){return(()=>{var _el$=_$createElement("box")
|
|
3138
|
-
_$insert(_el$,_$createComponent(Suspense2,{get children(){return _$createComponent(HelpView,props)
|
|
3139
|
-
}}))
|
|
3140
|
-
return _el$
|
|
3141
|
-
})()
|
|
3142
|
-
}
|
|
3143
|
-
function openKeyboardHelp(api,iconStyle=()=>"nerd",multilineCorners=()=>true,shortcuts=[]){createDialogStack(api,iconStyle,()=>true,multilineCorners).open(()=>_$createComponent(KeyboardHelpDialog,{api,shortcuts
|
|
3144
|
-
}))
|
|
3145
|
-
}
|
|
3146
3336
|
|
|
3147
3337
|
|
|
3148
3338
|
|
|
@@ -3170,11 +3360,6 @@ function supportsTitleVisibility(api){return hostCapabilities(api).titleVisibili
|
|
|
3170
3360
|
}
|
|
3171
3361
|
function supportsPermissionMode(api){return hostCapabilities(api).permissionMode!==false
|
|
3172
3362
|
}
|
|
3173
|
-
var QUICK_ACTION_METADATA=[["\uEA73","Rename"],["\uEA82","Timeline"],["\uEBCC","Copy transcript"],["\uEBAC","Export"],["\uEAF5","Compact"],["\uEBEB","Switch session"],["\uEA60","New session"],["\uEB9E","Fork session"],["\uEBCC","Copy last response"],["\uEAA1","First message"],["\uEA9A","Last message"],["\uEA82","Last user message"],["\uEA9A","Next message"],["\uEAA1","Previous message"],["\uEBB3","Auto-approve permissions"]]
|
|
3174
|
-
var QUICK_ACTIONS=QUICK_ACTION_IDS.map((command,index)=>({command,icon:QUICK_ACTION_METADATA[index][0],label:QUICK_ACTION_METADATA[index][1]
|
|
3175
|
-
}))
|
|
3176
|
-
function quickActionRequiresSession(command){return command!=="session.list"&&command!=="session.new"&&command!=="permission.mode"
|
|
3177
|
-
}
|
|
3178
3363
|
function quickActionLabel(api,action){if(action.command!=="permission.mode")
|
|
3179
3364
|
return action.label
|
|
3180
3365
|
const command=api.keymap.getCommands?.({visibility:"registered"}).find((candidate)=>candidate.name===action.command)
|
|
@@ -3186,8 +3371,6 @@ if(unsubscribe)
|
|
|
3186
3371
|
onCleanup8(unsubscribe)
|
|
3187
3372
|
return revision
|
|
3188
3373
|
}
|
|
3189
|
-
function orderQuickActionIds(order,favorites){return [...order.filter((id)=>favorites.has(id)),...order.filter((id)=>!favorites.has(id))]
|
|
3190
|
-
}
|
|
3191
3374
|
function quickActionDisabledReason(api,action){const route=api.route.current
|
|
3192
3375
|
if(quickActionRequiresSession(action.command)&&route.name!=="session")
|
|
3193
3376
|
return "Requires an open session"
|
|
@@ -3225,69 +3408,6 @@ return mode?"on":"off"
|
|
|
3225
3408
|
}
|
|
3226
3409
|
return sources.get(api)?.(id)
|
|
3227
3410
|
}
|
|
3228
|
-
var SEARCH_GROUPS=["Skills","Subagents","MCP","Actions"]
|
|
3229
|
-
var SEARCH_SECTIONS={Skills:"skills",Subagents:"subagents",MCP:"mcp",Actions:"quick_actions"
|
|
3230
|
-
}
|
|
3231
|
-
function searchContext(api){const route=api.route.current
|
|
3232
|
-
const sessionID=route.name==="session"&&"params" in route&&typeof route.params?.sessionID==="string"?route.params.sessionID:undefined
|
|
3233
|
-
const location=currentLocation(api)
|
|
3234
|
-
return{sessionID,location,key:JSON.stringify([route.name,sessionID??null,location.key])}}
|
|
3235
|
-
function buildSearchCandidates(input){const recent=new Map(input.recentSkills.map((location,index)=>[location,index]))
|
|
3236
|
-
const skills=[...input.skills].sort((a,b)=>Number(input.favoriteSkills.has(b.location))-Number(input.favoriteSkills.has(a.location))||(recent.get(a.location)??Infinity)-(recent.get(b.location)??Infinity)||a.name.localeCompare(b.name))
|
|
3237
|
-
const mcp=[...input.mcp].sort((a,b)=>Number(input.favoriteMcp.has(b.name))-Number(input.favoriteMcp.has(a.name))||a.name.localeCompare(b.name))
|
|
3238
|
-
const results=[...skills.map((skill)=>({id:`skill:${skill.location || skill.name}`,group:"Skills",title:skill.name,description:[input.skillGroups?.[skill.location],skill.description?.trim()||skill.location].filter(Boolean).join(" \xB7 "),keywords:`${skill.location} ${input.skillGroups?.[skill.location] ?? ""}`,skill
|
|
3239
|
-
})),...input.subagents.map((item)=>({id:`subagent:${item.session.id}`,group:"Subagents",title:item.session.title,description:[item.run?.outcome??item.status.type,item.run?.errorMessage].filter(Boolean).join(" \xB7 "),keywords:item.session.id,sessionID:item.session.id
|
|
3240
|
-
})),...mcp.map((item)=>({id:`mcp:${item.name}`,group:"MCP",title:item.name,description:[input.mcpGroups?.[item.name],mcpToggleAction2(item.status)==="disconnect"?"Disconnect":"Connect",item.status,item.error].filter(Boolean).join(" \xB7 "),keywords:input.mcpGroups?.[item.name],disabled:input.mcpBusy||!mcpToggleAction2(item.status)?"MCP operation in progress":undefined,server:item.name
|
|
3241
|
-
})),...orderQuickActionIds(input.actionOrder,input.favoriteQuickActions??new Set).flatMap((id)=>{const action=QUICK_ACTIONS.find((item)=>item.command===id)
|
|
3242
|
-
return action?[{id:`action:${id}`,group:"Actions",title:input.actionLabel?.(action)??action.label,description:id,command:id,disabled:input.actionDisabledReason?.(action)??(quickActionRequiresSession(action.command)&&!input.hasSession?"Requires an open session":undefined)
|
|
3243
|
-
}]:[]
|
|
3244
|
-
})]
|
|
3245
|
-
return [...new Map(results.map((item)=>[item.id,item])).values()]
|
|
3246
|
-
}
|
|
3247
|
-
function fuzzyScore(needle,value){const text=value.toLocaleLowerCase()
|
|
3248
|
-
const position=text.indexOf(needle)
|
|
3249
|
-
if(text===needle)
|
|
3250
|
-
return 2000
|
|
3251
|
-
if(position===0)
|
|
3252
|
-
return 1500
|
|
3253
|
-
if(position!==-1)
|
|
3254
|
-
return 1000-position
|
|
3255
|
-
let cursor=0
|
|
3256
|
-
let gaps=0
|
|
3257
|
-
for(const char of needle){const index=text.indexOf(char,cursor)
|
|
3258
|
-
if(index===-1)
|
|
3259
|
-
return -1
|
|
3260
|
-
gaps += index-cursor
|
|
3261
|
-
cursor=index + char.length
|
|
3262
|
-
}
|
|
3263
|
-
return Math.max(0,500-gaps)
|
|
3264
|
-
}
|
|
3265
|
-
function searchResults(candidates,query){const terms=query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean)
|
|
3266
|
-
const scored=candidates.flatMap((item,index)=>{let score=0
|
|
3267
|
-
for(const term of terms){const title=fuzzyScore(term,item.title)
|
|
3268
|
-
const detail=fuzzyScore(term,`${item.description} ${item.keywords ?? ""}`)
|
|
3269
|
-
if(title<0&&detail<0)
|
|
3270
|
-
return []
|
|
3271
|
-
score += Math.max(title>=0?title:-Infinity,detail>=0?detail-250:-Infinity)
|
|
3272
|
-
}
|
|
3273
|
-
return [{item,score,index}]
|
|
3274
|
-
})
|
|
3275
|
-
scored.sort((a,b)=>SEARCH_GROUPS.indexOf(a.item.group)-SEARCH_GROUPS.indexOf(b.item.group)||b.score-a.score||a.index-b.index)
|
|
3276
|
-
return scored.map(({item})=>item)
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
3411
|
function mixColor(from,to,amount){return RGBA3.fromValues(from.r + (to.r-from.r)*amount,from.g + (to.g-from.g)*amount,from.b + (to.b-from.b)*amount)
|
|
3292
3412
|
}
|
|
3293
3413
|
function createSearchDialogController(props){const context=searchContext(props.api)
|
|
@@ -3373,7 +3493,7 @@ function selectTab(tab){if(tab!==activeTab()){tabViews.set(activeTab(),{selected
|
|
|
3373
3493
|
})
|
|
3374
3494
|
const saved=tabViews.get(tab)
|
|
3375
3495
|
pendingScroll=saved?.scrollTop??0
|
|
3376
|
-
|
|
3496
|
+
batch3(()=>{setActiveTab(tab)
|
|
3377
3497
|
setSelectedID(saved?.selectedID)
|
|
3378
3498
|
})
|
|
3379
3499
|
}
|
|
@@ -3678,14 +3798,17 @@ clearTimeout(timer)
|
|
|
3678
3798
|
return _$createElement("box")
|
|
3679
3799
|
}
|
|
3680
3800
|
|
|
3681
|
-
|
|
3801
|
+
var alwaysCurrent=()=>true
|
|
3802
|
+
function createMcpPresetPreview(controller,preferences,name,isCurrent=alwaysCurrent){const target=controller.target()
|
|
3682
3803
|
const valid=()=>{const current=controller.target()
|
|
3683
|
-
return current.key===target.key&¤t.scope===target.scope
|
|
3804
|
+
return isCurrent()&¤t.key===target.key&¤t.scope===target.scope
|
|
3684
3805
|
}
|
|
3685
3806
|
const preset=createMemo2(()=>preferences.mcpPresets()[name])
|
|
3686
|
-
const preview=createMemo2(()=>mcpPresetPreview(controller.list(target),preset()??{}))
|
|
3807
|
+
const preview=createMemo2(()=>mcpPresetPreview(valid()?controller.list(target):[],preset()??{}))
|
|
3687
3808
|
const state=createMemo2(()=>controller.state(target))
|
|
3688
|
-
const blocked=createMemo2(()=>{if(!
|
|
3809
|
+
const blocked=createMemo2(()=>{if(!valid())
|
|
3810
|
+
return "Session or server changed; reopen the preview."
|
|
3811
|
+
if(!preset())
|
|
3689
3812
|
return "Preset no longer exists."
|
|
3690
3813
|
if(controller.mutating(target))
|
|
3691
3814
|
return "Wait for the current MCP operation to finish."
|
|
@@ -4375,7 +4498,7 @@ return _$createElement("box")
|
|
|
4375
4498
|
|
|
4376
4499
|
function UpgradeNotesBinding(props){let pending
|
|
4377
4500
|
let disposed=false
|
|
4378
|
-
|
|
4501
|
+
async function load(){const previous=await props.preferences.claimNavigatorUpgrade(props.version)
|
|
4379
4502
|
if(!previous||disposed)
|
|
4380
4503
|
return
|
|
4381
4504
|
try{let show=function(){if(disposed||props.api.lifecycle.signal?.aborted)
|
|
@@ -4395,7 +4518,8 @@ const entries=changesSince(await (props.read??readBundledChangelog)(),previous,p
|
|
|
4395
4518
|
if(entries.length===0||disposed)
|
|
4396
4519
|
return
|
|
4397
4520
|
show()
|
|
4398
|
-
} catch{}}
|
|
4521
|
+
} catch{}}
|
|
4522
|
+
onMount6(()=>void load().catch(()=>{}))
|
|
4399
4523
|
onCleanup16(()=>{disposed=true
|
|
4400
4524
|
clearTimeout(pending)
|
|
4401
4525
|
})
|
|
@@ -5606,6 +5730,8 @@ return _$createComponent(SectionWithHeaderAction,{get api(){return props.api
|
|
|
5606
5730
|
|
|
5607
5731
|
|
|
5608
5732
|
|
|
5733
|
+
var CHATGPT_METHODS=new Set(["chatgpt-browser","chatgpt-headless"]);
|
|
5734
|
+
|
|
5609
5735
|
|
|
5610
5736
|
|
|
5611
5737
|
|
|
@@ -5663,6 +5789,36 @@ return _$createComponent(SectionWithHeaderAction,{get api(){return props.api
|
|
|
5663
5789
|
|
|
5664
5790
|
|
|
5665
5791
|
|
|
5792
|
+
|
|
5793
|
+
var scheduleInterval=(callback,milliseconds)=>{const timer=setInterval(callback,milliseconds)
|
|
5794
|
+
return()=>clearInterval(timer)
|
|
5795
|
+
}
|
|
5796
|
+
function createLimitsRefresh(options,schedule=scheduleInterval){let disposed=false
|
|
5797
|
+
const selectedKey=createMemo12(()=>JSON.stringify(options.selectedModel?.()))
|
|
5798
|
+
const targetKey=createMemo12(()=>JSON.stringify(options.controller.current().model))
|
|
5799
|
+
const interval=createMemo12(()=>(parseLimitsRefreshMinutes(options.refreshMinutes?.())??DEFAULT_LIMITS_REFRESH_MINUTES)*60000)
|
|
5800
|
+
createEffect16(on2(selectedKey,()=>{const model=options.selectedModel?.()
|
|
5801
|
+
if(model&&options.accountSource)
|
|
5802
|
+
options.accountSource.refresh(model).catch(()=>{})
|
|
5803
|
+
}))
|
|
5804
|
+
createEffect16(on2(targetKey,()=>{const{model,adapter}=options.controller.current()
|
|
5805
|
+
if(model&&adapter)
|
|
5806
|
+
options.controller.refresh()
|
|
5807
|
+
}))
|
|
5808
|
+
async function refresh(){if(disposed)
|
|
5809
|
+
return
|
|
5810
|
+
const target=targetKey()
|
|
5811
|
+
const model=options.selectedModel?.()
|
|
5812
|
+
if(model&&options.accountSource)
|
|
5813
|
+
await options.accountSource.refresh(model)
|
|
5814
|
+
if(!disposed&&target===targetKey())
|
|
5815
|
+
await options.controller.refresh()
|
|
5816
|
+
}
|
|
5817
|
+
createEffect16(on2(interval,(milliseconds)=>{onCleanup20(schedule(()=>void refresh().catch(()=>{}),milliseconds))
|
|
5818
|
+
}))
|
|
5819
|
+
onCleanup20(()=>{disposed=true
|
|
5820
|
+
})
|
|
5821
|
+
}
|
|
5666
5822
|
|
|
5667
5823
|
|
|
5668
5824
|
|
|
@@ -5716,12 +5872,12 @@ function QuickActionRow(props){const{api,interaction
|
|
|
5716
5872
|
const icons=useIcons()
|
|
5717
5873
|
const toolbar=useToolbar()
|
|
5718
5874
|
const theme=()=>api.theme.current
|
|
5719
|
-
const [width,setWidth]=
|
|
5720
|
-
const shortcut=
|
|
5875
|
+
const [width,setWidth]=createSignal34(0)
|
|
5876
|
+
const shortcut=createMemo13(()=>{const bindings=api.keymap.getCommandBindings({visibility:"registered",commands:[props.action.command]
|
|
5721
5877
|
})
|
|
5722
5878
|
return api.keys.formatBindings(bindings.get(props.action.command))
|
|
5723
5879
|
})
|
|
5724
|
-
const visibleShortcut=
|
|
5880
|
+
const visibleShortcut=createMemo13(()=>{const value=shortcut()
|
|
5725
5881
|
return value&&width()>=Bun.stringWidth(props.action.label) + Bun.stringWidth(value) + (toolbar?11:8)?value:undefined
|
|
5726
5882
|
})
|
|
5727
5883
|
function run(){if(props.action.command==="session.new"&&props.onNewSession?.())
|
|
@@ -5785,7 +5941,7 @@ return _el$4
|
|
|
5785
5941
|
}})
|
|
5786
5942
|
}
|
|
5787
5943
|
function QuickActionsSection(props){const commandRevision=createQuickActionRevision(props.api)
|
|
5788
|
-
const actions=
|
|
5944
|
+
const actions=createMemo14(()=>{commandRevision()
|
|
5789
5945
|
return orderQuickActionIds(props.preferences.quickActionOrder?.()??QUICK_ACTIONS.map((action)=>action.command),props.preferences.favoriteQuickActions?.()??new Set).flatMap((id)=>{const action=QUICK_ACTIONS.find((candidate)=>candidate.command===id)
|
|
5790
5946
|
return action&&props.preferences.quickActionVisible?.(id)!==false?[{...action,label:quickActionLabel(props.api,action),disabled:quickActionDisabledReason(props.api,action)
|
|
5791
5947
|
}]:[]
|
|
@@ -5869,13 +6025,13 @@ item.activate(event)
|
|
|
5869
6025
|
|
|
5870
6026
|
|
|
5871
6027
|
function SidebarToggleBinding(props){const shortcuts=createSidebarShortcutMode(props.api)
|
|
5872
|
-
|
|
6028
|
+
createEffect17(()=>{const key=props.preferences.toggleKey()
|
|
5873
6029
|
untrack6(()=>shortcuts.bind(key))
|
|
5874
6030
|
})
|
|
5875
|
-
|
|
6031
|
+
createEffect17(()=>{if(shortcuts.active()&&(props.api.route.current.name!=="session"||props.api.ui.dialog.open||props.api.mode.current()==="modal"))
|
|
5876
6032
|
untrack6(shortcuts.cancel)
|
|
5877
6033
|
})
|
|
5878
|
-
|
|
6034
|
+
onCleanup21(shortcuts.dispose)
|
|
5879
6035
|
return(()=>{var _el$=_$createElement("box")
|
|
5880
6036
|
_$setProp(_el$,"position","absolute")
|
|
5881
6037
|
_$setProp(_el$,"top",0)
|
|
@@ -5897,11 +6053,11 @@ const unregisterCommands=props.api.keymap.registerLayer({commands:[...commands,.
|
|
|
5897
6053
|
})=>({name:name.replace(PLUGIN_ID,()=>LEGACY_PLUGIN_ID),run,enabled
|
|
5898
6054
|
}))]
|
|
5899
6055
|
})
|
|
5900
|
-
|
|
5901
|
-
|
|
6056
|
+
onCleanup21(unregisterCommands)
|
|
6057
|
+
createEffect17(()=>{const unregister=props.api.keymap.registerLayer({bindings:[{key:props.preferences.focusKey(),cmd:FOCUS_COMMAND
|
|
5902
6058
|
}]
|
|
5903
6059
|
})
|
|
5904
|
-
|
|
6060
|
+
onCleanup21(unregister)
|
|
5905
6061
|
})
|
|
5906
6062
|
return _$createElement("box")
|
|
5907
6063
|
}
|
|
@@ -5953,6 +6109,26 @@ rows.push(visible)
|
|
|
5953
6109
|
}
|
|
5954
6110
|
return{columns,overflow,rows,...widths&&{sizes}}}
|
|
5955
6111
|
|
|
6112
|
+
var PRIVATE_KEYS=new Set(["favoriteSkills","favoriteMcpServers","favoriteQuickActions","skillGroups","recentSkills","recentQuickActions","skippedSkillConfirmations","onboardingCompleted","codexAccountBindings","pendingResetAttempt","lastNavigatorVersion","previousNavigatorVersion","worktrees","user"]);
|
|
6113
|
+
function createPinnedToolbarModel(ports){function view(pin){const action=pin.kind==="action"?ports.action(pin.id):undefined
|
|
6114
|
+
const skill=pin.kind==="skill"?ports.skill(pin):undefined
|
|
6115
|
+
return{label:action?.label??(pin.kind==="skill"?pin.name:pin.id),stateLabel:action?.stateLabel,icons:toolbarPinIcons(pin,action?.state),text:pin.text,reason:action?.reason??skill?.reason
|
|
6116
|
+
}}
|
|
6117
|
+
function run(key){const pin=ports.pins().find((item)=>toolbarPinKey(item)===key)
|
|
6118
|
+
if(!pin)
|
|
6119
|
+
return
|
|
6120
|
+
const item=view(pin)
|
|
6121
|
+
if(item.reason){ports.unavailable(item.label,item.reason)
|
|
6122
|
+
return
|
|
6123
|
+
}
|
|
6124
|
+
if(pin.kind==="action"){ports.runAction(pin.id)
|
|
6125
|
+
return
|
|
6126
|
+
}
|
|
6127
|
+
const skill=ports.skill(pin).skill
|
|
6128
|
+
if(skill)
|
|
6129
|
+
ports.runSkill(skill)
|
|
6130
|
+
}
|
|
6131
|
+
return{pins:ports.pins,view,run}}
|
|
5956
6132
|
function toolbarPinWidth(pin){const states=Object.keys({...pin.stateIcons,...pin.stateIconLists})
|
|
5957
6133
|
const count=Math.max(toolbarPinIcons(pin).length,...states.map((state)=>toolbarPinIcons(pin,state).length))
|
|
5958
6134
|
const iconsWidth=Math.max(0,count*2-1)
|
|
@@ -5961,49 +6137,34 @@ return 2 + Math.max(1,iconsWidth + (count&&pin.text?1:0) + Bun.stringWidth(pin.t
|
|
|
5961
6137
|
function createToolbarModel(props){const icons=useIcons()
|
|
5962
6138
|
const revision=createQuickActionRevision(props.api)
|
|
5963
6139
|
const pins=()=>props.preferences.toolbarPins?.()??[]
|
|
5964
|
-
const needsSkills=
|
|
5965
|
-
const target=
|
|
5966
|
-
|
|
6140
|
+
const needsSkills=createMemo15(()=>pins().some((pin)=>pin.kind==="skill"))
|
|
6141
|
+
const target=createMemo15(()=>props.skills.target())
|
|
6142
|
+
createEffect18(()=>{if(!needsSkills())
|
|
5967
6143
|
return
|
|
5968
6144
|
const current=target()
|
|
5969
6145
|
const release=untrack7(()=>props.skills.activate(current))
|
|
5970
|
-
|
|
6146
|
+
onCleanup22(release)
|
|
5971
6147
|
untrack7(()=>{props.skills.refresh(current).catch(()=>{})
|
|
5972
6148
|
})
|
|
5973
6149
|
})
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
const state=quickActionState(props.api,
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
}}
|
|
5980
|
-
const skill=props.skills.list(target()).find((item)=>item.location===pin.location&&item.name===pin.name)
|
|
6150
|
+
const model=createPinnedToolbarModel({pins,action(id){revision()
|
|
6151
|
+
const action=QUICK_ACTIONS.find((item)=>item.command===id)
|
|
6152
|
+
const state=quickActionState(props.api,id)
|
|
6153
|
+
return{label:quickActionLabel(props.api,action),state,stateLabel:quickActionStates(props.api,id).find((item)=>item.id===state)?.label,reason:quickActionDisabledReason(props.api,action)
|
|
6154
|
+
}},skill(pin){const skill=props.skills.list(target()).find((item)=>item.location===pin.location&&item.name===pin.name)
|
|
5981
6155
|
const request=props.skills.state(target())
|
|
5982
6156
|
const unavailable=request.status==="loading"?"Loading Skills":"Skill is unavailable in this workspace"
|
|
5983
|
-
return{
|
|
5984
|
-
}}
|
|
5985
|
-
function run(key){const pin=pins().find((item)=>toolbarPinKey(item)===key)
|
|
5986
|
-
if(!pin)
|
|
5987
|
-
return
|
|
5988
|
-
const item=view(pin)
|
|
5989
|
-
if(item.reason){props.api.ui.toast({variant:"warning",title:item.label,message:item.reason})
|
|
5990
|
-
return
|
|
5991
|
-
}
|
|
5992
|
-
if(pin.kind==="action"){if(pin.id==="session.new"&&props.onNewSession?.())
|
|
6157
|
+
return{skill,reason:request.error?.message??(skill?skillUnavailable(props.skills,skill):unavailable)}},unavailable:(title,message)=>props.api.ui.toast({variant:"warning",title,message}),runAction(id){if(id==="session.new"&&props.onNewSession?.())
|
|
5993
6158
|
return
|
|
5994
|
-
const result=props.interaction.dispatchFromReturnTarget(
|
|
6159
|
+
const result=props.interaction.dispatchFromReturnTarget(id)
|
|
5995
6160
|
if(!result.ok)
|
|
5996
|
-
props.api.ui.toast({variant:"warning",title:item.
|
|
5997
|
-
|
|
5998
|
-
}
|
|
5999
|
-
const skill=props.skills.list(target()).find((candidate)=>candidate.location===pin.location&&candidate.name===pin.name)
|
|
6000
|
-
if(!skill)
|
|
6001
|
-
return
|
|
6002
|
-
const route=JSON.stringify(props.api.route.current)
|
|
6161
|
+
props.api.ui.toast({variant:"warning",title:quickActionLabel(props.api,QUICK_ACTIONS.find((item)=>item.command===id)),message:`Command is ${result.reason}`
|
|
6162
|
+
})
|
|
6163
|
+
},runSkill(skill){const route=JSON.stringify(props.api.route.current)
|
|
6003
6164
|
const dialogs=createDialogStack(props.api,icons.style,()=>route===JSON.stringify(props.api.route.current),icons.multilineCorners)
|
|
6004
6165
|
activateSkill({api:props.api,controller:props.skills,access:props.preferences,skill,dialogs})
|
|
6005
|
-
}
|
|
6006
|
-
return{
|
|
6166
|
+
}})
|
|
6167
|
+
return{...model,width:toolbarPinWidth}}
|
|
6007
6168
|
|
|
6008
6169
|
|
|
6009
6170
|
|
|
@@ -6013,13 +6174,13 @@ return{pins,view,run,width:toolbarPinWidth}}
|
|
|
6013
6174
|
|
|
6014
6175
|
function ToolbarButton(props){const icons=useIcons()
|
|
6015
6176
|
const foreground=()=>props.reason?props.api.theme.current.textMuted:item.foregroundColor()
|
|
6016
|
-
const [hovered,setHovered]=
|
|
6177
|
+
const [hovered,setHovered]=createSignal35(false)
|
|
6017
6178
|
const item=useSidebarItem(props.api,props.interaction,{id:props.id,position:()=>({section:0,row:props.row + 1,column:props.column
|
|
6018
6179
|
}),activate:props.onRun
|
|
6019
6180
|
},()=>props.reason?props.api.theme.current.textMuted:props.api.theme.current.accent,"control")
|
|
6020
6181
|
const content=()=>[props.icons.map((icon)=>terminalIcon(icon,icons.style())).join(" "),props.text].filter(Boolean).join(" ")
|
|
6021
|
-
|
|
6022
|
-
|
|
6182
|
+
createEffect19(()=>props.onDetails(props.id,item.focused()||hovered()))
|
|
6183
|
+
onCleanup23(()=>props.onDetails(props.id,false))
|
|
6023
6184
|
return _$createComponent(Show27,{get when(){return _$memo(()=>!!(props.width===3&&props.icons.length===1))()&&!props.text
|
|
6024
6185
|
},get fallback(){return _$createComponent(SelectionBox,{get id(){return props.id
|
|
6025
6186
|
},ref(r$){var _ref$2=item.ref
|
|
@@ -6060,17 +6221,17 @@ setHovered(false)
|
|
|
6060
6221
|
}
|
|
6061
6222
|
function SidebarToolbar(props){const model=createToolbarModel(props)
|
|
6062
6223
|
const icons=useIcons()
|
|
6063
|
-
const [width,setWidth]=
|
|
6064
|
-
const [detail,setDetail]=
|
|
6065
|
-
const keys=
|
|
6066
|
-
const widths=
|
|
6067
|
-
const layout=
|
|
6068
|
-
const rows=
|
|
6069
|
-
const byKey=
|
|
6224
|
+
const [width,setWidth]=createSignal36(3)
|
|
6225
|
+
const [detail,setDetail]=createSignal36()
|
|
6226
|
+
const keys=createMemo16(()=>model.pins().map((pin)=>toolbarPinKey(pin)))
|
|
6227
|
+
const widths=createMemo16(()=>new Map(model.pins().map((pin)=>[toolbarPinKey(pin),model.width(pin)])))
|
|
6228
|
+
const layout=createMemo16(()=>toolbarLayout(keys(),width(),props.preferences.toolbarOverflow?.()??"menu",widths()))
|
|
6229
|
+
const rows=createMemo16(()=>layout().rows.map((_,index)=>index))
|
|
6230
|
+
const byKey=createMemo16(()=>new Map(model.pins().map((pin)=>[toolbarPinKey(pin),pin])))
|
|
6070
6231
|
const view=(key)=>{const pin=byKey().get(key)
|
|
6071
6232
|
return pin&&model.view(pin)
|
|
6072
6233
|
}
|
|
6073
|
-
const details=
|
|
6234
|
+
const details=createMemo16(()=>{if(detail()==="toolbar:more")
|
|
6074
6235
|
return "More pinned actions and Skills"
|
|
6075
6236
|
const item=view(detail()??"")
|
|
6076
6237
|
return item?[item.label,item.stateLabel,item.reason].filter(Boolean).join(" \xB7 "):undefined
|
|
@@ -6143,18 +6304,18 @@ return _el$
|
|
|
6143
6304
|
|
|
6144
6305
|
|
|
6145
6306
|
|
|
6146
|
-
function createSkillSectionModel(props,query){const target=
|
|
6147
|
-
const recent=
|
|
6148
|
-
const groups=
|
|
6149
|
-
const grouped=
|
|
6150
|
-
const list=
|
|
6151
|
-
const filtered=
|
|
6307
|
+
function createSkillSectionModel(props,query){const target=createMemo17(()=>props.controller.target())
|
|
6308
|
+
const recent=createMemo17(()=>new Map((props.preferences.recentSkills?.()??[]).map((location,index)=>[location,index])))
|
|
6309
|
+
const groups=createMemo17(()=>props.preferences.skillGroups?.()??{})
|
|
6310
|
+
const grouped=createMemo17(()=>Object.keys(groups()).length>0)
|
|
6311
|
+
const list=createMemo17(()=>buildSkillGroupedView(props.controller.list(target()),props.preferences.favoriteSkills?.()??new Set,groups(),recent()))
|
|
6312
|
+
const filtered=createMemo17(()=>list().filter((item)=>matchesFilter(query(),item.name,item.description,item.assignedGroup)))
|
|
6152
6313
|
const visibility=createListVisibility({items:filtered,limit:()=>props.preferences.sectionItemLimit?.("skills")??0,resetKey:()=>JSON.stringify([target().key,query()])
|
|
6153
6314
|
})
|
|
6154
|
-
const state=
|
|
6155
|
-
|
|
6315
|
+
const state=createMemo17(()=>props.controller.state(target()))
|
|
6316
|
+
createEffect20(()=>{const current=target()
|
|
6156
6317
|
const deactivate=untrack8(()=>props.controller.activate?.(current)??(()=>{}))
|
|
6157
|
-
|
|
6318
|
+
onCleanup24(deactivate)
|
|
6158
6319
|
untrack8(()=>void props.controller.refresh(current).catch(()=>{}))
|
|
6159
6320
|
})
|
|
6160
6321
|
async function useSkill(item){try{const isInserted=await props.controller.use(target(),item.name)
|
|
@@ -6215,7 +6376,7 @@ return _el$
|
|
|
6215
6376
|
}
|
|
6216
6377
|
function SkillsSection(props){const dialogs=useDialogs(props.api)
|
|
6217
6378
|
const toolbar=useToolbar()
|
|
6218
|
-
const [query,setQuery]=
|
|
6379
|
+
const [query,setQuery]=createSignal37("")
|
|
6219
6380
|
const model=createSkillSectionModel(props,query)
|
|
6220
6381
|
const navigationSection=()=>props.navigationSection??3
|
|
6221
6382
|
function selectSkill(item){activateSkill({api:props.api,controller:props.controller,access:props.preferences,dialogs,skill:item,onConfigure:toolbar?(navigation)=>toolbar.configure({kind:"skill",name:item.name,location:item.location,icon:"book"
|
|
@@ -6292,34 +6453,34 @@ return _el$
|
|
|
6292
6453
|
|
|
6293
6454
|
|
|
6294
6455
|
|
|
6295
|
-
function createSubagentSectionModel(props){const list=
|
|
6296
|
-
const recent=
|
|
6297
|
-
const rows=
|
|
6298
|
-
const [query,setQuery]=
|
|
6299
|
-
const [mode,setMode]=
|
|
6300
|
-
const counts=
|
|
6301
|
-
const tabs=
|
|
6302
|
-
const filtered=
|
|
6303
|
-
const parentID=
|
|
6304
|
-
const targetKey=
|
|
6305
|
-
|
|
6456
|
+
function createSubagentSectionModel(props){const list=createMemo18(()=>props.controller.list(props.sessionID))
|
|
6457
|
+
const recent=createMemo18(()=>props.controller.recent?.(props.sessionID)??[])
|
|
6458
|
+
const rows=createMemo18(()=>buildSubagentView(list(),recent()))
|
|
6459
|
+
const [query,setQuery]=createSignal38("")
|
|
6460
|
+
const [mode,setMode]=createSignal38("all")
|
|
6461
|
+
const counts=createMemo18(()=>countSubagentView(rows(),query()))
|
|
6462
|
+
const tabs=createMemo18(()=>["all","active","recent",...counts().errors>0?["errors"]:[]])
|
|
6463
|
+
const filtered=createMemo18(()=>filterSubagentView(rows(),mode(),query()))
|
|
6464
|
+
const parentID=createMemo18(()=>props.api.state?.session?.get(props.sessionID)?.parentID)
|
|
6465
|
+
const targetKey=createMemo18(()=>props.controller.target?.(props.sessionID).key??props.sessionID)
|
|
6466
|
+
createEffect21(()=>{targetKey()
|
|
6306
6467
|
setQuery("")
|
|
6307
6468
|
setMode("all")
|
|
6308
6469
|
})
|
|
6309
|
-
|
|
6470
|
+
createEffect21(()=>{if(mode()!=="all"&&counts()[mode()]===0)
|
|
6310
6471
|
setMode("all")
|
|
6311
6472
|
})
|
|
6312
6473
|
const visibility=createListVisibility({items:filtered,limit:()=>props.preferences.sectionItemLimit?.("subagents")??0,resetKey:()=>JSON.stringify([targetKey(),query(),mode()])
|
|
6313
6474
|
})
|
|
6314
|
-
const clockKey=
|
|
6475
|
+
const clockKey=createMemo18(()=>props.preferences.expanded().subagents&&visibility.visible().some((item)=>item.status.type!=="idle")?targetKey():undefined)
|
|
6315
6476
|
const now=createSubagentClock(clockKey)
|
|
6316
|
-
const byId=
|
|
6317
|
-
const ids=
|
|
6318
|
-
const state=
|
|
6319
|
-
|
|
6477
|
+
const byId=createMemo18(()=>new Map(visibility.visible().map((item)=>[item.session.id,item])))
|
|
6478
|
+
const ids=createMemo18(()=>visibility.visible().map((item)=>item.session.id))
|
|
6479
|
+
const state=createMemo18(()=>props.controller.state(props.sessionID))
|
|
6480
|
+
createEffect21(()=>{const sessionID=props.sessionID
|
|
6320
6481
|
targetKey()
|
|
6321
6482
|
const deactivate=untrack9(()=>props.controller.activate?.(sessionID)??(()=>{}))
|
|
6322
|
-
|
|
6483
|
+
onCleanup25(deactivate)
|
|
6323
6484
|
untrack9(()=>void props.controller.refresh(sessionID).catch(()=>{}))
|
|
6324
6485
|
})
|
|
6325
6486
|
return{list,recent,rows,query,setQuery,mode,setMode,counts,tabs,filtered,parentID,visibility,now,byId,ids,state
|
|
@@ -6706,28 +6867,28 @@ return _el$2
|
|
|
6706
6867
|
return _el$
|
|
6707
6868
|
}})
|
|
6708
6869
|
}
|
|
6709
|
-
function TodoSection(props){const list=
|
|
6870
|
+
function TodoSection(props){const list=createMemo19(()=>props.controller.list(props.sessionID))
|
|
6710
6871
|
const icons=useIcons()
|
|
6711
6872
|
const unavailable=()=>hostCapabilityUnavailable(props.api,"todo")
|
|
6712
6873
|
const managed=()=>("guidance" in props.controller)?props.controller:undefined
|
|
6713
6874
|
const native=()=>("source" in props.controller)&&props.controller.source==="native"
|
|
6714
6875
|
const setup=()=>("setup" in props.controller)?props.controller.setup:undefined
|
|
6715
6876
|
const setupState=()=>setup()?.state(props.sessionID)
|
|
6716
|
-
const [mode,setMode]=
|
|
6717
|
-
const targetKey=
|
|
6718
|
-
|
|
6877
|
+
const [mode,setMode]=createSignal39("all")
|
|
6878
|
+
const targetKey=createMemo19(()=>props.controller.target?.(props.sessionID).key??props.sessionID)
|
|
6879
|
+
createEffect22(()=>{targetKey()
|
|
6719
6880
|
setMode("all")
|
|
6720
6881
|
})
|
|
6721
|
-
const view=
|
|
6882
|
+
const view=createMemo19(()=>buildTodoView(list(),mode()))
|
|
6722
6883
|
const visibility=createListVisibility({items:()=>view().rows,limit:()=>props.preferences.sectionItemLimit?.("todo")??0,resetKey:()=>JSON.stringify([targetKey(),mode()])
|
|
6723
6884
|
})
|
|
6724
|
-
const state=
|
|
6725
|
-
|
|
6885
|
+
const state=createMemo19(()=>props.controller.state(props.sessionID))
|
|
6886
|
+
createEffect22(()=>{const sessionID=props.sessionID
|
|
6726
6887
|
targetKey()
|
|
6727
6888
|
if(unavailable())
|
|
6728
6889
|
return
|
|
6729
6890
|
const deactivate=untrack10(()=>props.controller.activate?.(sessionID)??(()=>{}))
|
|
6730
|
-
|
|
6891
|
+
onCleanup26(deactivate)
|
|
6731
6892
|
untrack10(()=>void props.controller.refresh(sessionID).catch(()=>{}))
|
|
6732
6893
|
untrack10(()=>void setup()?.refresh(sessionID).catch(()=>{}))
|
|
6733
6894
|
})
|
|
@@ -6796,8 +6957,8 @@ return _el$4
|
|
|
6796
6957
|
}})
|
|
6797
6958
|
}
|
|
6798
6959
|
function SidebarContent(props){const sections=props.preferences.sections
|
|
6799
|
-
const visibleSections=
|
|
6800
|
-
|
|
6960
|
+
const visibleSections=createMemo20(()=>props.preferences.sectionOrder().filter((section)=>sections()[section]&&supportsSidebarSection(props.api,section)))
|
|
6961
|
+
onCleanup27(()=>props.interaction.setContentRoot(undefined))
|
|
6801
6962
|
return _$createComponent(ToolbarProvider,{get api(){return props.api
|
|
6802
6963
|
},get preferences(){return props.preferences
|
|
6803
6964
|
},get skills(){return props.skills
|
|
@@ -6848,8 +7009,8 @@ return _el$
|
|
|
6848
7009
|
}
|
|
6849
7010
|
|
|
6850
7011
|
|
|
6851
|
-
function McpPersistence(props){const persist=
|
|
6852
|
-
|
|
7012
|
+
function McpPersistence(props){const persist=createMemo21(()=>props.controller.persist())
|
|
7013
|
+
createEffect23(()=>{if(!props.api.state.ready)
|
|
6853
7014
|
return
|
|
6854
7015
|
persist()
|
|
6855
7016
|
const route=props.api.route.current
|
|
@@ -6857,12 +7018,12 @@ const params="params" in route?route.params:undefined
|
|
|
6857
7018
|
if(typeof params?.sessionID!=="string")
|
|
6858
7019
|
return
|
|
6859
7020
|
const current=props.controller.target()
|
|
6860
|
-
|
|
7021
|
+
onCleanup28(()=>props.controller.deactivate(current))
|
|
6861
7022
|
untrack11(()=>void props.controller.activate(current).catch(()=>{}))
|
|
6862
7023
|
})
|
|
6863
7024
|
return _$createElement("box")
|
|
6864
7025
|
}
|
|
6865
|
-
function PreferencesPersistence(props){
|
|
7026
|
+
function PreferencesPersistence(props){createEffect23(()=>{const location=currentLocation(props.api)
|
|
6866
7027
|
untrack11(()=>props.controller.setActiveScope(preferencesScope({directory:location.routing.directory,worktree:props.api.state.path.worktree
|
|
6867
7028
|
})))
|
|
6868
7029
|
props.controller.load()
|
|
@@ -6870,7 +7031,7 @@ props.controller.load()
|
|
|
6870
7031
|
return _$createElement("box")
|
|
6871
7032
|
}
|
|
6872
7033
|
function FirstRunWizardPersistence(props){let isChecked=false
|
|
6873
|
-
|
|
7034
|
+
createEffect23(()=>{if(isChecked)
|
|
6874
7035
|
return
|
|
6875
7036
|
isChecked=true
|
|
6876
7037
|
showFirstRunWizard(props.api,props.preferences).catch(()=>{})
|
|
@@ -6888,7 +7049,7 @@ return _$createElement("box")
|
|
|
6888
7049
|
|
|
6889
7050
|
function SidebarTitle(props){const icons=useIcons()
|
|
6890
7051
|
const theme=()=>props.api.theme.current
|
|
6891
|
-
const created=
|
|
7052
|
+
const created=createMemo22(()=>{const value=props.api.state.session.get(props.sessionID)?.time?.created
|
|
6892
7053
|
return value===undefined?undefined:`Created ${new Date(value).toLocaleDateString("en", {
|
|
6893
7054
|
dateStyle: "medium"
|
|
6894
7055
|
})}`
|
|
@@ -6897,7 +7058,7 @@ const settingsId=`${PLUGIN_ID}.settings`
|
|
|
6897
7058
|
const settings=useSidebarItem(props.api,props.interaction,{id:settingsId,position:{section:0,row:0,column:0
|
|
6898
7059
|
},activate:()=>openSettings(props.api,props.preferences,undefined,props.mcp,props.skills)
|
|
6899
7060
|
},()=>theme().textMuted,"control")
|
|
6900
|
-
|
|
7061
|
+
onCleanup29(()=>props.interaction.setTitleRoot(undefined))
|
|
6901
7062
|
return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("box"),_el$3=_$createElement("box")
|
|
6902
7063
|
_$insertNode(_el$,_el$2)
|
|
6903
7064
|
_$use((node)=>props.interaction.setTitleRoot(node),_el$)
|
|
@@ -6957,40 +7118,10 @@ const [user,project]=await Promise.all([safe(userPath?readSource(userPath,()=>re
|
|
|
6957
7118
|
return mergeConfiguredDefaults(configuredDefaultsFromPluginOptions(options),user,project)
|
|
6958
7119
|
}
|
|
6959
7120
|
|
|
6960
|
-
|
|
6961
|
-
var scheduleInterval=(callback,milliseconds)=>{const timer=setInterval(callback,milliseconds)
|
|
6962
|
-
return()=>clearInterval(timer)
|
|
6963
|
-
}
|
|
6964
|
-
function createLimitsRefresh(options,schedule=scheduleInterval){let disposed=false
|
|
6965
|
-
const selectedKey=createMemo22(()=>JSON.stringify(options.selectedModel?.()))
|
|
6966
|
-
const targetKey=createMemo22(()=>JSON.stringify(options.controller.current().model))
|
|
6967
|
-
const interval=createMemo22(()=>(parseLimitsRefreshMinutes(options.refreshMinutes?.())??DEFAULT_LIMITS_REFRESH_MINUTES)*60000)
|
|
6968
|
-
createEffect23(on2(selectedKey,()=>{const model=options.selectedModel?.()
|
|
6969
|
-
if(model&&options.accountSource)
|
|
6970
|
-
options.accountSource.refresh(model).catch(()=>{})
|
|
6971
|
-
}))
|
|
6972
|
-
createEffect23(on2(targetKey,()=>{const{model,adapter}=options.controller.current()
|
|
6973
|
-
if(model&&adapter)
|
|
6974
|
-
options.controller.refresh()
|
|
6975
|
-
}))
|
|
6976
|
-
async function refresh(){if(disposed)
|
|
6977
|
-
return
|
|
6978
|
-
const target=targetKey()
|
|
6979
|
-
const model=options.selectedModel?.()
|
|
6980
|
-
if(model&&options.accountSource)
|
|
6981
|
-
await options.accountSource.refresh(model)
|
|
6982
|
-
if(!disposed&&target===targetKey())
|
|
6983
|
-
await options.controller.refresh()
|
|
6984
|
-
}
|
|
6985
|
-
createEffect23(on2(interval,(milliseconds)=>{onCleanup29(schedule(()=>void refresh().catch(()=>{}),milliseconds))
|
|
6986
|
-
}))
|
|
6987
|
-
onCleanup29(()=>{disposed=true
|
|
6988
|
-
})
|
|
6989
|
-
}
|
|
6990
7121
|
function LimitsPersistence(props){createLimitsRefresh(props)
|
|
6991
7122
|
return _$createElement("box")
|
|
6992
7123
|
}
|
|
6993
|
-
function bindNavigatorAbout(api,status,updates,notes){const dispose=registerNavigatorAboutServices(api,{navigatorVersion:"0.
|
|
7124
|
+
function bindNavigatorAbout(api,status,updates,notes){const dispose=registerNavigatorAboutServices(api,{navigatorVersion:"0.27.0",openCodeVersion:api.app.version,checkState:()=>status.navigatorCheckState?.()??{status:"idle"},checking:()=>status.navigatorCheckState?.().status==="checking",updating:updates.navigatorUpdating,installed:updates.navigatorInstalled,check:()=>status.refreshNavigator?.()??Promise.resolve("unavailable"),update(dialogs){const target=status.navigatorUpdate()
|
|
6994
7125
|
if(target)
|
|
6995
7126
|
updates.navigator(target,dialogs)
|
|
6996
7127
|
},changelog:async ()=>notes.parseChangelog(await notes.readBundledChangelog())
|
|
@@ -7013,11 +7144,11 @@ const candidates=context.data.location.skill.list({directory})?.filter((skill)=>
|
|
|
7013
7144
|
const id=candidates.length===1?candidates[0].id:undefined
|
|
7014
7145
|
if(route.type!=="session"||session?.location.directory!==directory||!id)
|
|
7015
7146
|
return{data:false}
|
|
7016
|
-
await context.client.session.skill({sessionID:route.sessionID,id},{signal:options.signal})
|
|
7147
|
+
await context.client.experimental.session.skill({sessionID:route.sessionID,id},{signal:options.signal})
|
|
7017
7148
|
return{data:true}}},mcp:{async status(input,options={}){const response=await context.client.mcp.list(location(input),{signal:options.signal})
|
|
7018
7149
|
const data=Object.fromEntries(response.data.map((server)=>[server.name,server.status]))
|
|
7019
|
-
return{data}},async connect(input,options={}){await context.client.mcp.connect({server:input.name,...location(input)},{signal:options.signal})
|
|
7020
|
-
return{data:undefined}},async disconnect(input,options={}){await context.client.mcp.disconnect({server:input.name,...location(input)},{signal:options.signal})
|
|
7150
|
+
return{data}},async connect(input,options={}){await context.client.experimental.mcp.connect({server:input.name,...location(input)},{signal:options.signal})
|
|
7151
|
+
return{data:undefined}},async disconnect(input,options={}){await context.client.experimental.mcp.disconnect({server:input.name,...location(input)},{signal:options.signal})
|
|
7021
7152
|
return{data:undefined}}}}}}
|
|
7022
7153
|
var TERMINAL_EVENTS=new Set(["session.execution.succeeded","session.execution.failed","session.execution.interrupted"])
|
|
7023
7154
|
function executionStatus(name,event){const sessionID=event.data.sessionID
|
|
@@ -7037,7 +7168,7 @@ return ["session.status","session.retry.scheduled",...executions]
|
|
|
7037
7168
|
if(name==="session.idle")
|
|
7038
7169
|
return ["session.idle",...executions.slice(1)]
|
|
7039
7170
|
if(name==="session.error")
|
|
7040
|
-
return ["session.execution.failed"]
|
|
7171
|
+
return ["session.execution.failed","session.execution.interrupted"]
|
|
7041
7172
|
if(name==="mcp.tools.changed")
|
|
7042
7173
|
return ["mcp.status.changed","mcp.resources.changed"]
|
|
7043
7174
|
if(["session.created","session.deleted","server.connected","installation.update-available"].includes(name))
|
|
@@ -7082,7 +7213,7 @@ if(name==="session.deleted"){emit({sessionID})
|
|
|
7082
7213
|
return
|
|
7083
7214
|
}
|
|
7084
7215
|
if(name==="session.error"){const error=data.error
|
|
7085
|
-
emit({sessionID,error:error&&{name:error.type??"Error",data:{message:error.message??"Session error"}}})
|
|
7216
|
+
emit({sessionID,error:event.type==="session.execution.interrupted"?{name:"MessageAbortedError",data:{message:"Session aborted"}}:error&&{name:error.type??"Error",data:{message:error.message??"Session error"}}})
|
|
7086
7217
|
return
|
|
7087
7218
|
}
|
|
7088
7219
|
const info=context.data.session.get(sessionID)
|
|
@@ -7126,7 +7257,7 @@ context.keymap.dispatch(command.id)
|
|
|
7126
7257
|
context.renderer.keyInput.on("keypress",onKeypress)
|
|
7127
7258
|
const keymap={registerLayer(layer){const id=++sequence
|
|
7128
7259
|
const owner=getOwner()
|
|
7129
|
-
const [active,setActive]=
|
|
7260
|
+
const [active,setActive]=createSignal40(true)
|
|
7130
7261
|
const install=()=>{context.keymap.layer(()=>({mode:typeof layer.mode==="string"?layer.mode:"base",priority:layer.priority,enabled:()=>active()&&enabled(layer.enabled),target:layer.target?()=>layer.target:undefined,commands:v2LayerCommands(id,layer,()=>true,run,context.keymap.dispatch)
|
|
7131
7262
|
}))
|
|
7132
7263
|
}
|
|
@@ -7171,7 +7302,7 @@ cleanups.clear()
|
|
|
7171
7302
|
context.renderer.keyInput.off("keypress",onKeypress)
|
|
7172
7303
|
}}}
|
|
7173
7304
|
|
|
7174
|
-
function createV2SessionAdapter(context){const [retryDetails,setRetryDetails]=
|
|
7305
|
+
function createV2SessionAdapter(context){const [retryDetails,setRetryDetails]=createSignal41({})
|
|
7175
7306
|
const projections=new WeakMap
|
|
7176
7307
|
function normalize(info){const directory=info.location.directory
|
|
7177
7308
|
const workspaceID=context.location?.directory===directory?context.location.workspaceID:undefined
|
|
@@ -7243,7 +7374,7 @@ function DialogPrompt(props){return _$createComponent(SingleLinePrompt,_$mergePr
|
|
|
7243
7374
|
function DialogConfirm(props){let active=true
|
|
7244
7375
|
onCleanup30(()=>{active=false
|
|
7245
7376
|
})
|
|
7246
|
-
|
|
7377
|
+
async function confirm(){const confirmed=await context.ui.dialog.confirm({title:props.title,message:props.message
|
|
7247
7378
|
})
|
|
7248
7379
|
if(!active)
|
|
7249
7380
|
return
|
|
@@ -7251,6 +7382,10 @@ if(confirmed)
|
|
|
7251
7382
|
props.onConfirm?.()
|
|
7252
7383
|
else
|
|
7253
7384
|
props.onCancel?.()
|
|
7385
|
+
}
|
|
7386
|
+
onMount7(()=>{confirm().catch(()=>{if(active)
|
|
7387
|
+
props.onCancel?.()
|
|
7388
|
+
})
|
|
7254
7389
|
})
|
|
7255
7390
|
return _$createElement("box")
|
|
7256
7391
|
}
|
|
@@ -7334,48 +7469,18 @@ return parseTodos(item.state.input.todos)
|
|
|
7334
7469
|
}}
|
|
7335
7470
|
return []
|
|
7336
7471
|
}
|
|
7337
|
-
function createNativeTodoController(context,api){const
|
|
7338
|
-
const pending=new Map
|
|
7339
|
-
let activeTarget
|
|
7340
|
-
function target(sessionID){const session=api.state.session.get(sessionID)
|
|
7472
|
+
function createNativeTodoController(context,api){function target(sessionID){const session=api.state.session.get(sessionID)
|
|
7341
7473
|
const directory=session?.directory??api.state.path.directory
|
|
7342
7474
|
const workspace=session?.workspaceID
|
|
7343
7475
|
return{key:JSON.stringify([sessionID,directory,workspace??null]),sessionID,routing:{directory,...workspace&&{workspace}}}}
|
|
7344
7476
|
function list(sessionID){return nativeTodos(context.data.session.message.list(sessionID),context.data.session.get(sessionID)?.revert?.messageID)
|
|
7345
7477
|
}
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
const existing=pending.get(current.key)
|
|
7350
|
-
if(existing&&!force)
|
|
7351
|
-
return existing
|
|
7352
|
-
const requestState=requests.start(current.key,"refresh native todos",context.data.session.message.list(sessionID).length>0,force)
|
|
7353
|
-
if(!requestState)
|
|
7354
|
-
return existing
|
|
7355
|
-
const request=Promise.try(()=>Promise.all([context.data.session.sync(sessionID),context.data.session.message.sync(sessionID)])).then(()=>{if(!requestState.isCurrent())
|
|
7356
|
-
return []
|
|
7357
|
-
requestState.succeed()
|
|
7478
|
+
return{source:"native",...createTodoModel({lifecycle:api.lifecycle,target,cache:"source",hasValue:(sessionID)=>context.data.session.message.list(sessionID).length>0,source:{list,async refresh(sessionID,signal){signal.throwIfAborted()
|
|
7479
|
+
await Promise.all([context.data.session.sync(sessionID),context.data.session.message.sync(sessionID)])
|
|
7480
|
+
signal.throwIfAborted()
|
|
7358
7481
|
return list(sessionID)
|
|
7359
|
-
})
|
|
7360
|
-
|
|
7361
|
-
}).finally(()=>{requestState.finish()
|
|
7362
|
-
if(pending.get(current.key)===request)
|
|
7363
|
-
pending.delete(current.key)
|
|
7364
|
-
})
|
|
7365
|
-
pending.set(current.key,request)
|
|
7366
|
-
return request
|
|
7367
|
-
}
|
|
7368
|
-
return{source:"native",target,list,refresh,state:(sessionID)=>requests.state(target(sessionID).key),retry:(sessionID)=>refresh(sessionID,true),activate(sessionID){const key=target(sessionID).key
|
|
7369
|
-
if(activeTarget!==key){requests.abortAll()
|
|
7370
|
-
pending.clear()
|
|
7371
|
-
activeTarget=key
|
|
7372
|
-
}
|
|
7373
|
-
return()=>{if(activeTarget!==key)
|
|
7374
|
-
return
|
|
7375
|
-
activeTarget=undefined
|
|
7376
|
-
requests.abortAll()
|
|
7377
|
-
pending.clear()
|
|
7378
|
-
}}}}
|
|
7482
|
+
}}})
|
|
7483
|
+
}}
|
|
7379
7484
|
function collectionLocation(target){return{directory:target.routing.directory??"",...target.routing.workspace&&{workspaceID:target.routing.workspace}}}
|
|
7380
7485
|
async function syncCollection(collection,target,force,signal){signal.throwIfAborted()
|
|
7381
7486
|
const ref=collectionLocation(target)
|
|
@@ -7384,9 +7489,9 @@ collection.invalidate(ref)
|
|
|
7384
7489
|
await collection.sync(ref)
|
|
7385
7490
|
signal.throwIfAborted()
|
|
7386
7491
|
}
|
|
7387
|
-
function
|
|
7492
|
+
function createNativeMcpSource(data){const collection=()=>data().location.mcp.server
|
|
7388
7493
|
const projections=new WeakMap
|
|
7389
|
-
const list=(target)=>(collection.list(collectionLocation(target))??[]).map((server)=>{let item=projections.get(server)
|
|
7494
|
+
const list=(target)=>(collection().list(collectionLocation(target))??[]).map((server)=>{let item=projections.get(server)
|
|
7390
7495
|
if(!item){item={get name(){return server.name
|
|
7391
7496
|
},get title(){return server.title
|
|
7392
7497
|
},get status(){return server.status.status
|
|
@@ -7396,12 +7501,17 @@ projections.set(server,item)
|
|
|
7396
7501
|
}
|
|
7397
7502
|
return item
|
|
7398
7503
|
})
|
|
7399
|
-
return{list,async refresh(target,force,signal){
|
|
7504
|
+
return{list,async refresh(target,force,signal){const current=collection()
|
|
7505
|
+
await syncCollection(current,target,force,signal)
|
|
7506
|
+
if(collection()!==current)
|
|
7507
|
+
throw new DOMException("OpenCode connection changed","AbortError")
|
|
7400
7508
|
return list(target)
|
|
7401
7509
|
}}}
|
|
7402
|
-
function
|
|
7403
|
-
|
|
7404
|
-
|
|
7510
|
+
function createV2McpSource(context){return createNativeMcpSource(()=>context.data)
|
|
7511
|
+
}
|
|
7512
|
+
function createNativeSkillSource(input){const projections=new WeakMap
|
|
7513
|
+
const collection=()=>input.data().location.skill
|
|
7514
|
+
const list=(target)=>(collection().list(collectionLocation(target))??[]).map((skill)=>{let item=projections.get(skill)
|
|
7405
7515
|
if(!item){item={get id(){return skill.id
|
|
7406
7516
|
},get name(){return skill.name
|
|
7407
7517
|
},get description(){return skill.description
|
|
@@ -7412,28 +7522,49 @@ projections.set(skill,item)
|
|
|
7412
7522
|
}
|
|
7413
7523
|
return item
|
|
7414
7524
|
})
|
|
7415
|
-
return{list,async refresh(target,force,signal){
|
|
7525
|
+
return{list,async refresh(target,force,signal){const current=collection()
|
|
7526
|
+
await syncCollection(current,target,force,signal)
|
|
7527
|
+
if(collection()!==current)
|
|
7528
|
+
throw new DOMException("OpenCode connection changed","AbortError")
|
|
7416
7529
|
return list(target)
|
|
7417
7530
|
},async use(target,skill,signal){signal.throwIfAborted()
|
|
7418
|
-
const route=context.ui.router.current()
|
|
7419
|
-
const session=route.type==="session"&&context.data.session.get(route.sessionID)
|
|
7420
7531
|
const actual=list(target).find((item)=>item.id===skill.id&&item.location===skill.location)
|
|
7421
|
-
if(
|
|
7532
|
+
if(!actual?.id)
|
|
7422
7533
|
throw new Error("Skill target changed; select the source again")
|
|
7423
|
-
|
|
7424
|
-
return !signal.aborted
|
|
7534
|
+
return input.use(target,actual,signal)
|
|
7425
7535
|
}}}
|
|
7426
|
-
function
|
|
7427
|
-
|
|
7428
|
-
|
|
7536
|
+
function createV2SkillSource(context){return createNativeSkillSource({data:()=>context.data,async use(target,skill,signal){const route=context.ui.router.current()
|
|
7537
|
+
const session=route.type==="session"&&context.data.session.get(route.sessionID)
|
|
7538
|
+
if(route.type!=="session"||!session||session.location.directory!==target.routing.directory||!skill.id)
|
|
7539
|
+
throw new Error("Skill target changed; select the source again")
|
|
7540
|
+
await context.client.experimental.session.skill({sessionID:route.sessionID,id:skill.id},{signal})
|
|
7541
|
+
return !signal.aborted
|
|
7542
|
+
}})
|
|
7543
|
+
}
|
|
7544
|
+
function createNativeSubagentSource(input){return{list:(parentID)=>input.sessions().list().filter((item)=>item.parentID===parentID).flatMap((item)=>{const projected=input.project(item)
|
|
7545
|
+
return projected?[projected]:[]
|
|
7546
|
+
}),status:input.status,startedAt(id){const sessions=input.sessions()
|
|
7547
|
+
if(sessions.status(id)!=="running")
|
|
7548
|
+
return
|
|
7549
|
+
const time=sessions.get(id)?.time
|
|
7550
|
+
if(!time?.busy||time.idle!==undefined&&time.idle>=time.busy)
|
|
7551
|
+
return
|
|
7552
|
+
return time.busy
|
|
7553
|
+
},async refresh(parentID,signal){signal.throwIfAborted()
|
|
7554
|
+
const client=input.client()
|
|
7555
|
+
const sessions=input.sessions()
|
|
7429
7556
|
let cursor
|
|
7430
|
-
do{const page=await
|
|
7431
|
-
|
|
7432
|
-
|
|
7557
|
+
do{const page=await client.session.list(cursor?{cursor,limit:100}:{parentID,limit:100},{signal})
|
|
7558
|
+
signal.throwIfAborted()
|
|
7559
|
+
if(input.client()!==client||input.sessions()!==sessions)
|
|
7560
|
+
throw new DOMException("Subagent connection changed","AbortError")
|
|
7561
|
+
await Promise.all(page.data.filter((item)=>!sessions.get(item.id)).map((item)=>sessions.sync(item.id)))
|
|
7433
7562
|
cursor=page.cursor.next??undefined
|
|
7434
7563
|
signal.throwIfAborted()
|
|
7435
7564
|
} while(cursor)
|
|
7436
7565
|
}}}
|
|
7566
|
+
function createV2SubagentSource(context,api){return createNativeSubagentSource({sessions:()=>context.data.session,client:()=>context.client,project:(info)=>api.state.session.get(info.id),status:(id)=>api.state.session.status(id)??{type:"idle"}})
|
|
7567
|
+
}
|
|
7437
7568
|
function createOpenCodeV2Sources(context,api){return{skills:createV2SkillSource(context),mcp:createV2McpSource(context),subagents:createV2SubagentSource(context,api)
|
|
7438
7569
|
}}
|
|
7439
7570
|
function loadNavigatorUpdates(){const path=import.meta.url.includes("/dist/tui.js")?"./navigator-updates.js":"../navigator-updates"
|
|
@@ -7455,7 +7586,7 @@ const limitsPath=import.meta.url.includes("/dist/tui.js")?"./provider-limits.js"
|
|
|
7455
7586
|
const limitsModule=await import(limitsPath)
|
|
7456
7587
|
const LimitsSection=limitsModule.ProviderLimitsSection
|
|
7457
7588
|
const{controller:limits,codex,accountSource,selectModel,modelSelection
|
|
7458
|
-
}=limitsModule.createProviderQuotaIntegration(api,preferences,"0.
|
|
7589
|
+
}=limitsModule.createProviderQuotaIntegration(api,preferences,"0.27.0",hostContext)
|
|
7459
7590
|
const mcp=createMcpController(api,preferences.persistMcp,preferences,nativeSources?.mcp)
|
|
7460
7591
|
const interaction=createSidebarInteraction(api,()=>openKeyboardHelp(api,preferences.lspIconStyle,preferences.cornerFont,sidebarShortcutActions(api)))
|
|
7461
7592
|
const startup=createStartupSessionController(api,preferences)
|
|
@@ -7484,7 +7615,7 @@ api.slots.register({order:100,slots:{app(){return _$createComponent(IconProvider
|
|
|
7484
7615
|
},interaction,skills,subagents,mcp
|
|
7485
7616
|
}),_$createComponent(FirstRunWizardPersistence,{api,preferences
|
|
7486
7617
|
}),_$createComponent(StartupSessionBinding,{api,controller:startup
|
|
7487
|
-
}),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.
|
|
7618
|
+
}),_$createComponent(UpgradeNotesBinding,{api,preferences,version:"0.27.0"
|
|
7488
7619
|
}),_$createComponent(McpPersistence,{api,controller:mcp
|
|
7489
7620
|
})]
|
|
7490
7621
|
}})
|
|
@@ -7518,13 +7649,13 @@ return result.data?.type==="text"?result.data.content:undefined
|
|
|
7518
7649
|
const updateServices=await loadNavigatorUpdates()
|
|
7519
7650
|
const{createVersionStatus,createVersionUpdateActions,createOpenCodeV1VersionUpdater
|
|
7520
7651
|
}=updateServices
|
|
7521
|
-
const versionStatus=createVersionStatus(api,"0.
|
|
7522
|
-
const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.
|
|
7652
|
+
const versionStatus=createVersionStatus(api,"0.27.0")
|
|
7653
|
+
const versionUpdates=createVersionUpdateActions(api,versionStatus,createOpenCodeV1VersionUpdater(api,meta),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.27.0"))
|
|
7523
7654
|
bindNavigatorAbout(api,versionStatus,versionUpdates,updateServices)
|
|
7524
7655
|
api.slots.register({order:90,slots:{sidebar_footer(_context,props){return _$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
|
|
7525
7656
|
},get multilineCorners(){return preferences.cornerFont
|
|
7526
7657
|
},get children(){return _$createComponent(VersionFooter,{api,get sessionID(){return props.session_id
|
|
7527
|
-
},navigatorVersion:"0.
|
|
7658
|
+
},navigatorVersion:"0.27.0",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
|
|
7528
7659
|
},get onNavigatorUpdate(){return versionUpdates.navigator
|
|
7529
7660
|
}})
|
|
7530
7661
|
}})
|
|
@@ -7541,8 +7672,8 @@ return new TextDecoder().decode(await context.client.file.read({location:context
|
|
|
7541
7672
|
const updateServices=await loadNavigatorUpdates()
|
|
7542
7673
|
const{createVersionStatus,createVersionUpdateActions,createOpenCodeV2VersionUpdater
|
|
7543
7674
|
}=updateServices
|
|
7544
|
-
const versionStatus=createVersionStatus(adapter.api,"0.
|
|
7545
|
-
const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.
|
|
7675
|
+
const versionStatus=createVersionStatus(adapter.api,"0.27.0")
|
|
7676
|
+
const versionUpdates=createVersionUpdateActions(adapter.api,versionStatus,createOpenCodeV2VersionUpdater(context),()=>preferences.rememberNavigatorVersionBeforeUpdate("0.27.0"))
|
|
7546
7677
|
bindNavigatorAbout(adapter.api,versionStatus,versionUpdates,updateServices)
|
|
7547
7678
|
const disposeFooter=context.ui.slot({append:"sidebar.footer",render:()=>_$createComponent(IconProvider,{get style(){return preferences.lspIconStyle
|
|
7548
7679
|
},get multilineCorners(){return preferences.cornerFont
|
|
@@ -7550,7 +7681,7 @@ const disposeFooter=context.ui.slot({append:"sidebar.footer",render:()=>_$create
|
|
|
7550
7681
|
_$setProp(_el$,"flexDirection","row")
|
|
7551
7682
|
_$setProp(_el$,"justifyContent","space-between")
|
|
7552
7683
|
_$insert(_el$,_$createComponent(VersionSummary,{get api(){return adapter.api
|
|
7553
|
-
},navigatorVersion:"0.
|
|
7684
|
+
},navigatorVersion:"0.27.0",status:versionStatus,get onOpenCodeUpdate(){return versionUpdates.openCode
|
|
7554
7685
|
},get onNavigatorUpdate(){return versionUpdates.navigator
|
|
7555
7686
|
}}),null)
|
|
7556
7687
|
_$insert(_el$,_$createComponent(SettingsFooterButton,{get api(){return adapter.api
|