opencode-navigator 0.19.2 → 0.20.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 +13 -0
- package/README.md +107 -3
- package/dist/{chunk-r37n7w97.js → chunk-c1kw6ktv.js} +3 -4
- package/dist/chunk-cmmnfes2.js +7 -0
- package/dist/layout-preset-actions.js +438 -0
- package/dist/mcp-groups.js +4 -4
- package/dist/project-profile.js +280 -0
- package/dist/provider-limits.js +739 -0
- package/dist/quick-actions-settings.js +2 -5
- package/dist/settings-import.js +40 -0
- package/dist/skill-groups.js +4 -4
- package/dist/trusted-skills.js +22 -0
- package/dist/tui.js +389 -395
- package/package.json +4 -2
- package/scripts/save-project-profile.mjs +41 -0
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
import {PLUGIN_ID
|
|
2
|
+
} from "./chunk-cmmnfes2.js";
|
|
3
|
+
function createCodexAppServerClient(input){const command=input.command??["codex","app-server","--stdio"]
|
|
4
|
+
const timeoutMs=input.timeoutMs??1e4
|
|
5
|
+
const pending=new Map
|
|
6
|
+
let child
|
|
7
|
+
let startup
|
|
8
|
+
let sequence=0
|
|
9
|
+
let disposed=false
|
|
10
|
+
function failAll(error){for(const [id,request] of pending){clearTimeout(request.timer)
|
|
11
|
+
request.cleanup()
|
|
12
|
+
request.reject(error)
|
|
13
|
+
pending.delete(id)
|
|
14
|
+
}}
|
|
15
|
+
function write(message){if(!child?.stdin||typeof child.stdin==="number")
|
|
16
|
+
throw new Error("Codex app-server is unavailable")
|
|
17
|
+
child.stdin.write(`${JSON.stringify(message)}
|
|
18
|
+
`)
|
|
19
|
+
}
|
|
20
|
+
function accept(line){let message
|
|
21
|
+
try{message=JSON.parse(line)
|
|
22
|
+
} catch{return
|
|
23
|
+
}
|
|
24
|
+
if(typeof message.id!=="number")
|
|
25
|
+
return
|
|
26
|
+
const request=pending.get(message.id)
|
|
27
|
+
if(!request)
|
|
28
|
+
return
|
|
29
|
+
pending.delete(message.id)
|
|
30
|
+
clearTimeout(request.timer)
|
|
31
|
+
request.cleanup()
|
|
32
|
+
if(message.error){request.reject(new Error(message.error.code===-32601?"Codex app-server method unavailable":"Codex app-server request failed"))
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
request.resolve(message.result)
|
|
36
|
+
}
|
|
37
|
+
function stop(process,error){if(child!==process)
|
|
38
|
+
return
|
|
39
|
+
child=undefined
|
|
40
|
+
startup=undefined
|
|
41
|
+
failAll(error)
|
|
42
|
+
process.kill()
|
|
43
|
+
}
|
|
44
|
+
async function read(process){const output=process.stdout
|
|
45
|
+
if(!output||typeof output==="number")
|
|
46
|
+
return
|
|
47
|
+
const decoder=new TextDecoder
|
|
48
|
+
const reader=output.getReader()
|
|
49
|
+
let buffer=""
|
|
50
|
+
try{while(true){const next=await reader.read()
|
|
51
|
+
if(next.done)
|
|
52
|
+
break
|
|
53
|
+
buffer += decoder.decode(next.value,{stream:true})
|
|
54
|
+
if(buffer.length>1e6)
|
|
55
|
+
throw new Error("Codex app-server response exceeds 1 MB")
|
|
56
|
+
let end
|
|
57
|
+
while((end=buffer.indexOf(`
|
|
58
|
+
`))!==-1){const line=buffer.slice(0,end)
|
|
59
|
+
buffer=buffer.slice(end + 1)
|
|
60
|
+
if(line.length>0)
|
|
61
|
+
accept(line)
|
|
62
|
+
}}} catch(error){stop(process,error instanceof Error?error:new Error("Codex app-server stream failed"))
|
|
63
|
+
} finally{stop(process,new Error("Codex app-server closed"))
|
|
64
|
+
}}
|
|
65
|
+
function send(method,params,signal){if(disposed)
|
|
66
|
+
return Promise.reject(new DOMException("Codex app-server closed","AbortError"))
|
|
67
|
+
if(!child)
|
|
68
|
+
return Promise.reject(new Error("Codex app-server is unavailable"))
|
|
69
|
+
return new Promise((resolve,reject)=>{const id=++sequence
|
|
70
|
+
const abort=()=>{const request=pending.get(id)
|
|
71
|
+
if(!request)
|
|
72
|
+
return
|
|
73
|
+
pending.delete(id)
|
|
74
|
+
clearTimeout(request.timer)
|
|
75
|
+
request.cleanup()
|
|
76
|
+
reject(new DOMException("Codex request cancelled","AbortError"))
|
|
77
|
+
}
|
|
78
|
+
const timer=setTimeout(()=>{pending.delete(id)
|
|
79
|
+
signal?.removeEventListener("abort",abort)
|
|
80
|
+
reject(new Error("Codex app-server timed out"))
|
|
81
|
+
},timeoutMs)
|
|
82
|
+
if(signal?.aborted){clearTimeout(timer)
|
|
83
|
+
reject(new DOMException("Codex request cancelled","AbortError"))
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
signal?.addEventListener("abort",abort,{once:true})
|
|
87
|
+
pending.set(id,{resolve,reject,timer,cleanup:()=>signal?.removeEventListener("abort",abort)})
|
|
88
|
+
try{write({id,method,params})
|
|
89
|
+
} catch(error){abort()
|
|
90
|
+
reject(error)
|
|
91
|
+
}})
|
|
92
|
+
}
|
|
93
|
+
async function initialize(){if(disposed)
|
|
94
|
+
throw new Error("Codex app-server is unavailable")
|
|
95
|
+
const process=Bun.spawn([...command],{stdin:"pipe",stdout:"pipe",stderr:"ignore"})
|
|
96
|
+
child=process
|
|
97
|
+
read(process)
|
|
98
|
+
process.exited.then(()=>stop(process,new Error("Codex app-server closed")))
|
|
99
|
+
try{await send("initialize",{clientInfo:{name:"opencode-navigator",title:"Navigator",version:input.version},capabilities:null
|
|
100
|
+
})
|
|
101
|
+
write({method:"initialized"})
|
|
102
|
+
} catch(error){stop(process,error instanceof Error?error:new Error("Codex initialization failed"))
|
|
103
|
+
throw error
|
|
104
|
+
}}
|
|
105
|
+
return{async request(method,params={},signal){if(disposed)
|
|
106
|
+
throw new DOMException("Codex app-server closed","AbortError")
|
|
107
|
+
const starting=startup ??= initialize()
|
|
108
|
+
try{await starting
|
|
109
|
+
} catch(error){if(startup===starting)
|
|
110
|
+
startup=undefined
|
|
111
|
+
throw error
|
|
112
|
+
}
|
|
113
|
+
return await send(method,params,signal)
|
|
114
|
+
},dispose(){disposed=true
|
|
115
|
+
failAll(new DOMException("Codex app-server closed","AbortError"))
|
|
116
|
+
child?.kill()
|
|
117
|
+
}}}
|
|
118
|
+
function normalizeProviderQuota(snapshot){if(!snapshot.providerId||!snapshot.providerName||!snapshot.model.providerID||!snapshot.model.modelID||!snapshot.model.sessionID||!Number.isFinite(snapshot.fetchedAt)){throw new Error("Invalid provider quota identity")
|
|
119
|
+
}
|
|
120
|
+
const windows=snapshot.windows.map((window)=>{if(!window.id||!window.label||window.modelID!==snapshot.model.modelID||!window.unit||!Number.isFinite(window.used)||window.total!==undefined&&(!Number.isFinite(window.total)||window.total<0)||window.durationMinutes!==undefined&&(!Number.isFinite(window.durationMinutes)||window.durationMinutes<0)||window.resetsAt!==undefined&&!Number.isFinite(window.resetsAt)){throw new Error("Quota window does not match the selected model or contains invalid values")
|
|
121
|
+
}
|
|
122
|
+
return{...window}})
|
|
123
|
+
const bankedResets=snapshot.bankedResets
|
|
124
|
+
if(bankedResets&&(!Number.isSafeInteger(bankedResets.availableCount)||bankedResets.availableCount<0)){throw new Error("Invalid banked reset count")
|
|
125
|
+
}
|
|
126
|
+
return{...snapshot,windows,...bankedResets&&{bankedResets:{availableCount:bankedResets.availableCount,...bankedResets.credits&&{credits:[...bankedResets.credits]}}}}}
|
|
127
|
+
import {createSignal as createSignal2,untrack} from "solid-js"
|
|
128
|
+
import {createSignal} from "solid-js"
|
|
129
|
+
var idle={status:"idle"}
|
|
130
|
+
function isAbortError(cause){return typeof DOMException!=="undefined"&&cause instanceof DOMException&&cause.name==="AbortError"||cause instanceof Error&&cause.name==="AbortError"||typeof cause==="object"&&cause!==null&&"name" in cause&&cause.name==="AbortError"
|
|
131
|
+
}
|
|
132
|
+
function isRetryableError(cause){if(isAbortError(cause))
|
|
133
|
+
return false
|
|
134
|
+
if(typeof cause!=="object"||cause===null)
|
|
135
|
+
return true
|
|
136
|
+
const value=cause
|
|
137
|
+
const status=typeof value.status==="number"?value.status:value.response?.status
|
|
138
|
+
if(typeof status!=="number")
|
|
139
|
+
return true
|
|
140
|
+
return status===408||status===425||status===429||status>=500
|
|
141
|
+
}
|
|
142
|
+
function requestError(cause,operation,target){return{operation,target,message:cause instanceof Error?cause.message:`Failed to ${operation}`,retryable:isRetryableError(cause)
|
|
143
|
+
}}
|
|
144
|
+
function createRequestState(lifecycleSignal){const [states,setStates]=createSignal({})
|
|
145
|
+
const active=new Map
|
|
146
|
+
let sequence=0
|
|
147
|
+
function state(target){return states()[target]??idle
|
|
148
|
+
}
|
|
149
|
+
function abort(target){const request=active.get(target)
|
|
150
|
+
if(!request)
|
|
151
|
+
return
|
|
152
|
+
request.controller.abort()
|
|
153
|
+
active.delete(target)
|
|
154
|
+
const current=state(target)
|
|
155
|
+
setStates((values)=>({...values,[target]:current.lastSuccessfulAt?{status:"ready",lastSuccessfulAt:current.lastSuccessfulAt}:idle
|
|
156
|
+
}))
|
|
157
|
+
}
|
|
158
|
+
function abortAll(){for(const target of active.keys())
|
|
159
|
+
abort(target)
|
|
160
|
+
}
|
|
161
|
+
lifecycleSignal?.addEventListener("abort",abortAll,{once:true})
|
|
162
|
+
function start(target,operation,hasData,force=false){const previous=active.get(target)
|
|
163
|
+
if(previous&&!force)
|
|
164
|
+
return
|
|
165
|
+
previous?.controller.abort()
|
|
166
|
+
const id=++sequence
|
|
167
|
+
const controller=new AbortController
|
|
168
|
+
if(lifecycleSignal?.aborted)
|
|
169
|
+
controller.abort(lifecycleSignal.reason)
|
|
170
|
+
const current=state(target)
|
|
171
|
+
active.set(target,{id,controller})
|
|
172
|
+
setStates((values)=>({...values,[target]:{status:hasData?"refreshing":"loading",lastSuccessfulAt:current.lastSuccessfulAt
|
|
173
|
+
}}))
|
|
174
|
+
function isCurrent(){return active.get(target)?.id===id
|
|
175
|
+
}
|
|
176
|
+
function finish(){if(isCurrent())
|
|
177
|
+
active.delete(target)
|
|
178
|
+
}
|
|
179
|
+
return{signal:controller.signal,isCurrent,succeed(){if(!isCurrent())
|
|
180
|
+
return
|
|
181
|
+
setStates((values)=>({...values,[target]:{status:"ready",lastSuccessfulAt:Date.now()}}))
|
|
182
|
+
},fail(cause){if(!isCurrent())
|
|
183
|
+
return
|
|
184
|
+
if(controller.signal.aborted||isAbortError(cause)){setStates((values)=>({...values,[target]:current.lastSuccessfulAt?{status:"ready",lastSuccessfulAt:current.lastSuccessfulAt}:idle
|
|
185
|
+
}))
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
setStates((values)=>({...values,[target]:{status:"error",error:requestError(cause,operation,target),lastSuccessfulAt:current.lastSuccessfulAt
|
|
189
|
+
}}))
|
|
190
|
+
},finish
|
|
191
|
+
}}
|
|
192
|
+
return{state,start,abort,abortAll}}
|
|
193
|
+
function createProviderLimitsController(adapters,selectedModel,signal){const requests=createRequestState(signal)
|
|
194
|
+
const [snapshots,setSnapshots]=createSignal2({})
|
|
195
|
+
const targetKey=(model)=>JSON.stringify(model)
|
|
196
|
+
let activeKey
|
|
197
|
+
let targetAbort=new AbortController
|
|
198
|
+
function current(){const model=selectedModel()
|
|
199
|
+
const adapter=model&&adapters.find((candidate)=>candidate.supports(model))
|
|
200
|
+
const key=model?targetKey(model):undefined
|
|
201
|
+
if(activeKey!==key){untrack(requests.abortAll)
|
|
202
|
+
targetAbort.abort()
|
|
203
|
+
targetAbort=new AbortController
|
|
204
|
+
activeKey=key
|
|
205
|
+
}
|
|
206
|
+
return{model,adapter}}
|
|
207
|
+
async function refresh(force=false){const{model,adapter}=current()
|
|
208
|
+
if(!model||!adapter)
|
|
209
|
+
return
|
|
210
|
+
const key=targetKey(model)
|
|
211
|
+
const request=requests.start(key,"read provider limits",Boolean(snapshots()[key]),force)
|
|
212
|
+
if(!request)
|
|
213
|
+
return
|
|
214
|
+
try{const snapshot=normalizeProviderQuota(await adapter.read(model,request.signal))
|
|
215
|
+
if(!request.isCurrent()||targetKey(current().model??model)!==key)
|
|
216
|
+
return
|
|
217
|
+
setSnapshots((value)=>({...value,[key]:snapshot}))
|
|
218
|
+
request.succeed()
|
|
219
|
+
} catch(error){const status=error?.status??error?.response?.status
|
|
220
|
+
if(status===429&&request.isCurrent()){setSnapshots((value)=>({...value,[key]:{model,providerId:adapter.id,providerName:adapter.name,fetchedAt:Date.now(),availability:"rate_limited",windows:[]
|
|
221
|
+
}}))
|
|
222
|
+
request.succeed()
|
|
223
|
+
} else
|
|
224
|
+
request.fail(error)
|
|
225
|
+
} finally{request.finish()
|
|
226
|
+
}}
|
|
227
|
+
return{current,targetSignal(){current()
|
|
228
|
+
return signal?AbortSignal.any([signal,targetAbort.signal]):targetAbort.signal
|
|
229
|
+
},refresh,snapshot(){const{model,adapter}=current()
|
|
230
|
+
return model&&adapter?snapshots()[targetKey(model)]:undefined
|
|
231
|
+
},state(){const{model,adapter}=current()
|
|
232
|
+
return model&&adapter?requests.state(targetKey(model)):{status:"idle"}},dispose(){targetAbort.abort()
|
|
233
|
+
requests.abortAll()
|
|
234
|
+
for(const adapter of adapters)
|
|
235
|
+
adapter.dispose?.()
|
|
236
|
+
}}}
|
|
237
|
+
function windows(limit,model){return ["primary","secondary"].flatMap((name)=>{const value=limit[name]
|
|
238
|
+
return value?[{id:`${limit.limitId ?? model.modelID}.${name}`,label:limit.limitName?`${limit.limitName} \xB7 ${name}`:name,limitID:limit.limitId??undefined,modelID:model.modelID,unit:"%",used:value.usedPercent,...value.windowDurationMins!==null&&{durationMinutes:value.windowDurationMins},...value.resetsAt!==null&&{resetsAt:value.resetsAt}}]:[]
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
function createCodexQuotaAdapter(client){const adapter={id:"codex",name:"Codex CLI",supports:(model)=>model.providerID==="openai"&&Boolean(model.accountID),async discoverAccount(signal){const account=await client.request("account/read",{refreshToken:false},signal)
|
|
242
|
+
if(account.account?.type!=="chatgpt")
|
|
243
|
+
throw new Error("Sign in to Codex CLI with ChatGPT")
|
|
244
|
+
const usage=await client.request("account/rateLimits/read",{supportsLunaReserve:false,excludeResetCreditDetails:true},signal)
|
|
245
|
+
if(!usage.accountId)
|
|
246
|
+
throw new Error("Codex account identity is unavailable")
|
|
247
|
+
return usage.accountId
|
|
248
|
+
},async read(model,signal){const base={model,providerId:"codex",providerName:"Codex CLI",fetchedAt:Date.now(),windows:[]
|
|
249
|
+
}
|
|
250
|
+
const account=await client.request("account/read",{refreshToken:false},signal)
|
|
251
|
+
if(account.account?.type!=="chatgpt")
|
|
252
|
+
return{...base,availability:"unauthenticated"}
|
|
253
|
+
const usage=await client.request("account/rateLimits/read",{supportsLunaReserve:false,excludeResetCreditDetails:false},signal)
|
|
254
|
+
if(!usage.accountId||usage.accountId!==model.accountID)
|
|
255
|
+
return{...base,availability:"unauthenticated"}
|
|
256
|
+
const candidates=Object.values(usage.rateLimitsByLimitId??{})
|
|
257
|
+
const matched=(candidates.length>0?candidates:[usage.rateLimits]).filter((limit)=>limit.normalModelSlug===model.modelID)
|
|
258
|
+
if(matched.length===0)
|
|
259
|
+
return{...base,accountId:usage.accountId,availability:"unsupported"}
|
|
260
|
+
const creditSummary=usage.rateLimitResetCredits
|
|
261
|
+
return normalizeProviderQuota({...base,accountId:usage.accountId,availability:"ready",ordinaryUsageAllowed:usage.ordinaryUsageAllowed??undefined,windows:matched.flatMap((limit)=>windows(limit,model)),...creditSummary&&{bankedResets:{availableCount:creditSummary.availableCount,...creditSummary.credits&&{credits:creditSummary.credits.map((credit)=>({id:credit.id,type:credit.resetType,status:credit.status,grantedAt:credit.grantedAt,...credit.expiresAt!==null&&{expiresAt:credit.expiresAt},...credit.title&&{title:credit.title},...credit.description&&{description:credit.description}}))
|
|
262
|
+
}}}})
|
|
263
|
+
},async prepareResetCreditConsumption(model,creditID,signal){const snapshot=await adapter.read(model,signal)
|
|
264
|
+
if(snapshot.availability==="unauthenticated"||!snapshot.accountId||snapshot.accountId!==model.accountID){throw new Error("Codex account is unavailable or changed")
|
|
265
|
+
}
|
|
266
|
+
if(snapshot.availability!=="ready"||snapshot.ordinaryUsageAllowed!==false||snapshot.windows.length===0){throw new Error("Selected model has no eligible rate-limit window")
|
|
267
|
+
}
|
|
268
|
+
const credits=snapshot.bankedResets
|
|
269
|
+
if(!credits?.availableCount)
|
|
270
|
+
throw new Error("Codex reset credit is unavailable")
|
|
271
|
+
if(creditID){const credit=credits.credits?.find((item)=>item.id===creditID)
|
|
272
|
+
if(!credit||credit.type!=="codexRateLimits"||credit.status!=="available"||credit.expiresAt!==undefined&&credit.expiresAt<=Math.floor(Date.now() / 1000)){throw new Error("Selected banked reset is no longer available")
|
|
273
|
+
}}
|
|
274
|
+
return snapshot
|
|
275
|
+
},async consumeResetCredit(model,attempt,signal){if(!model.accountID||model.accountID!==attempt.accountID)
|
|
276
|
+
throw new Error("Codex account changed")
|
|
277
|
+
if(attempt.uncertain){const latest=await adapter.read(model,signal)
|
|
278
|
+
if(latest.accountId!==attempt.accountID)
|
|
279
|
+
throw new Error("Codex account changed")
|
|
280
|
+
} else
|
|
281
|
+
await adapter.prepareResetCreditConsumption(model,attempt.creditID,signal)
|
|
282
|
+
signal.throwIfAborted()
|
|
283
|
+
const result=await client.request("account/rateLimitResetCredit/consume",{idempotencyKey:attempt.idempotencyKey,...attempt.creditID&&{creditId:attempt.creditID}},signal)
|
|
284
|
+
return result.outcome
|
|
285
|
+
},dispose:client.dispose
|
|
286
|
+
}
|
|
287
|
+
return adapter
|
|
288
|
+
}
|
|
289
|
+
import {createComponent as _$createComponent} from "@opentui/solid"
|
|
290
|
+
import {createSignal as createSignal3} from "solid-js"
|
|
291
|
+
import {GuardedConfirmation} from "./tui.js"
|
|
292
|
+
function createCodexAccountLink(input){const [busy,setBusy]=createSignal3(false)
|
|
293
|
+
async function link(){const selected=input.model()
|
|
294
|
+
if(busy()||!selected||selected.providerID!=="openai"||!input.adapter.discoverAccount)
|
|
295
|
+
return
|
|
296
|
+
const key=JSON.stringify(selected)
|
|
297
|
+
const current=()=>JSON.stringify(input.model())===key
|
|
298
|
+
setBusy(true)
|
|
299
|
+
try{const accountID=await input.adapter.discoverAccount(input.api.lifecycle?.signal??new AbortController().signal)
|
|
300
|
+
if(!current())
|
|
301
|
+
return
|
|
302
|
+
input.dialogs.open(()=>_$createComponent(GuardedConfirmation,{get api(){return input.api
|
|
303
|
+
},current,title:"Link Codex CLI account",get message(){return `Use Codex CLI account ${accountID} for ${selected.modelID}? Only link accounts you know match this OpenCode provider.`
|
|
304
|
+
},onConfirm:()=>{if(!current())
|
|
305
|
+
return
|
|
306
|
+
input.preferences.setCodexAccountBinding(selected.providerID,selected.modelID,accountID)
|
|
307
|
+
input.dialogs.back()
|
|
308
|
+
}}))
|
|
309
|
+
} catch{if(current())
|
|
310
|
+
input.api.ui.toast({variant:"warning",title:"Codex account",message:"Install a supported Codex CLI and sign in with ChatGPT, then try again."
|
|
311
|
+
})
|
|
312
|
+
} finally{setBusy(false)
|
|
313
|
+
}}
|
|
314
|
+
return{link,busy
|
|
315
|
+
}}
|
|
316
|
+
import {effect as _$effect} from "@opentui/solid"
|
|
317
|
+
import {createTextNode as _$createTextNode} from "@opentui/solid"
|
|
318
|
+
import {insertNode as _$insertNode} from "@opentui/solid"
|
|
319
|
+
import {insert as _$insert} from "@opentui/solid"
|
|
320
|
+
import {memo as _$memo} from "@opentui/solid"
|
|
321
|
+
import {setProp as _$setProp} from "@opentui/solid"
|
|
322
|
+
import {createElement as _$createElement} from "@opentui/solid"
|
|
323
|
+
function QuotaWindowRow(props){const reset=()=>props.window.resetsAt===undefined?"":` \xB7 resets ${new Date(props.window.resetsAt * 1000).toLocaleString()}`
|
|
324
|
+
return(()=>{var _el$=_$createElement("box"),_el$2=_$createElement("text"),_el$3=_$createTextNode(`: `),_el$5=_$createTextNode(` used`),_el$6=_$createElement("text")
|
|
325
|
+
_$insertNode(_el$,_el$2)
|
|
326
|
+
_$insertNode(_el$,_el$6)
|
|
327
|
+
_$insertNode(_el$2,_el$3)
|
|
328
|
+
_$insertNode(_el$2,_el$5)
|
|
329
|
+
_$setProp(_el$2,"wrapMode","none")
|
|
330
|
+
_$setProp(_el$2,"truncate",true)
|
|
331
|
+
_$insert(_el$2,(()=>{var _c$=_$memo(()=>props.window.durationMinutes===undefined)
|
|
332
|
+
return()=>_c$()?props.window.label:`${props.window.durationMinutes} min`
|
|
333
|
+
})(),_el$3)
|
|
334
|
+
_$insert(_el$2,()=>props.window.used,_el$5)
|
|
335
|
+
_$insert(_el$2,(()=>{var _c$2=_$memo(()=>props.window.total===undefined)
|
|
336
|
+
return()=>_c$2()?"":`/${props.window.total}`
|
|
337
|
+
})(),_el$5)
|
|
338
|
+
_$insert(_el$2,()=>props.window.unit,_el$5)
|
|
339
|
+
_$setProp(_el$6,"wrapMode","word")
|
|
340
|
+
_$insert(_el$6,()=>reset().replace(" \xB7 ","")||"Reset time unavailable")
|
|
341
|
+
_$effect((_p$)=>{var _v$=props.api.theme.current.text,_v$2=props.api.theme.current.textMuted
|
|
342
|
+
_v$!==_p$.e&&(_p$.e=_$setProp(_el$2,"fg",_v$,_p$.e))
|
|
343
|
+
_v$2!==_p$.t&&(_p$.t=_$setProp(_el$6,"fg",_v$2,_p$.t))
|
|
344
|
+
return _p$
|
|
345
|
+
},{e:undefined,t:undefined
|
|
346
|
+
})
|
|
347
|
+
return _el$
|
|
348
|
+
})()
|
|
349
|
+
}
|
|
350
|
+
import {createComponent as _$createComponent3} from "@opentui/solid"
|
|
351
|
+
import {createEffect} from "solid-js"
|
|
352
|
+
import {SelectionMenu,useDialogs} from "./tui.js"
|
|
353
|
+
import {createComponent as _$createComponent2} from "@opentui/solid"
|
|
354
|
+
import {randomUUID} from "crypto"
|
|
355
|
+
import {GuardedConfirmation as GuardedConfirmation2,useDialogState} from "./tui.js"
|
|
356
|
+
function createResetCreditActions(input){const [busy,setBusy]=useDialogState("credit-busy",false)
|
|
357
|
+
const signal=input.controller.targetSignal
|
|
358
|
+
const warn=(message)=>input.api.ui.toast({variant:"warning",title:"Codex reset credit",message
|
|
359
|
+
})
|
|
360
|
+
async function consume(creditID){if(busy()||!input.current()||!input.model.accountID||!input.adapter.consumeResetCredit)
|
|
361
|
+
return
|
|
362
|
+
setBusy(true)
|
|
363
|
+
const previous=input.preferences.pendingResetAttempt()
|
|
364
|
+
const attempt=previous??{providerID:input.model.providerID,modelID:input.model.modelID,accountID:input.model.accountID,...creditID&&{creditID
|
|
365
|
+
},idempotencyKey:randomUUID(),createdAt:Date.now()
|
|
366
|
+
}
|
|
367
|
+
try{if(attempt.accountID!==input.model.accountID||attempt.modelID!==input.model.modelID||attempt.providerID!==input.model.providerID){throw new Error("Resolve the earlier attempt on its linked account and model first")
|
|
368
|
+
}
|
|
369
|
+
if(!previous)
|
|
370
|
+
await input.preferences.beginResetAttempt(attempt)
|
|
371
|
+
if(!input.current())
|
|
372
|
+
return
|
|
373
|
+
const outcome=await input.adapter.consumeResetCredit(input.model,{...attempt,uncertain:Boolean(previous)
|
|
374
|
+
},signal())
|
|
375
|
+
await input.preferences.finishResetAttempt(attempt.idempotencyKey)
|
|
376
|
+
await input.controller.refresh(true)
|
|
377
|
+
input.api.ui.toast({variant:outcome==="reset"||outcome==="alreadyRedeemed"?"success":"info",title:"Codex reset credit",message:{reset:"Reset applied. Restart or continue your Codex session.",alreadyRedeemed:"This attempt was already redeemed; no second credit was used.",noCredit:"No reset credit is available.",nothingToReset:"There is no eligible usage window to reset."
|
|
378
|
+
}[outcome]
|
|
379
|
+
})
|
|
380
|
+
if(input.current())
|
|
381
|
+
input.dialogs.close()
|
|
382
|
+
} catch(error){if(input.current()){warn(`${error instanceof Error ? error.message : "Reset request failed"} \xB7 retry keeps the same attempt`)
|
|
383
|
+
input.dialogs.back()
|
|
384
|
+
}} finally{setBusy(false)
|
|
385
|
+
}}
|
|
386
|
+
async function review(creditID){if(busy()||!input.current()||!input.adapter.prepareResetCreditConsumption||!input.adapter.consumeResetCredit)
|
|
387
|
+
return
|
|
388
|
+
setBusy(true)
|
|
389
|
+
try{const snapshot=await input.adapter.prepareResetCreditConsumption(input.model,creditID,signal())
|
|
390
|
+
if(!input.current()||snapshot.accountId!==input.model.accountID)
|
|
391
|
+
return
|
|
392
|
+
input.dialogs.open(()=>_$createComponent2(GuardedConfirmation2,{get api(){return input.api
|
|
393
|
+
},get current(){return input.current
|
|
394
|
+
},title:"Use one banked reset?",get message(){return `Account ${snapshot.accountId} \xB7 model ${input.model.modelID}. This may reset other eligible Codex windows.`
|
|
395
|
+
},onConfirm:()=>void consume(creditID)
|
|
396
|
+
}))
|
|
397
|
+
} catch(error){if(input.current())
|
|
398
|
+
warn(error instanceof Error?error.message:"Credit eligibility is unavailable")
|
|
399
|
+
} finally{setBusy(false)
|
|
400
|
+
}}
|
|
401
|
+
return{review,consume,busy
|
|
402
|
+
}}
|
|
403
|
+
function ResetCreditDialog(props){const dialogs=useDialogs(props.api)
|
|
404
|
+
const key=JSON.stringify(props.model)
|
|
405
|
+
const current=()=>JSON.stringify(props.controller.current().model)===key
|
|
406
|
+
const pending=props.preferences.pendingResetAttempt
|
|
407
|
+
const credits=()=>props.controller.snapshot()?.bankedResets
|
|
408
|
+
const actions=createResetCreditActions({...props,dialogs,current
|
|
409
|
+
})
|
|
410
|
+
createEffect(()=>{if(!current())
|
|
411
|
+
dialogs.close()
|
|
412
|
+
})
|
|
413
|
+
const options=()=>[...credits()?.credits?.map((credit)=>({title:credit.title??`Credit ${credit.id}`,value:`credit:${credit.id}`,icon:"presets",description:[credit.status,credit.description,credit.expiresAt&&`expires ${new Date(credit.expiresAt * 1000).toLocaleString()}`].filter(Boolean).join(" \xB7 ")
|
|
414
|
+
}))??[],...credits()?.availableCount&&(credits()?.credits?.length??0)<credits().availableCount?[{title:"Use next available credit",value:"credit:",icon:"presets",description:"Credit details unavailable"
|
|
415
|
+
}]:[],...pending()?[{title:"Reconcile pending attempt",value:"retry",icon:"retry",description:"Reuse the previous idempotency key"
|
|
416
|
+
}]:[],{title:"Cancel",value:"cancel",icon:"close",description:"No credit is used"
|
|
417
|
+
}]
|
|
418
|
+
return _$createComponent3(SelectionMenu,{get api(){return props.api
|
|
419
|
+
},get title(){return `Banked resets \xB7 ${credits()?.availableCount ?? 0} available${actions.busy() ? " \xB7 pending" : ""}`
|
|
420
|
+
},get options(){return options()
|
|
421
|
+
},get height(){return Math.min(18,options().length*3)
|
|
422
|
+
},onSelect:(option)=>{if(option.value==="cancel")
|
|
423
|
+
dialogs.back()
|
|
424
|
+
else if(option.value==="retry")
|
|
425
|
+
actions.consume(pending()?.creditID)
|
|
426
|
+
else if(option.value.startsWith("credit:"))
|
|
427
|
+
actions.review(option.value.slice("credit:".length)||undefined)
|
|
428
|
+
}})
|
|
429
|
+
}
|
|
430
|
+
import {insert as _$insert5} from "@opentui/solid"
|
|
431
|
+
import {setProp as _$setProp5} from "@opentui/solid"
|
|
432
|
+
import {effect as _$effect5} from "@opentui/solid"
|
|
433
|
+
import {createTextNode as _$createTextNode3} from "@opentui/solid"
|
|
434
|
+
import {insertNode as _$insertNode3} from "@opentui/solid"
|
|
435
|
+
import {createElement as _$createElement5} from "@opentui/solid"
|
|
436
|
+
import {memo as _$memo3} from "@opentui/solid"
|
|
437
|
+
import {createComponent as _$createComponent7} from "@opentui/solid"
|
|
438
|
+
import {For,Show as Show3} from "solid-js"
|
|
439
|
+
import {useDialogs as useDialogs2,useIcons as useIcons2} from "./tui.js"
|
|
440
|
+
import {createComponent as _$createComponent4} from "@opentui/solid"
|
|
441
|
+
import {effect as _$effect2} from "@opentui/solid"
|
|
442
|
+
import {insert as _$insert2} from "@opentui/solid"
|
|
443
|
+
import {setProp as _$setProp2} from "@opentui/solid"
|
|
444
|
+
import {createElement as _$createElement2} from "@opentui/solid"
|
|
445
|
+
import {SelectionBox} from "./tui.js"
|
|
446
|
+
import {createSignal as createSignal4,onCleanup} from "solid-js"
|
|
447
|
+
function sidebarInteractiveColors(theme,state,normalColor=theme.text,mode="row"){const highlighted=!state.disabled&&(state.focused||mode==="control"&&state.hovered)
|
|
448
|
+
let backgroundColor=theme.backgroundPanel
|
|
449
|
+
let foregroundColor=normalColor
|
|
450
|
+
if(highlighted)
|
|
451
|
+
backgroundColor=theme.primary
|
|
452
|
+
else if(mode==="control")
|
|
453
|
+
backgroundColor="transparent"
|
|
454
|
+
else if(state.focused||state.hovered)
|
|
455
|
+
backgroundColor=theme.backgroundElement
|
|
456
|
+
if(state.disabled)
|
|
457
|
+
foregroundColor=theme.textMuted
|
|
458
|
+
else if(highlighted)
|
|
459
|
+
foregroundColor=theme.selectedListItemText
|
|
460
|
+
return{backgroundColor,foregroundColor}}
|
|
461
|
+
function useSidebarItem(api,interaction,descriptor,normalColor=()=>api.theme.current.text,mode="row"){let unregister
|
|
462
|
+
const [hovered,setHovered]=createSignal4(false)
|
|
463
|
+
const disabled=()=>descriptor.disabled?.()===true
|
|
464
|
+
const focused=()=>Boolean(interaction?.ownsFocus()&&interaction.isSelected(descriptor.id))
|
|
465
|
+
const colors=()=>sidebarInteractiveColors(api.theme.current,{focused:focused(),hovered:hovered(),disabled:disabled()},normalColor(),mode)
|
|
466
|
+
const ref=(renderable)=>{unregister?.()
|
|
467
|
+
unregister=interaction?.register({...descriptor,renderable})
|
|
468
|
+
}
|
|
469
|
+
onCleanup(()=>unregister?.())
|
|
470
|
+
return{ref,disabled,focused,backgroundColor:()=>colors().backgroundColor,foregroundColor:()=>colors().foregroundColor,onMouseOver:()=>setHovered(true),onMouseOut:()=>setHovered(false),select(event){interaction?.mouseSelect(descriptor.id,event)
|
|
471
|
+
},activate(event){if(interaction)
|
|
472
|
+
return interaction.mouseActivate(descriptor.id,event)
|
|
473
|
+
if(disabled())
|
|
474
|
+
return false
|
|
475
|
+
descriptor.activate()
|
|
476
|
+
return true
|
|
477
|
+
}}}
|
|
478
|
+
function LimitsAction(props){const item=useSidebarItem(props.api,props.interaction,{id:props.id,position:()=>props.position,disabled:()=>props.disabled===true,activate:props.onActivate
|
|
479
|
+
})
|
|
480
|
+
return _$createComponent4(SelectionBox,{ref:(node)=>item.ref(node),get id(){return props.id
|
|
481
|
+
},height:1,get backgroundColor(){return item.backgroundColor()
|
|
482
|
+
},get onMouseOver(){return item.onMouseOver
|
|
483
|
+
},get onMouseOut(){return item.onMouseOut
|
|
484
|
+
},onMouseUp:(event)=>{event.stopPropagation()
|
|
485
|
+
item.activate(event)
|
|
486
|
+
},get children(){var _el$=_$createElement2("text")
|
|
487
|
+
_$setProp2(_el$,"wrapMode","none")
|
|
488
|
+
_$setProp2(_el$,"truncate",true)
|
|
489
|
+
_$insert2(_el$,()=>props.label)
|
|
490
|
+
_$effect2((_$p)=>_$setProp2(_el$,"fg",item.foregroundColor(),_$p))
|
|
491
|
+
return _el$
|
|
492
|
+
}})
|
|
493
|
+
}
|
|
494
|
+
import {createComponent as _$createComponent5} from "@opentui/solid"
|
|
495
|
+
import {effect as _$effect3} from "@opentui/solid"
|
|
496
|
+
import {insert as _$insert3} from "@opentui/solid"
|
|
497
|
+
import {setProp as _$setProp3} from "@opentui/solid"
|
|
498
|
+
import {createElement as _$createElement3} from "@opentui/solid"
|
|
499
|
+
import {Show} from "solid-js"
|
|
500
|
+
function LimitsStatus(props){const message=()=>{const snapshot=props.controller.snapshot()
|
|
501
|
+
const state=props.controller.state()
|
|
502
|
+
if(state.status==="error")
|
|
503
|
+
return snapshot?"Stale \xB7 refresh failed; retry below":"Quota read failed \xB7 retry below"
|
|
504
|
+
if(!snapshot)
|
|
505
|
+
return state.status==="loading"?"Reading provider limits\u2026":"Refresh to read provider limits"
|
|
506
|
+
const availability=snapshot.availability
|
|
507
|
+
if(availability==="unauthenticated")
|
|
508
|
+
return "Codex sign-in or linked account changed \xB7 relink below"
|
|
509
|
+
if(availability==="unsupported")
|
|
510
|
+
return "No documented quota bucket for this model"
|
|
511
|
+
if(availability==="rate_limited")
|
|
512
|
+
return "Provider rate-limited this read \xB7 try again later"
|
|
513
|
+
if(availability==="stale")
|
|
514
|
+
return "Stale provider data \xB7 refresh below"
|
|
515
|
+
return `Updated ${new Date(snapshot.fetchedAt).toLocaleTimeString()}`
|
|
516
|
+
}
|
|
517
|
+
return _$createComponent5(Show,{get when(){return props.controller.current().adapter
|
|
518
|
+
},get children(){var _el$=_$createElement3("text")
|
|
519
|
+
_$setProp3(_el$,"wrapMode","word")
|
|
520
|
+
_$insert3(_el$,message)
|
|
521
|
+
_$effect3((_$p)=>_$setProp3(_el$,"fg",props.api.theme.current.textMuted,_$p))
|
|
522
|
+
return _el$
|
|
523
|
+
}})
|
|
524
|
+
}
|
|
525
|
+
import {createComponent as _$createComponent6} from "@opentui/solid"
|
|
526
|
+
import {effect as _$effect4} from "@opentui/solid"
|
|
527
|
+
import {createTextNode as _$createTextNode2} from "@opentui/solid"
|
|
528
|
+
import {insertNode as _$insertNode2} from "@opentui/solid"
|
|
529
|
+
import {memo as _$memo2} from "@opentui/solid"
|
|
530
|
+
import {insert as _$insert4} from "@opentui/solid"
|
|
531
|
+
import {setProp as _$setProp4} from "@opentui/solid"
|
|
532
|
+
import {createElement as _$createElement4} from "@opentui/solid"
|
|
533
|
+
import {Show as Show2,createEffect as createEffect2,createSignal as createSignal5} from "solid-js"
|
|
534
|
+
import {SelectionBox as SelectionBox2,useIcons} from "./tui.js"
|
|
535
|
+
var graphemes=new Intl.Segmenter(undefined,{granularity:"grapheme"
|
|
536
|
+
})
|
|
537
|
+
function truncateEnd(value,maxWidth){if(Bun.stringWidth(value)<=maxWidth)
|
|
538
|
+
return value
|
|
539
|
+
if(maxWidth<=0)
|
|
540
|
+
return ""
|
|
541
|
+
const ellipsis=maxWidth>=3?"...":".".repeat(maxWidth)
|
|
542
|
+
const contentWidth=maxWidth-Bun.stringWidth(ellipsis)
|
|
543
|
+
let result=""
|
|
544
|
+
for(const{segment
|
|
545
|
+
} of graphemes.segment(value)){if(Bun.stringWidth(result + segment)>contentWidth)
|
|
546
|
+
break
|
|
547
|
+
result += segment
|
|
548
|
+
}
|
|
549
|
+
return result + ellipsis
|
|
550
|
+
}
|
|
551
|
+
function Section(props){const icons=useIcons()
|
|
552
|
+
const [actionWidth,setActionWidth]=createSignal5()
|
|
553
|
+
const icon=()=>props.section?icons.section(props.section):props.icon
|
|
554
|
+
const theme=()=>props.api.theme.current
|
|
555
|
+
const item=useSidebarItem(props.api,props.interaction,{id:props.sectionId??`section.${props.title}`,position:()=>({section:props.navigationSection??0,row:0,column:0
|
|
556
|
+
}),activate:props.onToggle
|
|
557
|
+
})
|
|
558
|
+
const action=props.headerAction?useSidebarItem(props.api,props.interaction,{id:props.headerAction.id,position:()=>({section:props.navigationSection??0,row:0,column:1
|
|
559
|
+
}),disabled:props.headerAction.disabled,activate:props.headerAction.onActivate
|
|
560
|
+
},()=>theme().accent,"control"):undefined
|
|
561
|
+
createEffect2(()=>{props.headerAction?.label()
|
|
562
|
+
setActionWidth(undefined)
|
|
563
|
+
})
|
|
564
|
+
const actionLabel=()=>{const label=props.headerAction?.label()??""
|
|
565
|
+
const width=actionWidth()
|
|
566
|
+
return width===undefined?label:truncateEnd(label,width-2)
|
|
567
|
+
}
|
|
568
|
+
return(()=>{var _el$=_$createElement4("box")
|
|
569
|
+
_$setProp4(_el$,"gap",1)
|
|
570
|
+
_$insert4(_el$,_$createComponent6(SelectionBox2,{ref:(node)=>item.ref(node),get id(){return props.sectionId
|
|
571
|
+
},flexDirection:"row",height:1,justifyContent:"space-between",gap:1,paddingLeft:1,paddingRight:1,get backgroundColor(){return item.backgroundColor()
|
|
572
|
+
},get onMouseOver(){return item.onMouseOver
|
|
573
|
+
},get onMouseOut(){return item.onMouseOut
|
|
574
|
+
},onMouseDown:(event)=>item.activate(event),onSizeChange:()=>setActionWidth(undefined),get children(){return [(()=>{var _el$2=_$createElement4("text"),_el$3=_$createElement4("span"),_el$4=_$createTextNode2(` `),_el$5=_$createElement4("span"),_el$6=_$createElement4("b")
|
|
575
|
+
_$insertNode2(_el$2,_el$3)
|
|
576
|
+
_$insertNode2(_el$2,_el$4)
|
|
577
|
+
_$insertNode2(_el$2,_el$5)
|
|
578
|
+
_$insertNode2(_el$2,_el$6)
|
|
579
|
+
_$setProp4(_el$2,"flexShrink",0)
|
|
580
|
+
_$setProp4(_el$2,"wrapMode","none")
|
|
581
|
+
_$insert4(_el$3,()=>icons.icon(props.open?"expanded":"collapsed"))
|
|
582
|
+
_$insert4(_el$5,(()=>{var _c$=_$memo2(()=>!!icon())
|
|
583
|
+
return()=>_c$()?`${icon()} `:""
|
|
584
|
+
})())
|
|
585
|
+
_$insert4(_el$6,()=>props.title)
|
|
586
|
+
_$effect4((_p$)=>{var _v$=item.foregroundColor(),_v$2={fg:item.focused()?item.foregroundColor():theme().accent
|
|
587
|
+
},_v$3={fg:item.focused()?item.foregroundColor():theme().accent
|
|
588
|
+
}
|
|
589
|
+
_v$!==_p$.e&&(_p$.e=_$setProp4(_el$2,"fg",_v$,_p$.e))
|
|
590
|
+
_v$2!==_p$.t&&(_p$.t=_$setProp4(_el$3,"style",_v$2,_p$.t))
|
|
591
|
+
_v$3!==_p$.a&&(_p$.a=_$setProp4(_el$5,"style",_v$3,_p$.a))
|
|
592
|
+
return _p$
|
|
593
|
+
},{e:undefined,t:undefined,a:undefined
|
|
594
|
+
})
|
|
595
|
+
return _el$2
|
|
596
|
+
})(),(()=>{var _el$7=_$createElement4("box"),_el$8=_$createElement4("text")
|
|
597
|
+
_$insertNode2(_el$7,_el$8)
|
|
598
|
+
_$setProp4(_el$7,"flexDirection","row")
|
|
599
|
+
_$setProp4(_el$7,"gap",1)
|
|
600
|
+
_$setProp4(_el$7,"flexGrow",1)
|
|
601
|
+
_$setProp4(_el$7,"minWidth",0)
|
|
602
|
+
_$setProp4(_el$7,"justifyContent","flex-end")
|
|
603
|
+
_$insert4(_el$7,_$createComponent6(Show2,{when:action,children:(control)=>_$createComponent6(SelectionBox2,{ref:(node)=>control().ref(node),get id(){return props.headerAction?.id
|
|
604
|
+
},flexShrink:1,minWidth:3,overflow:"hidden",get backgroundColor(){return control().backgroundColor()
|
|
605
|
+
},onSizeChange:function(){setActionWidth(this.width)
|
|
606
|
+
},get onMouseOver(){return control().onMouseOver
|
|
607
|
+
},get onMouseOut(){return control().onMouseOut
|
|
608
|
+
},onMouseDown:(event)=>event.stopPropagation(),onMouseUp:(event)=>{event.stopPropagation()
|
|
609
|
+
control().activate(event)
|
|
610
|
+
},get children(){var _el$9=_$createElement4("text")
|
|
611
|
+
_$setProp4(_el$9,"wrapMode","none")
|
|
612
|
+
_$setProp4(_el$9,"height",1)
|
|
613
|
+
_$insert4(_el$9,actionLabel)
|
|
614
|
+
_$effect4((_$p)=>_$setProp4(_el$9,"fg",control().foregroundColor(),_$p))
|
|
615
|
+
return _el$9
|
|
616
|
+
}})
|
|
617
|
+
}),_el$8)
|
|
618
|
+
_$setProp4(_el$8,"wrapMode","none")
|
|
619
|
+
_$setProp4(_el$8,"truncate",true)
|
|
620
|
+
_$setProp4(_el$8,"height",1)
|
|
621
|
+
_$insert4(_el$8,()=>props.summary)
|
|
622
|
+
_$effect4((_p$)=>{var _v$4=item.focused()?item.foregroundColor():theme().textMuted,_v$5=props.headerAction?0:1
|
|
623
|
+
_v$4!==_p$.e&&(_p$.e=_$setProp4(_el$8,"fg",_v$4,_p$.e))
|
|
624
|
+
_v$5!==_p$.t&&(_p$.t=_$setProp4(_el$8,"flexShrink",_v$5,_p$.t))
|
|
625
|
+
return _p$
|
|
626
|
+
},{e:undefined,t:undefined
|
|
627
|
+
})
|
|
628
|
+
return _el$7
|
|
629
|
+
})()]
|
|
630
|
+
}}),null)
|
|
631
|
+
_$insert4(_el$,_$createComponent6(Show2,{get when(){return props.open
|
|
632
|
+
},get children(){return props.children
|
|
633
|
+
}}),null)
|
|
634
|
+
return _el$
|
|
635
|
+
})()
|
|
636
|
+
}
|
|
637
|
+
function ProviderLimitsSection(props){const icons=useIcons2()
|
|
638
|
+
const dialogs=useDialogs2(props.api)
|
|
639
|
+
const model=()=>props.controller.current().model
|
|
640
|
+
const snapshot=props.controller.snapshot
|
|
641
|
+
const accountLink=createCodexAccountLink({api:props.api,preferences:props.preferences,adapter:props.codex,model,dialogs
|
|
642
|
+
})
|
|
643
|
+
const action=(id,row,label,onActivate,disabled=false)=>_$createComponent7(LimitsAction,{get api(){return props.api
|
|
644
|
+
},get interaction(){return props.interaction
|
|
645
|
+
},id:`${PLUGIN_ID}.limits.${id}`,get position(){return{section:props.navigationSection,row,column:0
|
|
646
|
+
}},label,onActivate,disabled
|
|
647
|
+
})
|
|
648
|
+
function openCredits(){const selected=model()
|
|
649
|
+
if(!selected)
|
|
650
|
+
return
|
|
651
|
+
dialogs.open(()=>_$createComponent7(ResetCreditDialog,{get api(){return props.api
|
|
652
|
+
},get preferences(){return props.preferences
|
|
653
|
+
},get controller(){return props.controller
|
|
654
|
+
},get adapter(){return props.codex
|
|
655
|
+
},model:selected
|
|
656
|
+
}))
|
|
657
|
+
}
|
|
658
|
+
return _$createComponent7(Section,{get api(){return props.api
|
|
659
|
+
},get interaction(){return props.interaction
|
|
660
|
+
},sectionId:`${PLUGIN_ID}.section.limits`,get navigationSection(){return props.navigationSection
|
|
661
|
+
},title:"LIMITS",section:"limits",get summary(){return _$memo3(()=>snapshot()?.availability==="ready")()?String(snapshot()?.windows.length):""
|
|
662
|
+
},get open(){return props.preferences.expanded().limits
|
|
663
|
+
},onToggle:()=>props.preferences.toggleSectionExpanded("limits"),get children(){return _$createComponent7(Show3,{get when(){return model()
|
|
664
|
+
},get fallback(){return(()=>{var _el$=_$createElement5("text")
|
|
665
|
+
_$insertNode3(_el$,_$createTextNode3(`Open a session to view model limits`))
|
|
666
|
+
_$effect5((_$p)=>_$setProp5(_el$,"fg",props.api.theme.current.textMuted,_$p))
|
|
667
|
+
return _el$
|
|
668
|
+
})()
|
|
669
|
+
},children:(selected)=>(()=>{var _el$3=_$createElement5("box"),_el$4=_$createElement5("text"),_el$5=_$createTextNode3(`/`)
|
|
670
|
+
_$insertNode3(_el$3,_el$4)
|
|
671
|
+
_$setProp5(_el$3,"gap",1)
|
|
672
|
+
_$insertNode3(_el$4,_el$5)
|
|
673
|
+
_$setProp5(_el$4,"wrapMode","none")
|
|
674
|
+
_$setProp5(_el$4,"truncate",true)
|
|
675
|
+
_$insert5(_el$4,()=>selected().providerID,_el$5)
|
|
676
|
+
_$insert5(_el$4,()=>selected().modelID,null)
|
|
677
|
+
_$insert5(_el$3,_$createComponent7(Show3,{get when(){return _$memo3(()=>!!!selected().accountID)()&&selected().providerID==="openai"
|
|
678
|
+
},get children(){return [(()=>{var _el$6=_$createElement5("text")
|
|
679
|
+
_$insertNode3(_el$6,_$createTextNode3(`Link the matching Codex account to read quotas`))
|
|
680
|
+
_$effect5((_$p)=>_$setProp5(_el$6,"fg",props.api.theme.current.textMuted,_$p))
|
|
681
|
+
return _el$6
|
|
682
|
+
})(),_$memo3(()=>action("link",1,`${icons.icon("scope")} Link Codex CLI account`,()=>void accountLink.link(),accountLink.busy()))]
|
|
683
|
+
}}),null)
|
|
684
|
+
_$insert5(_el$3,_$createComponent7(Show3,{get when(){return _$memo3(()=>!!selected().accountID)()&&snapshot()?.availability==="ready"
|
|
685
|
+
},get children(){return [(()=>{var _el$8=_$createElement5("text"),_el$9=_$createTextNode3(`Codex CLI \xB7 `)
|
|
686
|
+
_$insertNode3(_el$8,_el$9)
|
|
687
|
+
_$setProp5(_el$8,"wrapMode","none")
|
|
688
|
+
_$setProp5(_el$8,"truncate",true)
|
|
689
|
+
_$insert5(_el$8,()=>snapshot()?.accountId,null)
|
|
690
|
+
_$effect5((_$p)=>_$setProp5(_el$8,"fg",props.api.theme.current.textMuted,_$p))
|
|
691
|
+
return _el$8
|
|
692
|
+
})(),_$createComponent7(For,{get each(){return snapshot()?.windows
|
|
693
|
+
},children:(window)=>_$createComponent7(QuotaWindowRow,{get api(){return props.api
|
|
694
|
+
},window
|
|
695
|
+
})
|
|
696
|
+
}),_$createComponent7(Show3,{get when(){return snapshot()?.bankedResets
|
|
697
|
+
},children:(credits)=>[(()=>{var _el$10=_$createElement5("text"),_el$11=_$createTextNode3(`Banked resets: `)
|
|
698
|
+
_$insertNode3(_el$10,_el$11)
|
|
699
|
+
_$insert5(_el$10,()=>credits().availableCount,null)
|
|
700
|
+
_$effect5((_$p)=>_$setProp5(_el$10,"fg",props.api.theme.current.textMuted,_$p))
|
|
701
|
+
return _el$10
|
|
702
|
+
})(),_$createComponent7(Show3,{get when(){return credits().availableCount>0||props.preferences.pendingResetAttempt()
|
|
703
|
+
},get children(){return action("credits",2,`${icons.icon("info")} Review reset credits`,openCredits)
|
|
704
|
+
}})]
|
|
705
|
+
})]
|
|
706
|
+
}}),null)
|
|
707
|
+
_$insert5(_el$3,_$createComponent7(LimitsStatus,{get api(){return props.api
|
|
708
|
+
},get controller(){return props.controller
|
|
709
|
+
}}),null)
|
|
710
|
+
_$insert5(_el$3,_$createComponent7(Show3,{get when(){return selected().providerID!=="openai"
|
|
711
|
+
},get children(){var _el$0=_$createElement5("text")
|
|
712
|
+
_$insertNode3(_el$0,_$createTextNode3(`No documented quota API for this model`))
|
|
713
|
+
_$effect5((_$p)=>_$setProp5(_el$0,"fg",props.api.theme.current.textMuted,_$p))
|
|
714
|
+
return _el$0
|
|
715
|
+
}}),null)
|
|
716
|
+
_$insert5(_el$3,_$createComponent7(Show3,{get when(){return props.controller.current().adapter
|
|
717
|
+
},get children(){return action("refresh",3,`${icons.icon("retry")} ${props.controller.state().status === "error" ? "Retry" : "Refresh"}`,()=>void props.controller.refresh(true),["loading","refreshing"].includes(props.controller.state().status))
|
|
718
|
+
}}),null)
|
|
719
|
+
_$insert5(_el$3,_$createComponent7(Show3,{get when(){return _$memo3(()=>selected().providerID==="openai")()&&selected().accountID
|
|
720
|
+
},get children(){return [_$memo3(()=>action("unlink",4,`${icons.icon("close")} Unlink Codex account`,()=>props.preferences.setCodexAccountBinding(selected().providerID,selected().modelID))),_$createComponent7(Show3,{get when(){return snapshot()?.availability==="unauthenticated"
|
|
721
|
+
},get children(){return action("relink",5,`${icons.icon("scope")} Relink Codex account`,()=>void accountLink.link(),accountLink.busy())
|
|
722
|
+
}})]
|
|
723
|
+
}}),null)
|
|
724
|
+
_$effect5((_$p)=>_$setProp5(_el$4,"fg",props.api.theme.current.textMuted,_$p))
|
|
725
|
+
return _el$3
|
|
726
|
+
})()
|
|
727
|
+
})
|
|
728
|
+
}})
|
|
729
|
+
}
|
|
730
|
+
function selectedV1Model(api){const route=api.route.current
|
|
731
|
+
const sessionID="params" in route?route.params?.sessionID:undefined
|
|
732
|
+
if(route.name!=="session"||typeof sessionID!=="string")
|
|
733
|
+
return
|
|
734
|
+
const model=api.state.session.get(sessionID)?.model
|
|
735
|
+
if(!model)
|
|
736
|
+
return
|
|
737
|
+
return{sessionID,providerID:model.providerID,modelID:model.id,...model.variant&&{variant:model.variant}}}
|
|
738
|
+
export{ProviderLimitsSection,createCodexAppServerClient,createCodexQuotaAdapter,createProviderLimitsController,selectedV1Model
|
|
739
|
+
}
|