@cortexkit/opencode-anthropic-auth 1.9.2 → 1.9.4
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/README.md +2 -1
- package/dist/cli.js +1 -1
- package/dist/index-9yjbmhf1.js +2 -0
- package/dist/index-agh52sak.js +2 -0
- package/dist/index-fvzta6xw.js +410 -0
- package/dist/index.js +8 -8
- package/dist/sidebar-state.d.ts +9 -0
- package/dist/sidebar-state.js +1 -1
- package/dist/tui-preferences.d.ts +41 -0
- package/dist/tui-preferences.js +6 -0
- package/package.json +11 -5
- package/src/sidebar-state.ts +54 -0
- package/src/tui-preferences.ts +331 -0
- package/src/tui.tsx +300 -78
- package/dist/index-99mvcpj7.js +0 -2
- package/dist/index-hjkkewsy.js +0 -409
package/src/tui.tsx
CHANGED
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
3
6
|
import type {
|
|
4
7
|
TuiPlugin,
|
|
5
8
|
TuiPluginApi,
|
|
6
9
|
TuiPluginModule,
|
|
7
10
|
} from '@opencode-ai/plugin/tui'
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
createEffect,
|
|
13
|
+
createSignal,
|
|
14
|
+
For,
|
|
15
|
+
type JSX,
|
|
16
|
+
onCleanup,
|
|
17
|
+
Show,
|
|
18
|
+
} from 'solid-js'
|
|
12
19
|
|
|
13
20
|
import {
|
|
14
21
|
type AccountQuota,
|
|
22
|
+
computeQuotaPacing,
|
|
15
23
|
DEFAULT_SIDEBAR_STATE,
|
|
16
|
-
|
|
24
|
+
FIVE_HOUR_MS,
|
|
17
25
|
getCollapsedQuotaSummary,
|
|
18
26
|
getSidebarState,
|
|
27
|
+
type QuotaPacing,
|
|
19
28
|
resolveActiveAccount,
|
|
29
|
+
SEVEN_DAY_MS,
|
|
30
|
+
type SidebarState,
|
|
20
31
|
} from './sidebar-state.js'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
import {
|
|
33
|
+
type AnthropicAuthTuiPrefs,
|
|
34
|
+
type AppearancePrefs,
|
|
35
|
+
computeEffectiveOrder,
|
|
36
|
+
DEFAULT_SLOT_ORDER,
|
|
37
|
+
PLUGIN_KEY,
|
|
38
|
+
queueTuiPreferenceUpdate,
|
|
39
|
+
readTuiPreferencesFile,
|
|
40
|
+
resolveAnthropicAuthPrefs,
|
|
41
|
+
watchTuiPreferences,
|
|
42
|
+
} from './tui-preferences.js'
|
|
24
43
|
|
|
25
44
|
const ID = 'cortexkit.anthropic-auth'
|
|
26
|
-
const BAR_WIDTH = 10
|
|
27
|
-
const BAR_FILLED = '\u2588'
|
|
28
|
-
const BAR_EMPTY = '\u2591'
|
|
29
45
|
|
|
30
46
|
// Plugin version for the header (mirrors the Magic Context / AFT convention).
|
|
31
47
|
// Read at runtime from package.json relative to this module — NOT a TS JSON
|
|
@@ -70,18 +86,61 @@ function toneColor(theme: ThemeCurrent, tone: Tone): ThemeColor {
|
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
88
|
|
|
73
|
-
function usageTone(usedPct: number): Tone {
|
|
74
|
-
if (usedPct <
|
|
75
|
-
if (usedPct <
|
|
89
|
+
function usageTone(usedPct: number, appearance: AppearancePrefs): Tone {
|
|
90
|
+
if (usedPct < appearance.warnThreshold) return 'ok'
|
|
91
|
+
if (usedPct < appearance.errorThreshold) return 'warn'
|
|
76
92
|
return 'err'
|
|
77
93
|
}
|
|
78
94
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
interface BarSegment {
|
|
96
|
+
text: string
|
|
97
|
+
tone: Tone
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const PACE_RESERVE_CHAR = '\u2592'
|
|
101
|
+
const PACE_DEFICIT_CHAR = '\u2593'
|
|
102
|
+
|
|
103
|
+
// Variant C pacing bar: normal fill up to min(used, pace), then a pace
|
|
104
|
+
// segment covering the gap — headroom being banked (reserve, ok tone) or the
|
|
105
|
+
// overshoot itself (deficit, err tone) — then empty cells. Without pacing the
|
|
106
|
+
// bar renders as a single fill+empty pair, identical to the pre-pacing look.
|
|
107
|
+
// Empty text nodes still occupy a phantom cell in the opentui flex row, so
|
|
108
|
+
// zero-length segments are dropped from every return path (plain and pacing).
|
|
109
|
+
function quotaBarSegments(
|
|
110
|
+
usedPct: number,
|
|
111
|
+
appearance: AppearancePrefs,
|
|
112
|
+
pacing: QuotaPacing | null,
|
|
113
|
+
): BarSegment[] {
|
|
114
|
+
const width = appearance.barWidth
|
|
115
|
+
const cells = (pct: number) =>
|
|
116
|
+
Math.max(0, Math.min(Math.round((pct / 100) * width), width))
|
|
117
|
+
const usedCells = cells(usedPct)
|
|
118
|
+
const fillTone = usageTone(usedPct, appearance)
|
|
119
|
+
const plain: BarSegment[] = [
|
|
120
|
+
{ text: appearance.barFilledChar.repeat(usedCells), tone: fillTone },
|
|
121
|
+
{
|
|
122
|
+
text: appearance.barEmptyChar.repeat(width - usedCells),
|
|
123
|
+
tone: fillTone,
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
if (!pacing) return plain.filter((segment) => segment.text.length > 0)
|
|
127
|
+
const paceCells = cells(pacing.pacePercent)
|
|
128
|
+
const lo = Math.min(usedCells, paceCells)
|
|
129
|
+
const hi = Math.max(usedCells, paceCells)
|
|
130
|
+
if (hi === lo) return plain.filter((segment) => segment.text.length > 0)
|
|
131
|
+
const overspent = usedCells > paceCells
|
|
132
|
+
return (
|
|
133
|
+
[
|
|
134
|
+
{ text: appearance.barFilledChar.repeat(lo), tone: fillTone },
|
|
135
|
+
{
|
|
136
|
+
text: (overspent ? PACE_DEFICIT_CHAR : PACE_RESERVE_CHAR).repeat(
|
|
137
|
+
hi - lo,
|
|
138
|
+
),
|
|
139
|
+
tone: overspent ? 'err' : 'ok',
|
|
140
|
+
},
|
|
141
|
+
{ text: appearance.barEmptyChar.repeat(width - hi), tone: fillTone },
|
|
142
|
+
] as BarSegment[]
|
|
143
|
+
).filter((segment) => segment.text.length > 0)
|
|
85
144
|
}
|
|
86
145
|
|
|
87
146
|
function formatResetIn(resetsAt: string | undefined): string {
|
|
@@ -91,8 +150,13 @@ function formatResetIn(resetsAt: string | undefined): string {
|
|
|
91
150
|
const mins = Math.floor(ms / 60_000)
|
|
92
151
|
if (mins < 60) return `${mins}m`
|
|
93
152
|
const hrs = Math.floor(mins / 60)
|
|
94
|
-
|
|
95
|
-
|
|
153
|
+
if (hrs < 24) {
|
|
154
|
+
const rm = mins % 60
|
|
155
|
+
return rm > 0 ? `${hrs}h${rm}m` : `${hrs}h`
|
|
156
|
+
}
|
|
157
|
+
const days = Math.floor(hrs / 24)
|
|
158
|
+
const rh = hrs % 24
|
|
159
|
+
return rh > 0 ? `${days}d${rh}h` : `${days}d`
|
|
96
160
|
}
|
|
97
161
|
|
|
98
162
|
function formatUntil(until: number | undefined): string {
|
|
@@ -154,14 +218,27 @@ function CollapsedRow(props: {
|
|
|
154
218
|
}
|
|
155
219
|
|
|
156
220
|
// Quota window row: muted label left, tone-colored bar + percentage right,
|
|
157
|
-
// with an optional muted reset suffix.
|
|
221
|
+
// with an optional muted reset suffix. When pacing data is present, the bar
|
|
222
|
+
// gains a pace segment and an off-pace window adds a muted subline with the
|
|
223
|
+
// reserve/deficit delta and the projected runout.
|
|
158
224
|
function QuotaRow(props: {
|
|
159
225
|
theme: ThemeCurrent
|
|
226
|
+
appearance: AppearancePrefs
|
|
160
227
|
label: string
|
|
161
228
|
window: { usedPercent: number; resetsAt?: string } | undefined
|
|
229
|
+
pacing: QuotaPacing | null
|
|
162
230
|
}) {
|
|
163
231
|
const used = () => props.window?.usedPercent ?? 0
|
|
164
232
|
const reset = () => formatResetIn(props.window?.resetsAt)
|
|
233
|
+
const paceLine = () => {
|
|
234
|
+
const pacing = props.pacing
|
|
235
|
+
if (!pacing || pacing.state === 'on-pace') return null
|
|
236
|
+
const pct = Math.round(Math.abs(pacing.deltaPercent))
|
|
237
|
+
if (pacing.state === 'reserve') return `reserve ${pct}% \u00b7 lasts`
|
|
238
|
+
return pacing.runsOutAt
|
|
239
|
+
? `deficit ${pct}% \u00b7 out in ${formatResetIn(pacing.runsOutAt)}`
|
|
240
|
+
: `deficit ${pct}% \u00b7 lasts`
|
|
241
|
+
}
|
|
165
242
|
return (
|
|
166
243
|
<Show
|
|
167
244
|
when={props.window}
|
|
@@ -178,10 +255,16 @@ function QuotaRow(props: {
|
|
|
178
255
|
<box width='100%' flexDirection='row' justifyContent='space-between'>
|
|
179
256
|
<box flexDirection='row'>
|
|
180
257
|
<text fg={props.theme.textMuted}>{props.label.padEnd(3)}</text>
|
|
181
|
-
<
|
|
182
|
-
{
|
|
183
|
-
|
|
184
|
-
|
|
258
|
+
<For each={quotaBarSegments(used(), props.appearance, props.pacing)}>
|
|
259
|
+
{(segment) => (
|
|
260
|
+
<text fg={toneColor(props.theme, segment.tone)}>
|
|
261
|
+
{segment.text}
|
|
262
|
+
</text>
|
|
263
|
+
)}
|
|
264
|
+
</For>
|
|
265
|
+
<text
|
|
266
|
+
fg={toneColor(props.theme, usageTone(used(), props.appearance))}
|
|
267
|
+
>
|
|
185
268
|
{` ${String(Math.round(used())).padStart(3)}%`}
|
|
186
269
|
</text>
|
|
187
270
|
</box>
|
|
@@ -189,20 +272,46 @@ function QuotaRow(props: {
|
|
|
189
272
|
<text fg={props.theme.textMuted}>{reset()}</text>
|
|
190
273
|
</Show>
|
|
191
274
|
</box>
|
|
275
|
+
<Show when={paceLine()}>
|
|
276
|
+
<box width='100%' flexDirection='row'>
|
|
277
|
+
<text fg={props.theme.textMuted}>{' '}</text>
|
|
278
|
+
<text
|
|
279
|
+
fg={toneColor(
|
|
280
|
+
props.theme,
|
|
281
|
+
props.pacing?.state === 'deficit' ? 'warn' : 'muted',
|
|
282
|
+
)}
|
|
283
|
+
>
|
|
284
|
+
{paceLine()}
|
|
285
|
+
</text>
|
|
286
|
+
</box>
|
|
287
|
+
</Show>
|
|
192
288
|
</Show>
|
|
193
289
|
)
|
|
194
290
|
}
|
|
195
291
|
|
|
196
292
|
// Account block: header row (name + status word) then per-window quota rows.
|
|
293
|
+
// Pacing is computed here — per window, against the wall clock at render time
|
|
294
|
+
// (re-evaluated on every state poll) — and disabled by passing false.
|
|
197
295
|
function AccountBlock(props: {
|
|
198
296
|
theme: ThemeCurrent
|
|
297
|
+
appearance: AppearancePrefs
|
|
199
298
|
name: string
|
|
200
299
|
quota: AccountQuota | null
|
|
201
300
|
active: boolean
|
|
301
|
+
pacingEnabled: boolean
|
|
202
302
|
marginTop?: number
|
|
203
303
|
}) {
|
|
204
304
|
const statusWord = () => (props.active ? 'active' : 'idle')
|
|
205
305
|
const statusTone = (): Tone => (props.active ? 'ok' : 'muted')
|
|
306
|
+
const pacingFor = (
|
|
307
|
+
window:
|
|
308
|
+
| { usedPercent: number; remainingPercent: number; resetsAt?: string }
|
|
309
|
+
| undefined,
|
|
310
|
+
windowMs: number,
|
|
311
|
+
) =>
|
|
312
|
+
props.pacingEnabled && window
|
|
313
|
+
? computeQuotaPacing(window, windowMs, Date.now())
|
|
314
|
+
: null
|
|
206
315
|
return (
|
|
207
316
|
<box width='100%' flexDirection='column' marginTop={props.marginTop ?? 0}>
|
|
208
317
|
<box width='100%' flexDirection='row' justifyContent='space-between'>
|
|
@@ -219,13 +328,17 @@ function AccountBlock(props: {
|
|
|
219
328
|
>
|
|
220
329
|
<QuotaRow
|
|
221
330
|
theme={props.theme}
|
|
331
|
+
appearance={props.appearance}
|
|
222
332
|
label='5h'
|
|
223
333
|
window={props.quota?.five_hour}
|
|
334
|
+
pacing={pacingFor(props.quota?.five_hour, FIVE_HOUR_MS)}
|
|
224
335
|
/>
|
|
225
336
|
<QuotaRow
|
|
226
337
|
theme={props.theme}
|
|
338
|
+
appearance={props.appearance}
|
|
227
339
|
label='7d'
|
|
228
340
|
window={props.quota?.seven_day}
|
|
341
|
+
pacing={pacingFor(props.quota?.seven_day, SEVEN_DAY_MS)}
|
|
229
342
|
/>
|
|
230
343
|
</Show>
|
|
231
344
|
</box>
|
|
@@ -238,9 +351,77 @@ async function readStateFromFile(): Promise<SidebarState> {
|
|
|
238
351
|
return getSidebarState()
|
|
239
352
|
}
|
|
240
353
|
|
|
241
|
-
|
|
354
|
+
interface SidebarController {
|
|
355
|
+
prefs: () => AnthropicAuthTuiPrefs
|
|
356
|
+
collapsed: () => boolean
|
|
357
|
+
toggleCollapsed: () => void
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// The TUI may unmount and remount sidebar_content when the user switches
|
|
361
|
+
// views (e.g. main -> subagent -> main). A remount re-runs the component
|
|
362
|
+
// body, so any signal created inside the component would reset to its
|
|
363
|
+
// seed. The controller lives in the plugin closure (process lifetime)
|
|
364
|
+
// and owns the durable prefs/collapse signals plus the single shared
|
|
365
|
+
// watcher subscription, so collapse and live pref reloads survive the
|
|
366
|
+
// remount. No effects or memos here — those need a Solid owner, so the
|
|
367
|
+
// poll-interval createEffect stays inside the component.
|
|
368
|
+
function createSidebarController(
|
|
369
|
+
initialPrefs: AnthropicAuthTuiPrefs,
|
|
370
|
+
): SidebarController {
|
|
371
|
+
const [prefs, setPrefs] = createSignal<AnthropicAuthTuiPrefs>(initialPrefs)
|
|
372
|
+
const seedCollapsed =
|
|
373
|
+
initialPrefs.rememberCollapsed && initialPrefs.collapsed != null
|
|
374
|
+
? initialPrefs.collapsed
|
|
375
|
+
: initialPrefs.startCollapsed
|
|
376
|
+
const [collapsed, setCollapsed] = createSignal(seedCollapsed)
|
|
377
|
+
let lastPersistedCollapsed: boolean | null = initialPrefs.collapsed
|
|
378
|
+
let lastApplied = JSON.stringify(initialPrefs)
|
|
379
|
+
|
|
380
|
+
// The watcher lives for the plugin/process lifetime — it is intentionally
|
|
381
|
+
// never disposed. Collapse guard mirrors the race-fix in toggleCollapsed:
|
|
382
|
+
// lastPersistedCollapsed is advanced only once our own write lands, so
|
|
383
|
+
// watcher echoes of the previous persisted value are rejected by the
|
|
384
|
+
// `!==` check and cannot revert a user click.
|
|
385
|
+
watchTuiPreferences(() => {
|
|
386
|
+
void (async () => {
|
|
387
|
+
const next = resolveAnthropicAuthPrefs(await readTuiPreferencesFile())
|
|
388
|
+
const serialized = JSON.stringify(next)
|
|
389
|
+
if (serialized === lastApplied) return
|
|
390
|
+
lastApplied = serialized
|
|
391
|
+
setPrefs(next)
|
|
392
|
+
if (
|
|
393
|
+
next.rememberCollapsed &&
|
|
394
|
+
next.collapsed != null &&
|
|
395
|
+
next.collapsed !== lastPersistedCollapsed
|
|
396
|
+
) {
|
|
397
|
+
lastPersistedCollapsed = next.collapsed
|
|
398
|
+
setCollapsed(next.collapsed)
|
|
399
|
+
}
|
|
400
|
+
})()
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
function toggleCollapsed() {
|
|
404
|
+
const next = !collapsed()
|
|
405
|
+
setCollapsed(next)
|
|
406
|
+
if (prefs().rememberCollapsed) {
|
|
407
|
+
void queueTuiPreferenceUpdate(PLUGIN_KEY, ['collapsed'], next).then(
|
|
408
|
+
() => {
|
|
409
|
+
lastPersistedCollapsed = next
|
|
410
|
+
},
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return { prefs, collapsed, toggleCollapsed }
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function QuotaSidebar(props: {
|
|
419
|
+
api: TuiPluginApi
|
|
420
|
+
controller: SidebarController
|
|
421
|
+
}) {
|
|
422
|
+
const prefs = props.controller.prefs
|
|
423
|
+
const collapsed = props.controller.collapsed
|
|
242
424
|
const [state, setState] = createSignal<SidebarState>(DEFAULT_SIDEBAR_STATE)
|
|
243
|
-
const [collapsed, setCollapsed] = createSignal(false)
|
|
244
425
|
let lastUpdated = 0
|
|
245
426
|
let debounce: ReturnType<typeof setTimeout> | null = null
|
|
246
427
|
|
|
@@ -257,19 +438,23 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
257
438
|
debounce = setTimeout(() => {
|
|
258
439
|
debounce = null
|
|
259
440
|
void refresh()
|
|
260
|
-
},
|
|
441
|
+
}, prefs().refreshDebounceMs)
|
|
261
442
|
}
|
|
262
443
|
|
|
263
444
|
// Background poller (server and TUI run as separate module instances, so we
|
|
264
445
|
// sync through the state file) plus event-driven refreshes for low latency.
|
|
265
|
-
|
|
446
|
+
// The interval rebuilds whenever pollMs changes.
|
|
447
|
+
createEffect(() => {
|
|
448
|
+
const timer = setInterval(refresh, prefs().pollMs)
|
|
449
|
+
onCleanup(() => clearInterval(timer))
|
|
450
|
+
})
|
|
266
451
|
const unsubs = [
|
|
267
452
|
props.api.event.on('session.updated', scheduleRefresh),
|
|
268
453
|
props.api.event.on('message.updated', scheduleRefresh),
|
|
269
454
|
]
|
|
270
455
|
setTimeout(refresh, 300)
|
|
456
|
+
|
|
271
457
|
onCleanup(() => {
|
|
272
|
-
clearInterval(timer)
|
|
273
458
|
if (debounce) clearTimeout(debounce)
|
|
274
459
|
for (const u of unsubs) u()
|
|
275
460
|
})
|
|
@@ -279,18 +464,42 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
279
464
|
const hasData = () =>
|
|
280
465
|
state().main.quota != null || enabledFallbacks().length > 0
|
|
281
466
|
|
|
282
|
-
const headerLabel = () =>
|
|
283
|
-
|
|
467
|
+
const headerLabel = () => {
|
|
468
|
+
const name = prefs().header.label
|
|
469
|
+
return !hasData() ? name : collapsed() ? `\u25b6 ${name}` : `\u25bc ${name}`
|
|
470
|
+
}
|
|
284
471
|
const activeAccount = () => resolveActiveAccount(state())
|
|
285
472
|
const activeQuotaSummary = () =>
|
|
286
473
|
getCollapsedQuotaSummary(activeAccount().quota)
|
|
474
|
+
const activePacingDeficit = () => {
|
|
475
|
+
if (!prefs().sections.pacing) return false
|
|
476
|
+
const quota = activeAccount().quota
|
|
477
|
+
if (!quota) return false
|
|
478
|
+
const now = Date.now()
|
|
479
|
+
const windows: Array<[typeof quota.five_hour, number]> = [
|
|
480
|
+
[quota.five_hour, FIVE_HOUR_MS],
|
|
481
|
+
[quota.seven_day, SEVEN_DAY_MS],
|
|
482
|
+
]
|
|
483
|
+
return windows.some(
|
|
484
|
+
([w, ms]) =>
|
|
485
|
+
w != null && computeQuotaPacing(w, ms, now)?.state === 'deficit',
|
|
486
|
+
)
|
|
487
|
+
}
|
|
287
488
|
const activeQuotaTone = (): Tone => {
|
|
288
489
|
const summary = activeQuotaSummary()
|
|
289
490
|
const values = [
|
|
290
491
|
summary.fiveHourUsedPercent,
|
|
291
492
|
summary.sevenDayUsedPercent,
|
|
292
493
|
].filter((value): value is number => value != null)
|
|
293
|
-
|
|
494
|
+
// Pacing deficit is an advisory projection, not actual quota exhaustion,
|
|
495
|
+
// so it can only BUMP the usage tone up to warn at most — never soften a
|
|
496
|
+
// real warn/err usage reading and never paint a true red.
|
|
497
|
+
const base: Tone =
|
|
498
|
+
values.length > 0
|
|
499
|
+
? usageTone(Math.max(...values), prefs().appearance)
|
|
500
|
+
: 'muted'
|
|
501
|
+
if (!activePacingDeficit()) return base
|
|
502
|
+
return base === 'ok' || base === 'muted' ? 'warn' : base
|
|
294
503
|
}
|
|
295
504
|
|
|
296
505
|
const quotaBackedOff = () => state().main.quotaBackedOff === true
|
|
@@ -298,7 +507,8 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
298
507
|
const degraded = () => quotaBackedOff() || refreshBackedOff()
|
|
299
508
|
|
|
300
509
|
const cacheKeep = () => state().cacheKeep
|
|
301
|
-
const showCache = () =>
|
|
510
|
+
const showCache = () =>
|
|
511
|
+
prefs().sections.cache && cacheKeep() != null && cacheKeep()?.window != null
|
|
302
512
|
const relayValue = () => {
|
|
303
513
|
const r = state().relay
|
|
304
514
|
if (!r) return '\u2014'
|
|
@@ -324,7 +534,7 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
324
534
|
justifyContent='space-between'
|
|
325
535
|
alignItems='center'
|
|
326
536
|
onMouseDown={() => {
|
|
327
|
-
if (hasData())
|
|
537
|
+
if (hasData()) props.controller.toggleCollapsed()
|
|
328
538
|
}}
|
|
329
539
|
>
|
|
330
540
|
<box paddingLeft={1} paddingRight={1} backgroundColor={theme().accent}>
|
|
@@ -335,7 +545,7 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
335
545
|
<Show
|
|
336
546
|
when={degraded()}
|
|
337
547
|
fallback={
|
|
338
|
-
<Show when={PLUGIN_VERSION !== ''}>
|
|
548
|
+
<Show when={prefs().header.showVersion && PLUGIN_VERSION !== ''}>
|
|
339
549
|
<text fg={theme().textMuted}>{`v${PLUGIN_VERSION}`}</text>
|
|
340
550
|
</Show>
|
|
341
551
|
}
|
|
@@ -385,46 +595,56 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
385
595
|
}
|
|
386
596
|
>
|
|
387
597
|
{/* Quota */}
|
|
388
|
-
<
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
598
|
+
<Show when={prefs().sections.quota}>
|
|
599
|
+
<SectionHeader theme={theme()} title='Quota' />
|
|
600
|
+
<AccountBlock
|
|
601
|
+
theme={theme()}
|
|
602
|
+
appearance={prefs().appearance}
|
|
603
|
+
name='main'
|
|
604
|
+
quota={state().main.quota}
|
|
605
|
+
active={state().activeId === 'main'}
|
|
606
|
+
pacingEnabled={prefs().sections.pacing}
|
|
607
|
+
/>
|
|
608
|
+
<Show when={prefs().sections.fallbackAccounts}>
|
|
609
|
+
<For each={enabledFallbacks()}>
|
|
610
|
+
{(fb) => (
|
|
611
|
+
<AccountBlock
|
|
612
|
+
theme={theme()}
|
|
613
|
+
appearance={prefs().appearance}
|
|
614
|
+
name={fb.label ?? fb.id}
|
|
615
|
+
quota={fb.quota}
|
|
616
|
+
active={state().activeId === fb.id}
|
|
617
|
+
pacingEnabled={prefs().sections.pacing}
|
|
618
|
+
marginTop={1}
|
|
619
|
+
/>
|
|
620
|
+
)}
|
|
621
|
+
</For>
|
|
622
|
+
</Show>
|
|
623
|
+
</Show>
|
|
406
624
|
</Show>
|
|
407
625
|
|
|
408
626
|
{/* Routing */}
|
|
409
|
-
<
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
627
|
+
<Show when={prefs().sections.routing}>
|
|
628
|
+
<SectionHeader theme={theme()} title='Routing' />
|
|
629
|
+
<StatRow
|
|
630
|
+
theme={theme()}
|
|
631
|
+
label='Route'
|
|
632
|
+
value={state().route}
|
|
633
|
+
tone='accent'
|
|
634
|
+
/>
|
|
635
|
+
<StatRow
|
|
636
|
+
theme={theme()}
|
|
637
|
+
label='Mode'
|
|
638
|
+
value={state().fastMode ? 'fast' : 'std'}
|
|
639
|
+
tone={state().fastMode ? 'accent' : 'muted'}
|
|
640
|
+
/>
|
|
641
|
+
<StatRow
|
|
642
|
+
theme={theme()}
|
|
643
|
+
label='Relay'
|
|
644
|
+
value={relayValue()}
|
|
645
|
+
tone={state().relay?.enabled ? 'ok' : 'muted'}
|
|
646
|
+
/>
|
|
647
|
+
</Show>
|
|
428
648
|
|
|
429
649
|
{/* Cache */}
|
|
430
650
|
<Show when={showCache()}>
|
|
@@ -446,7 +666,7 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
446
666
|
</Show>
|
|
447
667
|
|
|
448
668
|
{/* Health — only when something is wrong */}
|
|
449
|
-
<Show when={degraded()}>
|
|
669
|
+
<Show when={degraded() && prefs().sections.health}>
|
|
450
670
|
<SectionHeader theme={theme()} title='Health' />
|
|
451
671
|
<Show when={quotaBackedOff()}>
|
|
452
672
|
<StatRow
|
|
@@ -471,11 +691,13 @@ function QuotaSidebar(props: { api: TuiPluginApi }) {
|
|
|
471
691
|
}
|
|
472
692
|
|
|
473
693
|
const tui: TuiPlugin = async (api) => {
|
|
694
|
+
const root = await readTuiPreferencesFile()
|
|
695
|
+
const controller = createSidebarController(resolveAnthropicAuthPrefs(root))
|
|
474
696
|
api.slots.register({
|
|
475
|
-
order:
|
|
697
|
+
order: computeEffectiveOrder(root, PLUGIN_KEY, DEFAULT_SLOT_ORDER),
|
|
476
698
|
slots: {
|
|
477
699
|
sidebar_content(_ctx: unknown, _props: { session_id: string }) {
|
|
478
|
-
return <QuotaSidebar api={api} />
|
|
700
|
+
return <QuotaSidebar api={api} controller={controller} />
|
|
479
701
|
},
|
|
480
702
|
},
|
|
481
703
|
})
|
package/dist/index-99mvcpj7.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{mkdir as O,readFile as Q,writeFile as W}from"node:fs/promises";import{tmpdir as X}from"node:os";import{dirname as Y,join as K}from"node:path";var Z="OPENCODE_ANTHROPIC_AUTH_SIDEBAR_STATE_FILE",$=K(X(),"opencode-anthropic-auth"),x=K($,"sidebar-state.json");function M(){return process.env[Z]||x}var B={main:{quota:null},fallbacks:[],activeId:void 0,route:"main",relay:null,fastMode:!1,lastUpdated:0};async function h(){try{let G=await Q(M(),"utf8");return JSON.parse(G)}catch{return B}}async function j(G){try{let z=M();await O(Y(z),{recursive:!0}),await W(z,JSON.stringify(G),"utf8")}catch{}}function w(G){let z=G.activeId;if(z&&z!=="main"){let C=G.fallbacks.find((J)=>J.enabled&&J.id===z);if(C)return{id:C.id,name:C.label??C.id,quota:C.quota}}return{id:"main",name:"main",quota:G.main.quota}}function R(G){let z=G?.five_hour?.usedPercent??null,C=G?.seven_day?.usedPercent??null;if(z==null&&C==null)return{fiveHourUsedPercent:z,sevenDayUsedPercent:C,text:null};return{fiveHourUsedPercent:z,sevenDayUsedPercent:C,text:`5h: ${z==null?"—":`${Math.round(z)}%`} 7d: ${C==null?"—":`${Math.round(C)}%`}`}}
|
|
2
|
-
export{M as Sa,B as Ta,h as Ua,j as Va,w as Wa,R as Xa};
|