@cortexkit/opencode-openai-auth 0.11.1 → 1.0.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/README.md +15 -26
- package/dist/index-766sfrh5.js +1 -0
- package/dist/index-8kj7sa1p.js +12 -0
- package/dist/index-8rqfbhfw.js +221 -0
- package/dist/index-b3q3j39x.js +2 -0
- package/dist/index-bmd7x5aq.js +2 -0
- package/dist/index-eqh53k8v.js +2 -0
- package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
- package/dist/index-jyr8y8k4.js +7 -0
- package/dist/index-rv08jmd4.js +2 -0
- package/dist/index.d.ts +578 -145
- package/dist/index.js +1 -110
- package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
- package/dist/port-file-Do3lQIEk.d.ts +29 -0
- package/dist/protocol-BOBdkqqm.d.ts +198 -0
- package/dist/rpc/port-file.d.ts +4 -17
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +2 -2
- package/dist/rpc/protocol.js +1 -0
- package/dist/rpc/rpc-client.d.ts +5 -4
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +2 -0
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
- package/dist/sidebar-state.d.ts +2 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/token-util-wdza526f.js +1 -0
- package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
- package/dist/tui-preferences.js +5 -2
- package/dist/tui.d.ts +9 -4
- package/dist/v2/server.d.ts +54 -0
- package/dist/v2/server.js +4 -0
- package/package.json +21 -9
- package/src/tui/entry.mjs +26 -25
- package/src/tui-compiled/raw/logger.js +24 -0
- package/src/tui-compiled/raw/rpc/protocol.js +3 -0
- package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/raw/selector.js +21 -0
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/raw/sidebar-state.js +960 -0
- package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
- package/src/tui-compiled/raw/tui-preferences.js +135 -0
- package/src/tui-compiled/raw/tui.tsx +135 -143
- package/src/tui-compiled/runtime/logger.js +24 -0
- package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
- package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/runtime/selector.js +21 -0
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/runtime/sidebar-state.js +960 -0
- package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
- package/src/tui-compiled/runtime/tui-preferences.js +135 -0
- package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
- package/dist/index-4effjmr6.js +0 -2
- package/dist/index-9ehhb051.js +0 -2
- package/dist/index-g3hsdjh4.js +0 -2
- package/dist/index-hh52v8sb.js +0 -2
- package/dist/index-jwn4r3sw.js +0 -1
- package/dist/index-sq36b120.js +0 -211
- package/dist/internal-BTndPmW2.d.ts +0 -914
- package/dist/quota-normalize-08hpp51v.js +0 -1
- package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
- package/dist/token-util-ypgz7rge.js +0 -1
- package/src/tui-compiled/raw/core-account-paths.ts +0 -114
- package/src/tui-compiled/raw/core-custody-state.ts +0 -243
- package/src/tui-compiled/raw/logger.ts +0 -53
- package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
- package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
- package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
- package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
- package/src/tui-compiled/raw/tui-preferences.ts +0 -328
- package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
- package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
- package/src/tui-compiled/runtime/logger.ts +0 -53
- package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
- package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
- package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
- package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
- package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
- package/src/tui-compiled/shared/internal.ts +0 -6
- package/src/tui-compiled/shared/logger.ts +0 -264
- package/src/tui-compiled/shared/paths.ts +0 -33
- package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
- package/src/tui-compiled/shared/util/error.ts +0 -9
- package/src/tui-compiled/shared/util/open-url.ts +0 -30
|
@@ -1,1096 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
|
|
3
|
-
import { errorMessage, openUrl } from '../shared/internal.ts'
|
|
4
|
-
import type { TuiPluginApi } from '@opencode-ai/plugin/tui'
|
|
5
|
-
import { createLogger } from './logger.ts'
|
|
6
|
-
import type { OpenDialogPayload } from './rpc-protocol.ts'
|
|
7
|
-
import {
|
|
8
|
-
type AccountQuota,
|
|
9
|
-
getCollapsedQuotaSummary,
|
|
10
|
-
getSidebarState,
|
|
11
|
-
resolveSessionSidebarRouting,
|
|
12
|
-
type SidebarState,
|
|
13
|
-
} from './sidebar-state.ts'
|
|
14
|
-
|
|
15
|
-
const log = createLogger('rpc-tui')
|
|
16
|
-
|
|
17
|
-
type ApplyFn = (
|
|
18
|
-
command: OpenDialogPayload['command'],
|
|
19
|
-
args: string,
|
|
20
|
-
) => Promise<{ text: string; knobs: Record<string, unknown> }>
|
|
21
|
-
|
|
22
|
-
export function buildCachekeepDialogOptions(payload: OpenDialogPayload) {
|
|
23
|
-
const enabled = payload.knobs.enabled === true
|
|
24
|
-
const subagents = payload.knobs.subagents === true
|
|
25
|
-
const sustain = payload.knobs.sustain === true
|
|
26
|
-
const running = payload.knobs.running === true
|
|
27
|
-
const tracked = Number(payload.knobs.tracked ?? 0)
|
|
28
|
-
const generatedAt = Number(payload.knobs.generatedAt ?? Date.now())
|
|
29
|
-
const lastWarmAt = Number(payload.knobs.lastWarmAt ?? 0)
|
|
30
|
-
const maxIdleWarmMs = Number(payload.knobs.maxIdleWarmMs ?? 60 * 60 * 1000)
|
|
31
|
-
const maxSubagentIdleMs = Number(
|
|
32
|
-
payload.knobs.maxSubagentIdleMs ?? 30 * 60 * 1000,
|
|
33
|
-
)
|
|
34
|
-
const windowKnob = payload.knobs.window as
|
|
35
|
-
| { startHour: number; endHour: number }
|
|
36
|
-
| undefined
|
|
37
|
-
const windowLabel = windowKnob
|
|
38
|
-
? `${String(windowKnob.startHour).padStart(2, '0')}-${String(windowKnob.endHour).padStart(2, '0')}`
|
|
39
|
-
: 'always (no window)'
|
|
40
|
-
const lastWarm = lastWarmAt
|
|
41
|
-
? `${Math.ceil((generatedAt - lastWarmAt) / 1000)}s ago`
|
|
42
|
-
: 'none yet'
|
|
43
|
-
const idleWindow = Math.round(maxIdleWarmMs / 60_000)
|
|
44
|
-
const subIdleWindow = Math.round(maxSubagentIdleMs / 60_000)
|
|
45
|
-
const statusParts = [
|
|
46
|
-
enabled ? '● Enabled' : '○ Disabled',
|
|
47
|
-
`timer ${running ? 'armed' : 'idle'}`,
|
|
48
|
-
`${tracked} tracked`,
|
|
49
|
-
`last warm ${lastWarm}`,
|
|
50
|
-
`window ${windowLabel}`,
|
|
51
|
-
`${idleWindow}m idle cap`,
|
|
52
|
-
`sustain ${sustain ? 'on' : 'off'}`,
|
|
53
|
-
`subagent idle ${subIdleWindow}m`,
|
|
54
|
-
].filter((part) => part.length > 0)
|
|
55
|
-
|
|
56
|
-
return [
|
|
57
|
-
{
|
|
58
|
-
title: statusParts.join(' | '),
|
|
59
|
-
value: 'status',
|
|
60
|
-
description: 'Current cachekeep state',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
title: enabled ? 'Turn off' : 'Turn on',
|
|
64
|
-
value: enabled ? 'off' : 'on',
|
|
65
|
-
description: enabled
|
|
66
|
-
? 'Stop capturing and warming prompt-cache prefixes'
|
|
67
|
-
: 'Persistently enable capture; the timer self-arms on request capture',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
title: subagents ? 'Subagent warming: on' : 'Subagent warming: off',
|
|
71
|
-
value: subagents ? 'subagents off' : 'subagents on',
|
|
72
|
-
description: subagents
|
|
73
|
-
? `Warm subagent sessions (${subIdleWindow}m idle cap). Disable to skip subagents.`
|
|
74
|
-
: `Skip subagent sessions. Enable to warm them too (${subIdleWindow}m idle cap).`,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
title: sustain
|
|
78
|
-
? 'Sustain main sessions: on'
|
|
79
|
-
: 'Sustain main sessions: off',
|
|
80
|
-
value: sustain ? 'sustain off' : 'sustain on',
|
|
81
|
-
description:
|
|
82
|
-
'main-only, idle-cap only; the configured clock window still applies.',
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
title: windowKnob ? `Warm window: ${windowLabel}` : 'Set warm window…',
|
|
86
|
-
value: 'set_window',
|
|
87
|
-
description: windowKnob
|
|
88
|
-
? `Currently only warming between ${windowLabel} local hours — pick to change`
|
|
89
|
-
: 'Restrict capture + warm to a clock-hour window, e.g. 9-18 or 22-6',
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
title: 'Clear warm window',
|
|
93
|
-
value: 'clear_window',
|
|
94
|
-
description: windowKnob
|
|
95
|
-
? 'Remove the clock-hour restriction — return to always-warm (within idle caps)'
|
|
96
|
-
: 'No clock-hour window is currently set',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
title: 'Refresh status',
|
|
100
|
-
value: 'refresh',
|
|
101
|
-
description: 'Re-read cachekeep status',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
title: 'Close',
|
|
105
|
-
value: 'close',
|
|
106
|
-
description: 'Close this dialog',
|
|
107
|
-
},
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
type ResetAccountKnob = {
|
|
112
|
-
accountKey: string
|
|
113
|
-
label: string
|
|
114
|
-
usedPercent?: number
|
|
115
|
-
availableCount?: number
|
|
116
|
-
applicableAvailableCount?: number
|
|
117
|
-
eligible: boolean
|
|
118
|
-
reason?: string
|
|
119
|
-
selectedCreditId?: string
|
|
120
|
-
selectedCreditExpiresAt?: string
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
type ResetPreviewKnob = ResetAccountKnob & {
|
|
124
|
-
chatgptAccountId?: string
|
|
125
|
-
resetTime?: string
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type ResetDialogOption = {
|
|
129
|
-
title: string
|
|
130
|
-
value: string
|
|
131
|
-
description: string
|
|
132
|
-
disabled?: boolean
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function resetAccountOptions(payload: OpenDialogPayload): ResetDialogOption[] {
|
|
136
|
-
const accounts = Array.isArray(payload.knobs.accounts)
|
|
137
|
-
? (payload.knobs.accounts as ResetAccountKnob[])
|
|
138
|
-
: []
|
|
139
|
-
return [
|
|
140
|
-
...accounts.map((account) => {
|
|
141
|
-
const used =
|
|
142
|
-
account.usedPercent === undefined
|
|
143
|
-
? 'quota unavailable'
|
|
144
|
-
: `${account.usedPercent}%`
|
|
145
|
-
const counts = `${account.applicableAvailableCount === undefined ? '?' : account.applicableAvailableCount}/${account.availableCount ?? '?'}`
|
|
146
|
-
const status = account.eligible
|
|
147
|
-
? 'eligible'
|
|
148
|
-
: (account.reason ?? 'unavailable')
|
|
149
|
-
const details = `${used} · ${counts}`
|
|
150
|
-
const shortDate =
|
|
151
|
-
account.selectedCreditExpiresAt?.slice(0, 10) ?? 'unknown'
|
|
152
|
-
return {
|
|
153
|
-
title: `${account.label} — ${status}`,
|
|
154
|
-
value: `account:${account.accountKey}`,
|
|
155
|
-
description: account.eligible
|
|
156
|
-
? `${details} · exp ${shortDate}`
|
|
157
|
-
: details,
|
|
158
|
-
}
|
|
159
|
-
}),
|
|
160
|
-
{
|
|
161
|
-
title: 'Refresh',
|
|
162
|
-
value: 'refresh',
|
|
163
|
-
description: 'Re-check account quota and reset credits',
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
title: 'Close',
|
|
167
|
-
value: 'close',
|
|
168
|
-
description: 'Close this dialog',
|
|
169
|
-
},
|
|
170
|
-
]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function resetResultOptions(payload: OpenDialogPayload): ResetDialogOption[] {
|
|
174
|
-
const accountKey =
|
|
175
|
-
typeof payload.knobs.accountKey === 'string'
|
|
176
|
-
? payload.knobs.accountKey
|
|
177
|
-
: undefined
|
|
178
|
-
const chatgptAccountId =
|
|
179
|
-
typeof payload.knobs.chatgptAccountId === 'string'
|
|
180
|
-
? payload.knobs.chatgptAccountId
|
|
181
|
-
: undefined
|
|
182
|
-
const code = typeof payload.knobs.code === 'string' ? payload.knobs.code : ''
|
|
183
|
-
const retrySafe =
|
|
184
|
-
(code === 'ambiguous' ||
|
|
185
|
-
code === 'http_error' ||
|
|
186
|
-
code === 'expired_unreconciled' ||
|
|
187
|
-
payload.knobs.stateWriteFailed === true) &&
|
|
188
|
-
typeof payload.knobs.retryGuidance === 'string' &&
|
|
189
|
-
accountKey !== undefined &&
|
|
190
|
-
chatgptAccountId !== undefined
|
|
191
|
-
return [
|
|
192
|
-
{
|
|
193
|
-
title: payload.text,
|
|
194
|
-
value: 'result',
|
|
195
|
-
description: 'Command result',
|
|
196
|
-
},
|
|
197
|
-
...(retrySafe
|
|
198
|
-
? [
|
|
199
|
-
{
|
|
200
|
-
title: 'Retry',
|
|
201
|
-
value: 'retry',
|
|
202
|
-
description:
|
|
203
|
-
'Reuse the bound request and credit identifiers for this uncertain outcome',
|
|
204
|
-
},
|
|
205
|
-
]
|
|
206
|
-
: []),
|
|
207
|
-
{
|
|
208
|
-
title: 'Refresh',
|
|
209
|
-
value: 'refresh',
|
|
210
|
-
description: 'Re-check account quota and reset credits',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
title: 'Close',
|
|
214
|
-
value: 'close',
|
|
215
|
-
description: 'Close this dialog',
|
|
216
|
-
},
|
|
217
|
-
]
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function openResetDialog(
|
|
221
|
-
api: TuiPluginApi,
|
|
222
|
-
payload: OpenDialogPayload,
|
|
223
|
-
apply: ApplyFn,
|
|
224
|
-
) {
|
|
225
|
-
let applyGeneration = 0
|
|
226
|
-
const render = (state: OpenDialogPayload) => {
|
|
227
|
-
const applyAndRender = (args: string) => {
|
|
228
|
-
const generation = ++applyGeneration
|
|
229
|
-
log.debug('reset dialog apply', { args, generation })
|
|
230
|
-
void apply('openai-reset', args)
|
|
231
|
-
.then((result) => {
|
|
232
|
-
log.debug('reset dialog apply result', {
|
|
233
|
-
generation,
|
|
234
|
-
currentGeneration: applyGeneration,
|
|
235
|
-
stage: result.knobs?.stage,
|
|
236
|
-
accountRows: Array.isArray(result.knobs?.accounts)
|
|
237
|
-
? (result.knobs.accounts as unknown[]).length
|
|
238
|
-
: undefined,
|
|
239
|
-
})
|
|
240
|
-
if (generation !== applyGeneration) return
|
|
241
|
-
if (typeof result.knobs?.stage !== 'string') {
|
|
242
|
-
render({
|
|
243
|
-
command: 'openai-reset',
|
|
244
|
-
text: 'The reset request is still completing or returned no result. Choose Refresh to check the current state — do NOT re-confirm.',
|
|
245
|
-
knobs: { stage: 'result', code: 'unknown' },
|
|
246
|
-
})
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
render({
|
|
250
|
-
command: 'openai-reset',
|
|
251
|
-
text: result.text,
|
|
252
|
-
knobs: result.knobs,
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
.catch((error) => {
|
|
256
|
-
if (generation !== applyGeneration) return
|
|
257
|
-
log.warn('reset dialog apply rejected', {
|
|
258
|
-
error: errorMessage(error),
|
|
259
|
-
})
|
|
260
|
-
render({
|
|
261
|
-
command: 'openai-reset',
|
|
262
|
-
text: 'Command failed — the plugin could not complete the request; the plugin log has details.',
|
|
263
|
-
knobs: { stage: 'result', code: 'error' },
|
|
264
|
-
})
|
|
265
|
-
})
|
|
266
|
-
}
|
|
267
|
-
const stage = state.knobs.stage
|
|
268
|
-
log.debug('reset dialog render', {
|
|
269
|
-
stage,
|
|
270
|
-
accountRows: Array.isArray(state.knobs.accounts)
|
|
271
|
-
? (state.knobs.accounts as unknown[]).length
|
|
272
|
-
: undefined,
|
|
273
|
-
knobKeys: Object.keys(state.knobs),
|
|
274
|
-
})
|
|
275
|
-
api.ui.dialog.setSize('xlarge')
|
|
276
|
-
|
|
277
|
-
if (stage === 'confirm') {
|
|
278
|
-
const preview = state.knobs.preview as ResetPreviewKnob | undefined
|
|
279
|
-
const accountKey = preview?.accountKey
|
|
280
|
-
const chatgptAccountId = preview?.chatgptAccountId
|
|
281
|
-
const availableCount = preview?.availableCount ?? 'unknown'
|
|
282
|
-
const DialogConfirm = api.ui.DialogConfirm
|
|
283
|
-
api.ui.dialog.replace(() => (
|
|
284
|
-
<DialogConfirm
|
|
285
|
-
title='Reset quota window'
|
|
286
|
-
message={`${state.text}\n\nThis SPENDS 1 of ${availableCount} reset credits — irreversible.\n\nChoose Reset to continue or Cancel to return.\n\nEnter = Cancel (host default). Press Tab then Enter to Reset.`}
|
|
287
|
-
onConfirm={() => {
|
|
288
|
-
if (!accountKey || !chatgptAccountId) return
|
|
289
|
-
applyAndRender(
|
|
290
|
-
`confirm ${encodeURIComponent(accountKey)} ${encodeURIComponent(chatgptAccountId)}`,
|
|
291
|
-
)
|
|
292
|
-
}}
|
|
293
|
-
onCancel={() => applyAndRender('refresh')}
|
|
294
|
-
/>
|
|
295
|
-
))
|
|
296
|
-
return
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const DialogSelect = api.ui.DialogSelect<string>
|
|
300
|
-
const options =
|
|
301
|
-
stage === 'result'
|
|
302
|
-
? resetResultOptions(state)
|
|
303
|
-
: resetAccountOptions(state)
|
|
304
|
-
api.ui.dialog.replace(() => (
|
|
305
|
-
<DialogSelect
|
|
306
|
-
title={
|
|
307
|
-
stage === 'result' ? 'OpenAI reset result' : 'OpenAI reset credits'
|
|
308
|
-
}
|
|
309
|
-
options={options}
|
|
310
|
-
onSelect={(option) => {
|
|
311
|
-
if (option.disabled) return
|
|
312
|
-
if (option.value === 'result') return
|
|
313
|
-
if (option.value === 'close') {
|
|
314
|
-
applyGeneration += 1
|
|
315
|
-
api.ui.dialog.clear()
|
|
316
|
-
return
|
|
317
|
-
}
|
|
318
|
-
if (option.value === 'refresh') {
|
|
319
|
-
applyAndRender('refresh')
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
if (option.value === 'retry') {
|
|
323
|
-
const accountKey = state.knobs.accountKey
|
|
324
|
-
const chatgptAccountId = state.knobs.chatgptAccountId
|
|
325
|
-
if (
|
|
326
|
-
typeof accountKey !== 'string' ||
|
|
327
|
-
typeof chatgptAccountId !== 'string'
|
|
328
|
-
) {
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
applyAndRender(
|
|
332
|
-
`retry ${encodeURIComponent(accountKey)} ${encodeURIComponent(chatgptAccountId)}`,
|
|
333
|
-
)
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
if (!option.value.startsWith('account:')) return
|
|
337
|
-
const accountKey = option.value.slice('account:'.length)
|
|
338
|
-
applyAndRender(`select ${encodeURIComponent(accountKey)}`)
|
|
339
|
-
}}
|
|
340
|
-
/>
|
|
341
|
-
))
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
render(payload)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function showText(api: TuiPluginApi, text: string) {
|
|
348
|
-
api.ui.dialog.setSize('xlarge')
|
|
349
|
-
api.ui.dialog.replace(() => (
|
|
350
|
-
<box flexDirection='column' padding={1} width='100%'>
|
|
351
|
-
<text>{text}</text>
|
|
352
|
-
</box>
|
|
353
|
-
))
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function showSetCachekeepWindowPrompt(
|
|
357
|
-
api: TuiPluginApi,
|
|
358
|
-
apply: ApplyFn,
|
|
359
|
-
state: OpenDialogPayload,
|
|
360
|
-
render: (state: OpenDialogPayload) => void,
|
|
361
|
-
) {
|
|
362
|
-
const DialogPrompt = api.ui.DialogPrompt
|
|
363
|
-
const knobWindow = state.knobs.window as
|
|
364
|
-
| { startHour: number; endHour: number }
|
|
365
|
-
| undefined
|
|
366
|
-
const seed = knobWindow ? `${knobWindow.startHour}-${knobWindow.endHour}` : ''
|
|
367
|
-
api.ui.dialog.setSize('xlarge')
|
|
368
|
-
api.ui.dialog.replace(() => (
|
|
369
|
-
<DialogPrompt
|
|
370
|
-
title='Cachekeep warm window'
|
|
371
|
-
description={() => <text>{state.text}</text>}
|
|
372
|
-
placeholder='HH-HH (e.g. 9-18 or 22-6)'
|
|
373
|
-
value={seed}
|
|
374
|
-
onConfirm={(value: string) => {
|
|
375
|
-
const trimmed = value.trim()
|
|
376
|
-
if (!trimmed) {
|
|
377
|
-
render(state)
|
|
378
|
-
return
|
|
379
|
-
}
|
|
380
|
-
void apply('openai-cachekeep', trimmed).then((r) => {
|
|
381
|
-
api.ui.toast({ message: r.text })
|
|
382
|
-
render({
|
|
383
|
-
command: 'openai-cachekeep',
|
|
384
|
-
text: r.text,
|
|
385
|
-
knobs: r.knobs,
|
|
386
|
-
})
|
|
387
|
-
})
|
|
388
|
-
}}
|
|
389
|
-
onCancel={() => render(state)}
|
|
390
|
-
/>
|
|
391
|
-
))
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
export function openCommandDialog(
|
|
395
|
-
api: TuiPluginApi,
|
|
396
|
-
payload: OpenDialogPayload,
|
|
397
|
-
apply: ApplyFn,
|
|
398
|
-
sessionId?: string,
|
|
399
|
-
) {
|
|
400
|
-
if (payload.command === 'openai-reset') {
|
|
401
|
-
openResetDialog(api, payload, apply)
|
|
402
|
-
return
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (payload.command === 'openai-routing') {
|
|
406
|
-
const current = (payload.knobs.mode as string) ?? 'main-first'
|
|
407
|
-
const DialogSelect = api.ui.DialogSelect<string>
|
|
408
|
-
api.ui.dialog.setSize('xlarge')
|
|
409
|
-
api.ui.dialog.replace(() => (
|
|
410
|
-
<DialogSelect
|
|
411
|
-
title='OpenAI routing'
|
|
412
|
-
current={current}
|
|
413
|
-
options={[
|
|
414
|
-
{
|
|
415
|
-
title: 'Main first',
|
|
416
|
-
value: 'main-first',
|
|
417
|
-
description: 'Use the main account until exhausted',
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
title: 'Fallback first',
|
|
421
|
-
value: 'fallback-first',
|
|
422
|
-
description: 'Prefer fallback accounts, preserve main',
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
title: 'Sticky balanced',
|
|
426
|
-
value: 'sticky-balanced',
|
|
427
|
-
description:
|
|
428
|
-
'Keep each session on one account while balancing new sessions',
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
title: "Reset this session's pin",
|
|
432
|
-
value: 'reset',
|
|
433
|
-
description:
|
|
434
|
-
'Unpin this session; the next request may still choose the same account',
|
|
435
|
-
},
|
|
436
|
-
]}
|
|
437
|
-
onSelect={(option) => {
|
|
438
|
-
void apply('openai-routing', String(option.value)).then((r) => {
|
|
439
|
-
api.ui.toast({ message: r.text })
|
|
440
|
-
api.ui.dialog.clear()
|
|
441
|
-
})
|
|
442
|
-
}}
|
|
443
|
-
/>
|
|
444
|
-
))
|
|
445
|
-
return
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if (payload.command === 'openai-dump') {
|
|
449
|
-
const enabled = payload.knobs.enabled === true
|
|
450
|
-
const DialogConfirm = api.ui.DialogConfirm
|
|
451
|
-
api.ui.dialog.setSize('xlarge')
|
|
452
|
-
api.ui.dialog.replace(() => (
|
|
453
|
-
<DialogConfirm
|
|
454
|
-
title='OpenAI request dump'
|
|
455
|
-
message={`${payload.text}\n\n${enabled ? 'Disable' : 'Enable'} request dump?`}
|
|
456
|
-
onConfirm={() => {
|
|
457
|
-
void apply('openai-dump', enabled ? 'off' : 'on').then((r) => {
|
|
458
|
-
api.ui.toast({ message: r.text })
|
|
459
|
-
api.ui.dialog.clear()
|
|
460
|
-
})
|
|
461
|
-
}}
|
|
462
|
-
onCancel={() => api.ui.dialog.clear()}
|
|
463
|
-
/>
|
|
464
|
-
))
|
|
465
|
-
return
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (payload.command === 'openai-killswitch') {
|
|
469
|
-
const config = (payload.knobs.config ?? {}) as {
|
|
470
|
-
enabled?: boolean
|
|
471
|
-
main?: Record<string, number>
|
|
472
|
-
accounts?: Record<string, Record<string, number>>
|
|
473
|
-
}
|
|
474
|
-
const accountIds = (payload.knobs.accountIds as string[]) ?? []
|
|
475
|
-
const enabled = config.enabled === true
|
|
476
|
-
const readT = (t: Record<string, number> | undefined) => {
|
|
477
|
-
const fh = t?.primary ?? t?.['5h'] ?? 5
|
|
478
|
-
const sd = t?.secondary ?? t?.['1w'] ?? 10
|
|
479
|
-
return { fh, sd }
|
|
480
|
-
}
|
|
481
|
-
const mainT = readT(config.main)
|
|
482
|
-
const seedParts = [`main:${mainT.fh},${mainT.sd}`]
|
|
483
|
-
for (const id of accountIds) {
|
|
484
|
-
const t = readT(config.accounts?.[id] ?? config.main)
|
|
485
|
-
seedParts.push(`${id}:${t.fh},${t.sd}`)
|
|
486
|
-
}
|
|
487
|
-
const seed = seedParts.join(' ')
|
|
488
|
-
|
|
489
|
-
const openEdit = () => {
|
|
490
|
-
const DialogPrompt = api.ui.DialogPrompt
|
|
491
|
-
api.ui.dialog.setSize('xlarge')
|
|
492
|
-
api.ui.dialog.replace(() => (
|
|
493
|
-
<DialogPrompt
|
|
494
|
-
title='Killswitch thresholds'
|
|
495
|
-
description={() => <text>{payload.text}</text>}
|
|
496
|
-
placeholder='main:5,10 work-alt:5,10'
|
|
497
|
-
value={seed}
|
|
498
|
-
onConfirm={(value: string) => {
|
|
499
|
-
void apply('openai-killswitch', `set ${value.trim()}`).then((r) => {
|
|
500
|
-
api.ui.toast({ message: r.text })
|
|
501
|
-
api.ui.dialog.clear()
|
|
502
|
-
})
|
|
503
|
-
}}
|
|
504
|
-
onCancel={() => api.ui.dialog.clear()}
|
|
505
|
-
/>
|
|
506
|
-
))
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const DialogSelect = api.ui.DialogSelect<string>
|
|
510
|
-
api.ui.dialog.setSize('xlarge')
|
|
511
|
-
api.ui.dialog.replace(() => (
|
|
512
|
-
<DialogSelect
|
|
513
|
-
title='OpenAI killswitch'
|
|
514
|
-
options={[
|
|
515
|
-
{
|
|
516
|
-
title: enabled ? 'Disable killswitch' : 'Enable killswitch',
|
|
517
|
-
value: enabled ? 'off' : 'on',
|
|
518
|
-
description: enabled
|
|
519
|
-
? 'Stop hard-blocking on low quota'
|
|
520
|
-
: 'Hard-block requests when quota drops below thresholds',
|
|
521
|
-
},
|
|
522
|
-
{
|
|
523
|
-
title: 'Edit thresholds\u2026',
|
|
524
|
-
value: 'edit',
|
|
525
|
-
description: 'Set per-account primary,secondary cutoffs',
|
|
526
|
-
},
|
|
527
|
-
]}
|
|
528
|
-
onSelect={(option) => {
|
|
529
|
-
if (option.value === 'edit') {
|
|
530
|
-
openEdit()
|
|
531
|
-
return
|
|
532
|
-
}
|
|
533
|
-
void apply('openai-killswitch', String(option.value)).then((r) => {
|
|
534
|
-
api.ui.toast({ message: r.text })
|
|
535
|
-
api.ui.dialog.clear()
|
|
536
|
-
})
|
|
537
|
-
}}
|
|
538
|
-
/>
|
|
539
|
-
))
|
|
540
|
-
return
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (payload.command === 'openai-logging') {
|
|
544
|
-
const current = (payload.knobs.level as string) ?? 'info'
|
|
545
|
-
const levels = ['error', 'warn', 'info', 'debug', 'trace']
|
|
546
|
-
const DialogSelect = api.ui.DialogSelect<string>
|
|
547
|
-
api.ui.dialog.setSize('xlarge')
|
|
548
|
-
api.ui.dialog.replace(() => (
|
|
549
|
-
<DialogSelect
|
|
550
|
-
title='OpenAI logging'
|
|
551
|
-
current={current}
|
|
552
|
-
options={levels.map((level) => ({
|
|
553
|
-
title: level,
|
|
554
|
-
value: level,
|
|
555
|
-
description:
|
|
556
|
-
level === current
|
|
557
|
-
? 'currently active'
|
|
558
|
-
: `Set log level to ${level}`,
|
|
559
|
-
}))}
|
|
560
|
-
onSelect={(option) => {
|
|
561
|
-
void apply('openai-logging', String(option.value)).then((r) => {
|
|
562
|
-
api.ui.toast({ message: r.text })
|
|
563
|
-
api.ui.dialog.clear()
|
|
564
|
-
})
|
|
565
|
-
}}
|
|
566
|
-
/>
|
|
567
|
-
))
|
|
568
|
-
return
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (payload.command === 'openai-cachekeep') {
|
|
572
|
-
const render = (state: OpenDialogPayload) => {
|
|
573
|
-
const options = buildCachekeepDialogOptions(state)
|
|
574
|
-
const DialogSelect = api.ui.DialogSelect<string>
|
|
575
|
-
api.ui.dialog.setSize('xlarge')
|
|
576
|
-
api.ui.dialog.replace(() => (
|
|
577
|
-
<DialogSelect
|
|
578
|
-
title='OpenAI cachekeep'
|
|
579
|
-
current='status'
|
|
580
|
-
options={options}
|
|
581
|
-
onSelect={(option) => {
|
|
582
|
-
if (option.value === 'close') {
|
|
583
|
-
api.ui.dialog.clear()
|
|
584
|
-
return
|
|
585
|
-
}
|
|
586
|
-
if (option.value === 'set_window') {
|
|
587
|
-
showSetCachekeepWindowPrompt(api, apply, state, render)
|
|
588
|
-
return
|
|
589
|
-
}
|
|
590
|
-
if (option.value === 'clear_window') {
|
|
591
|
-
// The option label is `clear_window`, but executeCachekeepCommand
|
|
592
|
-
// matches on `window clear` — forward the canonical form or the
|
|
593
|
-
// command falls through to the usage-text branch and the window
|
|
594
|
-
// is never cleared.
|
|
595
|
-
void apply('openai-cachekeep', 'window clear').then((r) => {
|
|
596
|
-
render({
|
|
597
|
-
command: 'openai-cachekeep',
|
|
598
|
-
text: r.text,
|
|
599
|
-
knobs: r.knobs,
|
|
600
|
-
})
|
|
601
|
-
})
|
|
602
|
-
return
|
|
603
|
-
}
|
|
604
|
-
const args =
|
|
605
|
-
option.value === 'refresh' ? 'status' : String(option.value)
|
|
606
|
-
void apply('openai-cachekeep', args).then((r) => {
|
|
607
|
-
render({
|
|
608
|
-
command: 'openai-cachekeep',
|
|
609
|
-
text: r.text,
|
|
610
|
-
knobs: r.knobs,
|
|
611
|
-
})
|
|
612
|
-
})
|
|
613
|
-
}}
|
|
614
|
-
/>
|
|
615
|
-
))
|
|
616
|
-
}
|
|
617
|
-
render(payload)
|
|
618
|
-
return
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (payload.command === 'openai-account') {
|
|
622
|
-
openAccountDialog(api, apply, sessionId, payload)
|
|
623
|
-
return
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// fallback for quota (display-only)
|
|
627
|
-
showText(api, payload.text)
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// -- Accounts dialog ---------------------------------------------------------
|
|
631
|
-
|
|
632
|
-
export function formatQuotaWindows(
|
|
633
|
-
quota: AccountQuota | null | undefined,
|
|
634
|
-
): string {
|
|
635
|
-
const windowSummary = getCollapsedQuotaSummary(quota ?? null).text
|
|
636
|
-
if (windowSummary) return windowSummary
|
|
637
|
-
if (quota?.resetCreditsAvailable !== undefined) {
|
|
638
|
-
return `resets: ${quota.resetCreditsAvailable}`
|
|
639
|
-
}
|
|
640
|
-
return 'no quota data'
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
function osc52Copy(api: TuiPluginApi, text: string): boolean {
|
|
644
|
-
try {
|
|
645
|
-
const renderer = api.renderer as unknown as
|
|
646
|
-
| {
|
|
647
|
-
copyToClipboardOSC52?: (t: string) => boolean
|
|
648
|
-
}
|
|
649
|
-
| null
|
|
650
|
-
| undefined
|
|
651
|
-
if (renderer && typeof renderer.copyToClipboardOSC52 === 'function') {
|
|
652
|
-
return renderer.copyToClipboardOSC52(text)
|
|
653
|
-
}
|
|
654
|
-
} catch {
|
|
655
|
-
// best-effort
|
|
656
|
-
}
|
|
657
|
-
return false
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
export function buildAccountDialogRows(
|
|
661
|
-
state: SidebarState,
|
|
662
|
-
sessionId?: string,
|
|
663
|
-
) {
|
|
664
|
-
const activeId = resolveSessionSidebarRouting(state, sessionId).activeId
|
|
665
|
-
return [
|
|
666
|
-
{
|
|
667
|
-
title: `main${activeId === 'main' ? ' \u2022 active' : ''}`,
|
|
668
|
-
value: 'main',
|
|
669
|
-
description: formatQuotaWindows(state.main.quota),
|
|
670
|
-
},
|
|
671
|
-
...state.fallbacks.map((fb) => ({
|
|
672
|
-
title: `${fb.label ?? fb.id}${activeId === fb.id ? ' \u2022 active' : ''}${!fb.enabled ? ' (disabled)' : ''}`,
|
|
673
|
-
value: fb.id,
|
|
674
|
-
description: formatQuotaWindows(fb.quota),
|
|
675
|
-
})),
|
|
676
|
-
]
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
export function accountDialogModeOption(mode: unknown) {
|
|
680
|
-
const claustrum = mode === 'claustrum'
|
|
681
|
-
return {
|
|
682
|
-
title: claustrum ? 'Leave Claustrum' : 'Enter Claustrum',
|
|
683
|
-
value: claustrum ? '__local__' : '__claustrum__',
|
|
684
|
-
description: claustrum
|
|
685
|
-
? 'Require fresh local logins before local refresh resumes'
|
|
686
|
-
: 'Verify every enabled account before custody takes over',
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
export function buildFallbackAccountOptions(input: {
|
|
691
|
-
id: string
|
|
692
|
-
label?: string
|
|
693
|
-
enabled: boolean
|
|
694
|
-
index: number
|
|
695
|
-
count: number
|
|
696
|
-
}) {
|
|
697
|
-
const name = input.label ?? input.id
|
|
698
|
-
const options: Array<{
|
|
699
|
-
title: string
|
|
700
|
-
value: string
|
|
701
|
-
description: string
|
|
702
|
-
}> = [
|
|
703
|
-
{
|
|
704
|
-
title: input.enabled ? 'Disable' : 'Enable',
|
|
705
|
-
value: input.enabled ? 'disable' : 'enable',
|
|
706
|
-
description: `${input.enabled ? 'Disable' : 'Enable'} ${name}`,
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
title: 'Remove',
|
|
710
|
-
value: 'remove',
|
|
711
|
-
description: `Remove ${name}`,
|
|
712
|
-
},
|
|
713
|
-
]
|
|
714
|
-
if (input.index > 0) {
|
|
715
|
-
options.push({
|
|
716
|
-
title: 'Move up',
|
|
717
|
-
value: 'move_up',
|
|
718
|
-
description: 'Swap with the preceding fallback',
|
|
719
|
-
})
|
|
720
|
-
}
|
|
721
|
-
if (input.index < input.count - 1) {
|
|
722
|
-
options.push({
|
|
723
|
-
title: 'Move down',
|
|
724
|
-
value: 'move_down',
|
|
725
|
-
description: 'Swap with the following fallback',
|
|
726
|
-
})
|
|
727
|
-
}
|
|
728
|
-
options.push({
|
|
729
|
-
title: 'Back',
|
|
730
|
-
value: 'back',
|
|
731
|
-
description: 'Return to account list',
|
|
732
|
-
})
|
|
733
|
-
return options
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
function openAccountDialog(
|
|
737
|
-
api: TuiPluginApi,
|
|
738
|
-
apply: ApplyFn,
|
|
739
|
-
sessionId?: string,
|
|
740
|
-
payload?: OpenDialogPayload,
|
|
741
|
-
) {
|
|
742
|
-
const DialogConfirm = api.ui.DialogConfirm
|
|
743
|
-
let claustrumMode = payload?.knobs.claustrumMode
|
|
744
|
-
|
|
745
|
-
function showL1() {
|
|
746
|
-
void getSidebarState().then((state) => {
|
|
747
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
748
|
-
api.ui.dialog.setSize('xlarge')
|
|
749
|
-
api.ui.dialog.replace(() => (
|
|
750
|
-
<DialogSelectInner
|
|
751
|
-
title='OpenAI Accounts'
|
|
752
|
-
options={[
|
|
753
|
-
accountDialogModeOption(claustrumMode),
|
|
754
|
-
...buildAccountDialogRows(state, sessionId),
|
|
755
|
-
{
|
|
756
|
-
title: 'Add account\u2026',
|
|
757
|
-
value: '__add__',
|
|
758
|
-
description: 'Sign in to a new OpenAI account',
|
|
759
|
-
},
|
|
760
|
-
]}
|
|
761
|
-
onSelect={(option) => {
|
|
762
|
-
if (
|
|
763
|
-
option.value === '__claustrum__' ||
|
|
764
|
-
option.value === '__local__'
|
|
765
|
-
) {
|
|
766
|
-
const args =
|
|
767
|
-
option.value === '__claustrum__' ? 'claustrum' : 'local'
|
|
768
|
-
void apply('openai-account', args).then((r) => {
|
|
769
|
-
api.ui.toast({ message: r.text })
|
|
770
|
-
claustrumMode = r.knobs.claustrumMode
|
|
771
|
-
showL1()
|
|
772
|
-
})
|
|
773
|
-
return
|
|
774
|
-
}
|
|
775
|
-
if (option.value === '__add__') {
|
|
776
|
-
showAddFlow()
|
|
777
|
-
return
|
|
778
|
-
}
|
|
779
|
-
// Main has no per-account actions: it is not removable or reorderable,
|
|
780
|
-
// regardless of whether the active session uses mode or sticky routing.
|
|
781
|
-
if (option.value === 'main') return
|
|
782
|
-
showL2Fallback(option.value)
|
|
783
|
-
}}
|
|
784
|
-
/>
|
|
785
|
-
))
|
|
786
|
-
})
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
function showL2Fallback(id: string) {
|
|
790
|
-
void getSidebarState().then((state) => {
|
|
791
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
792
|
-
const fbIndex = state.fallbacks.findIndex((f) => f.id === id)
|
|
793
|
-
const fb = state.fallbacks[fbIndex]
|
|
794
|
-
if (!fb) {
|
|
795
|
-
showL1()
|
|
796
|
-
return
|
|
797
|
-
}
|
|
798
|
-
const options = buildFallbackAccountOptions({
|
|
799
|
-
id: fb.id,
|
|
800
|
-
label: fb.label,
|
|
801
|
-
enabled: fb.enabled,
|
|
802
|
-
index: fbIndex,
|
|
803
|
-
count: state.fallbacks.length,
|
|
804
|
-
})
|
|
805
|
-
|
|
806
|
-
api.ui.dialog.setSize('xlarge')
|
|
807
|
-
api.ui.dialog.replace(() => (
|
|
808
|
-
<DialogSelectInner
|
|
809
|
-
title={fb.label ?? fb.id}
|
|
810
|
-
options={options}
|
|
811
|
-
onSelect={(option) => {
|
|
812
|
-
if (option.value === 'remove') {
|
|
813
|
-
api.ui.dialog.setSize('xlarge')
|
|
814
|
-
api.ui.dialog.replace(() => (
|
|
815
|
-
<DialogConfirm
|
|
816
|
-
title='Remove account'
|
|
817
|
-
message={`Remove ${fb.label ?? fb.id}?`}
|
|
818
|
-
onConfirm={() => {
|
|
819
|
-
void apply('openai-account', `remove ${id}`).then((r) => {
|
|
820
|
-
api.ui.toast({ message: r.text })
|
|
821
|
-
showL1()
|
|
822
|
-
})
|
|
823
|
-
}}
|
|
824
|
-
onCancel={() => showL2Fallback(id)}
|
|
825
|
-
/>
|
|
826
|
-
))
|
|
827
|
-
return
|
|
828
|
-
}
|
|
829
|
-
if (option.value === 'enable' || option.value === 'disable') {
|
|
830
|
-
void apply('openai-account', `${option.value} ${id}`).then(
|
|
831
|
-
(r) => {
|
|
832
|
-
api.ui.toast({ message: r.text })
|
|
833
|
-
showL1()
|
|
834
|
-
},
|
|
835
|
-
)
|
|
836
|
-
return
|
|
837
|
-
}
|
|
838
|
-
if (option.value === 'move_up') {
|
|
839
|
-
const neighbor = state.fallbacks[fbIndex - 1]
|
|
840
|
-
if (neighbor) {
|
|
841
|
-
void apply('openai-account', `order ${id} ${neighbor.id}`).then(
|
|
842
|
-
(r) => {
|
|
843
|
-
api.ui.toast({ message: r.text })
|
|
844
|
-
showL1()
|
|
845
|
-
},
|
|
846
|
-
)
|
|
847
|
-
}
|
|
848
|
-
return
|
|
849
|
-
}
|
|
850
|
-
if (option.value === 'move_down') {
|
|
851
|
-
const neighbor = state.fallbacks[fbIndex + 1]
|
|
852
|
-
if (neighbor) {
|
|
853
|
-
void apply('openai-account', `order ${id} ${neighbor.id}`).then(
|
|
854
|
-
(r) => {
|
|
855
|
-
api.ui.toast({ message: r.text })
|
|
856
|
-
showL1()
|
|
857
|
-
},
|
|
858
|
-
)
|
|
859
|
-
}
|
|
860
|
-
return
|
|
861
|
-
}
|
|
862
|
-
showL1()
|
|
863
|
-
}}
|
|
864
|
-
/>
|
|
865
|
-
))
|
|
866
|
-
})
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// -- Add flow ---------------------------------------------------------------
|
|
870
|
-
|
|
871
|
-
function showAddFlow() {
|
|
872
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
873
|
-
api.ui.dialog.setSize('xlarge')
|
|
874
|
-
api.ui.dialog.replace(() => (
|
|
875
|
-
<DialogSelectInner
|
|
876
|
-
title='Add account'
|
|
877
|
-
options={[
|
|
878
|
-
{
|
|
879
|
-
title: 'Browser sign-in (local)',
|
|
880
|
-
value: 'browser',
|
|
881
|
-
description: 'Opens a browser window for OAuth on this machine',
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
title: 'Device code (remote / no browser)',
|
|
885
|
-
value: 'device',
|
|
886
|
-
description: 'Enter a code on another device to authorize',
|
|
887
|
-
},
|
|
888
|
-
{
|
|
889
|
-
title: 'Back',
|
|
890
|
-
value: 'back',
|
|
891
|
-
description: 'Return to account list',
|
|
892
|
-
},
|
|
893
|
-
]}
|
|
894
|
-
onSelect={(option) => {
|
|
895
|
-
if (option.value === 'browser') {
|
|
896
|
-
showLabelPrompt('browser')
|
|
897
|
-
return
|
|
898
|
-
}
|
|
899
|
-
if (option.value === 'device') {
|
|
900
|
-
showLabelPrompt('device')
|
|
901
|
-
return
|
|
902
|
-
}
|
|
903
|
-
showL1()
|
|
904
|
-
}}
|
|
905
|
-
/>
|
|
906
|
-
))
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
function showLabelPrompt(mode: 'browser' | 'device') {
|
|
910
|
-
const DialogPromptInner = api.ui.DialogPrompt
|
|
911
|
-
api.ui.dialog.setSize('xlarge')
|
|
912
|
-
api.ui.dialog.replace(() => (
|
|
913
|
-
<DialogPromptInner
|
|
914
|
-
title='Label (optional)'
|
|
915
|
-
description={() => (
|
|
916
|
-
<text>
|
|
917
|
-
Give this account a name (e.g. "work", "personal"). Leave empty to
|
|
918
|
-
auto-detect.
|
|
919
|
-
</text>
|
|
920
|
-
)}
|
|
921
|
-
placeholder='e.g. work'
|
|
922
|
-
value=''
|
|
923
|
-
onConfirm={(label: string) => {
|
|
924
|
-
if (mode === 'browser') {
|
|
925
|
-
startBrowserAdd(label.trim() || undefined)
|
|
926
|
-
} else {
|
|
927
|
-
startDeviceAdd(label.trim() || undefined)
|
|
928
|
-
}
|
|
929
|
-
}}
|
|
930
|
-
onCancel={() => showAddFlow()}
|
|
931
|
-
/>
|
|
932
|
-
))
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
function startBrowserAdd(label: string | undefined) {
|
|
936
|
-
const args = label ? `add ${label}` : 'add'
|
|
937
|
-
void apply('openai-account', args).then((r) => {
|
|
938
|
-
const url = r.knobs.url as string | undefined
|
|
939
|
-
const instructions = r.knobs.instructions as string | undefined
|
|
940
|
-
|
|
941
|
-
if (!url) {
|
|
942
|
-
api.ui.toast({ message: 'Failed to get auth URL', variant: 'warning' })
|
|
943
|
-
showL1()
|
|
944
|
-
return
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// Auto-open the browser (best-effort)
|
|
948
|
-
try {
|
|
949
|
-
openUrl(url)
|
|
950
|
-
} catch {
|
|
951
|
-
// best-effort
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
showBrowserAuthScreen(url, instructions)
|
|
955
|
-
})
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
function showBrowserAuthScreen(
|
|
959
|
-
url: string,
|
|
960
|
-
_instructions: string | undefined,
|
|
961
|
-
) {
|
|
962
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
963
|
-
api.ui.dialog.setSize('xlarge')
|
|
964
|
-
api.ui.dialog.replace(() => (
|
|
965
|
-
<DialogSelectInner
|
|
966
|
-
title='Browser sign-in'
|
|
967
|
-
options={[
|
|
968
|
-
{
|
|
969
|
-
title: 'Copy auth URL',
|
|
970
|
-
value: 'copy_url',
|
|
971
|
-
description: url,
|
|
972
|
-
},
|
|
973
|
-
{
|
|
974
|
-
title: 'Open in browser',
|
|
975
|
-
value: 'open',
|
|
976
|
-
description: 'Try to open the URL in your browser',
|
|
977
|
-
},
|
|
978
|
-
{
|
|
979
|
-
title: "Done / I've authorized",
|
|
980
|
-
value: 'done',
|
|
981
|
-
description: 'Return to account list',
|
|
982
|
-
},
|
|
983
|
-
{
|
|
984
|
-
title: 'Cancel',
|
|
985
|
-
value: 'cancel',
|
|
986
|
-
description: 'Return to account list without adding',
|
|
987
|
-
},
|
|
988
|
-
]}
|
|
989
|
-
skipFilter={true}
|
|
990
|
-
onSelect={(option) => {
|
|
991
|
-
if (option.value === 'copy_url') {
|
|
992
|
-
const ok = osc52Copy(api, url)
|
|
993
|
-
api.ui.toast({
|
|
994
|
-
message: ok
|
|
995
|
-
? 'Auth URL copied'
|
|
996
|
-
: 'Copy unsupported — select the URL text manually',
|
|
997
|
-
variant: ok ? 'success' : 'warning',
|
|
998
|
-
})
|
|
999
|
-
return
|
|
1000
|
-
}
|
|
1001
|
-
if (option.value === 'open') {
|
|
1002
|
-
try {
|
|
1003
|
-
openUrl(url)
|
|
1004
|
-
} catch {
|
|
1005
|
-
// best-effort
|
|
1006
|
-
}
|
|
1007
|
-
return
|
|
1008
|
-
}
|
|
1009
|
-
showL1()
|
|
1010
|
-
}}
|
|
1011
|
-
/>
|
|
1012
|
-
))
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function startDeviceAdd(label: string | undefined) {
|
|
1016
|
-
const args = label ? `add --headless ${label}` : 'add --headless'
|
|
1017
|
-
void apply('openai-account', args).then((r) => {
|
|
1018
|
-
const verificationUrl = r.knobs.verificationUrl as string | undefined
|
|
1019
|
-
const userCode = r.knobs.userCode as string | undefined
|
|
1020
|
-
const instructions = r.knobs.instructions as string | undefined
|
|
1021
|
-
|
|
1022
|
-
if (!verificationUrl) {
|
|
1023
|
-
api.ui.toast({
|
|
1024
|
-
message: 'Failed to get device code',
|
|
1025
|
-
variant: 'warning',
|
|
1026
|
-
})
|
|
1027
|
-
showL1()
|
|
1028
|
-
return
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
showDeviceCodeScreen(verificationUrl, userCode, instructions)
|
|
1032
|
-
})
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
function showDeviceCodeScreen(
|
|
1036
|
-
verificationUrl: string,
|
|
1037
|
-
userCode: string | undefined,
|
|
1038
|
-
_instructions: string | undefined,
|
|
1039
|
-
) {
|
|
1040
|
-
const DialogSelectInner = api.ui.DialogSelect<string>
|
|
1041
|
-
api.ui.dialog.setSize('xlarge')
|
|
1042
|
-
api.ui.dialog.replace(() => (
|
|
1043
|
-
<DialogSelectInner
|
|
1044
|
-
title='Device code'
|
|
1045
|
-
options={[
|
|
1046
|
-
{
|
|
1047
|
-
title: userCode ? `Copy code: ${userCode}` : 'Copy code',
|
|
1048
|
-
value: 'copy_code',
|
|
1049
|
-
description: 'Copy the user code to clipboard',
|
|
1050
|
-
},
|
|
1051
|
-
{
|
|
1052
|
-
title: 'Copy URL',
|
|
1053
|
-
value: 'copy_url',
|
|
1054
|
-
description: verificationUrl,
|
|
1055
|
-
},
|
|
1056
|
-
{
|
|
1057
|
-
title: "Done / I've authorized",
|
|
1058
|
-
value: 'done',
|
|
1059
|
-
description: 'Return to account list',
|
|
1060
|
-
},
|
|
1061
|
-
{
|
|
1062
|
-
title: 'Cancel',
|
|
1063
|
-
value: 'cancel',
|
|
1064
|
-
description: 'Return to account list without adding',
|
|
1065
|
-
},
|
|
1066
|
-
]}
|
|
1067
|
-
skipFilter={true}
|
|
1068
|
-
onSelect={(option) => {
|
|
1069
|
-
if (option.value === 'copy_code' && userCode) {
|
|
1070
|
-
const ok = osc52Copy(api, userCode)
|
|
1071
|
-
api.ui.toast({
|
|
1072
|
-
message: ok
|
|
1073
|
-
? 'Code copied'
|
|
1074
|
-
: 'Copy unsupported — enter the code manually',
|
|
1075
|
-
variant: ok ? 'success' : 'warning',
|
|
1076
|
-
})
|
|
1077
|
-
return
|
|
1078
|
-
}
|
|
1079
|
-
if (option.value === 'copy_url') {
|
|
1080
|
-
const ok = osc52Copy(api, verificationUrl)
|
|
1081
|
-
api.ui.toast({
|
|
1082
|
-
message: ok
|
|
1083
|
-
? 'URL copied'
|
|
1084
|
-
: 'Copy unsupported — enter the URL manually',
|
|
1085
|
-
variant: ok ? 'success' : 'warning',
|
|
1086
|
-
})
|
|
1087
|
-
return
|
|
1088
|
-
}
|
|
1089
|
-
showL1()
|
|
1090
|
-
}}
|
|
1091
|
-
/>
|
|
1092
|
-
))
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
void showL1()
|
|
1096
|
-
}
|