@cortexkit/opencode-anthropic-auth 1.22.0 → 2.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 +259 -198
- package/dist/billing-lineage.d.ts +51 -0
- package/dist/cli-failure.d.ts +69 -0
- package/dist/cli.d.ts +16 -10
- package/dist/cli.js +111 -6
- package/dist/custody-dimensions.d.ts +19 -0
- package/dist/custody-mode.d.ts +46 -0
- package/dist/effort-history.d.ts +11 -2
- package/dist/fable-fallback.d.ts +1 -1
- package/dist/index-1jkyhgyr.js +2 -0
- package/dist/{index-bx5hsgf8.js → index-2arfb1y3.js} +2 -2
- package/dist/{index-926t2bzc.js → index-4ya27n12.js} +1 -1
- package/dist/{index-cgez4m0h.js → index-aaajxwzd.js} +1 -1
- package/dist/index-ae3fq4n6.js +42 -0
- package/dist/index-fds0s2j4.js +2 -0
- package/dist/index-nngmvczq.js +450 -0
- package/dist/index-pn597b6f.js +2 -0
- package/dist/{index-7f57ewkp.js → index-rqync0pc.js} +1 -1
- package/dist/index.js +1 -33
- package/dist/native-command.d.ts +19 -0
- package/dist/prime-manager-registry.d.ts +5 -1
- package/dist/rpc/notifications.d.ts +4 -3
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +15 -4
- package/dist/rpc/protocol.js +1 -1
- package/dist/rpc/rpc-client.d.ts +3 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/rpc/rpc-server.d.ts +5 -1
- package/dist/rpc/server-registry.d.ts +10 -0
- package/dist/setup/activation.d.ts +7 -0
- package/dist/setup/claustrum.d.ts +26 -0
- package/dist/setup/command-runner.d.ts +2 -0
- package/dist/setup/command.d.ts +20 -0
- package/dist/setup/command.test.d.ts +1 -0
- package/dist/setup/detect.d.ts +5 -0
- package/dist/setup/jsonc.d.ts +9 -0
- package/dist/setup/native-activation.d.ts +29 -0
- package/dist/setup/native-enrollment.d.ts +26 -0
- package/dist/setup/native-enrollment.test.d.ts +1 -0
- package/dist/setup/native-local.d.ts +20 -0
- package/dist/setup/native-login.d.ts +3 -0
- package/dist/setup/native-paths.d.ts +15 -0
- package/dist/setup/native-paths.test.d.ts +1 -0
- package/dist/setup/opencode-config.d.ts +18 -0
- package/dist/setup/paths.d.ts +15 -0
- package/dist/setup/pi.d.ts +18 -0
- package/dist/setup/process-fence.d.ts +20 -0
- package/dist/setup/process-fence.test.d.ts +1 -0
- package/dist/setup/types.d.ts +37 -0
- package/dist/sidebar-state.d.ts +3 -1
- package/dist/sidebar-state.js +1 -1
- package/dist/transform.d.ts +5 -1
- package/dist/tui/command-dialogs.d.ts +32 -2
- package/dist/tui-preferences.js +2 -2
- package/dist/v2/activation.d.ts +7 -0
- package/dist/v2/adapter.d.ts +276 -0
- package/dist/v2/install.d.ts +13 -0
- package/dist/v2/lifecycle.d.ts +8 -0
- package/dist/v2/menu-contract.d.ts +8 -0
- package/dist/v2/menu.d.ts +10 -0
- package/dist/v2/models.d.ts +6 -0
- package/dist/v2/ownership.d.ts +11 -0
- package/dist/v2/prime.d.ts +12 -0
- package/dist/v2/rpc.d.ts +50 -0
- package/dist/v2/server.d.ts +8 -0
- package/dist/v2/server.js +4 -0
- package/dist/v2/setup.d.ts +58 -0
- package/dist/v2/source.d.ts +41 -0
- package/dist/v2/tui.d.ts +13 -0
- package/dist/v2/tui.js +2 -0
- package/dist/v2/wire.d.ts +36 -0
- package/package.json +26 -6
- package/src/rpc/notifications.ts +37 -26
- package/src/rpc/protocol.ts +24 -7
- package/src/rpc/rpc-client.ts +18 -1
- package/src/rpc/rpc-server.ts +38 -8
- package/src/rpc/server-registry.ts +61 -0
- package/src/sidebar-state.ts +26 -2
- package/src/tui/command-dialogs.tsx +537 -68
- package/src/tui/entry.mjs +44 -18
- package/src/tui-compiled/rpc/protocol.ts +24 -7
- package/src/tui-compiled/rpc/rpc-client.ts +18 -1
- package/src/tui-compiled/sidebar-state.ts +26 -2
- package/src/tui-compiled/tui/command-dialogs.tsx +488 -95
- package/src/tui-compiled/tui.tsx +25 -7
- package/src/tui.tsx +38 -9
- package/dist/index-8aga2zwv.js +0 -4
- package/dist/index-d3f8g6ve.js +0 -428
- package/dist/index-sh51yqxs.js +0 -2
- package/dist/logger-jrkranbz.js +0 -1
|
@@ -1,10 +1,277 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
CommandApplyRequest,
|
|
5
|
+
CommandApplyResult,
|
|
6
|
+
CommandDialogPayload,
|
|
7
|
+
CommandMenuModel,
|
|
8
|
+
KnobValue,
|
|
9
|
+
MenuAction,
|
|
10
|
+
MenuItem,
|
|
11
|
+
MenuSection,
|
|
12
|
+
} from '@cortexkit/anthropic-auth-core'
|
|
13
|
+
import {
|
|
14
|
+
custodyStatusLabel,
|
|
15
|
+
type PrimeAccountStatus,
|
|
16
|
+
} from '@cortexkit/anthropic-auth-core'
|
|
3
17
|
import type { TuiPluginApi } from '@opencode-ai/plugin/tui'
|
|
4
18
|
import type { AccountDialogAccount } from '../rpc/protocol'
|
|
5
19
|
import type { OpenDialogPayload } from '../rpc/protocol.js'
|
|
6
20
|
import { formatPrimeCost, formatPrimeTime } from '../sidebar-state.js'
|
|
7
21
|
|
|
22
|
+
/** Render menu sections, accounts and action inputs; send changes to the host instead of writing credentials here. */
|
|
23
|
+
export function openNativeMenuDialog(
|
|
24
|
+
api: {
|
|
25
|
+
readonly ui: Pick<
|
|
26
|
+
TuiPluginApi['ui'],
|
|
27
|
+
'dialog' | 'DialogSelect' | 'DialogPrompt' | 'DialogConfirm' | 'toast'
|
|
28
|
+
>
|
|
29
|
+
},
|
|
30
|
+
payload: CommandDialogPayload,
|
|
31
|
+
apply: (request: CommandApplyRequest) => Promise<CommandApplyResult>,
|
|
32
|
+
isCurrentSession: () => boolean,
|
|
33
|
+
): void {
|
|
34
|
+
let menu: CommandMenuModel = payload.menu
|
|
35
|
+
let inFlight = false
|
|
36
|
+
const DialogSelect = api.ui.DialogSelect<string>
|
|
37
|
+
const DialogPrompt = api.ui.DialogPrompt
|
|
38
|
+
const DialogConfirm = api.ui.DialogConfirm
|
|
39
|
+
const active = () => isCurrentSession() && !inFlight
|
|
40
|
+
|
|
41
|
+
const root = () => {
|
|
42
|
+
if (!active()) return
|
|
43
|
+
api.ui.dialog.setSize('xlarge')
|
|
44
|
+
api.ui.dialog.replace(() => (
|
|
45
|
+
<DialogSelect
|
|
46
|
+
title={menu.title}
|
|
47
|
+
options={[
|
|
48
|
+
...menu.sections.map((section) => ({
|
|
49
|
+
title: section.title,
|
|
50
|
+
value: section.id,
|
|
51
|
+
})),
|
|
52
|
+
{ title: 'Close', value: '__close' },
|
|
53
|
+
]}
|
|
54
|
+
onSelect={(option) => {
|
|
55
|
+
if (!active()) return
|
|
56
|
+
const section = menu.sections.find(
|
|
57
|
+
(entry) => entry.id === option.value,
|
|
58
|
+
)
|
|
59
|
+
if (section) showSection(section)
|
|
60
|
+
else if (option.value === '__close') api.ui.dialog.clear()
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const showSection = (section: MenuSection) => {
|
|
67
|
+
if (!active()) return
|
|
68
|
+
api.ui.dialog.replace(() => (
|
|
69
|
+
<box flexDirection='column' padding={1}>
|
|
70
|
+
<text>{section.lines.join('\n')}</text>
|
|
71
|
+
<DialogSelect
|
|
72
|
+
title={section.title}
|
|
73
|
+
options={[
|
|
74
|
+
...section.items.map((item) => ({
|
|
75
|
+
title: item.label,
|
|
76
|
+
value: `item:${item.id}`,
|
|
77
|
+
description: item.detail,
|
|
78
|
+
})),
|
|
79
|
+
...section.actions.map((action) => ({
|
|
80
|
+
title: action.label,
|
|
81
|
+
value: `action:${action.id}`,
|
|
82
|
+
description: action.description,
|
|
83
|
+
})),
|
|
84
|
+
{ title: 'Back', value: '__back' },
|
|
85
|
+
]}
|
|
86
|
+
onSelect={(option) => {
|
|
87
|
+
if (!active()) return
|
|
88
|
+
if (option.value === '__back') return root()
|
|
89
|
+
const action = section.actions.find(
|
|
90
|
+
(entry) => `action:${entry.id}` === option.value,
|
|
91
|
+
)
|
|
92
|
+
if (action) return collect(section, action)
|
|
93
|
+
const item = section.items.find(
|
|
94
|
+
(entry) => `item:${entry.id}` === option.value,
|
|
95
|
+
)
|
|
96
|
+
if (item) showItem(section, item)
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
</box>
|
|
100
|
+
))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const showItem = (section: MenuSection, item: MenuItem) => {
|
|
104
|
+
if (!active()) return
|
|
105
|
+
api.ui.dialog.replace(() => (
|
|
106
|
+
<box flexDirection='column' padding={1}>
|
|
107
|
+
<text>{item.detail ?? ''}</text>
|
|
108
|
+
<DialogSelect
|
|
109
|
+
title={item.label}
|
|
110
|
+
options={[
|
|
111
|
+
...item.actions.map((action) => ({
|
|
112
|
+
title: action.label,
|
|
113
|
+
value: action.id,
|
|
114
|
+
description: action.description,
|
|
115
|
+
})),
|
|
116
|
+
{ title: 'Back', value: '__back' },
|
|
117
|
+
]}
|
|
118
|
+
onSelect={(option) => {
|
|
119
|
+
if (!active()) return
|
|
120
|
+
const action = item.actions.find(
|
|
121
|
+
(entry) => entry.id === option.value,
|
|
122
|
+
)
|
|
123
|
+
if (action) collect(section, action, item)
|
|
124
|
+
else if (option.value === '__back') showSection(section)
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
</box>
|
|
128
|
+
))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const collect = (
|
|
132
|
+
section: MenuSection,
|
|
133
|
+
action: MenuAction,
|
|
134
|
+
item?: MenuItem,
|
|
135
|
+
) => {
|
|
136
|
+
const values: Record<string, KnobValue> = {}
|
|
137
|
+
const back = () => {
|
|
138
|
+
for (const key of Object.keys(values)) delete values[key]
|
|
139
|
+
if (item) showItem(section, item)
|
|
140
|
+
else showSection(section)
|
|
141
|
+
}
|
|
142
|
+
const submit = async () => {
|
|
143
|
+
if (!active()) return
|
|
144
|
+
inFlight = true
|
|
145
|
+
try {
|
|
146
|
+
const result = await apply({
|
|
147
|
+
command: menu.command,
|
|
148
|
+
sectionId: section.id,
|
|
149
|
+
actionId: action.id,
|
|
150
|
+
...(item ? { itemId: item.id } : {}),
|
|
151
|
+
values: { ...values },
|
|
152
|
+
...(action.confirm ? { confirmed: true } : {}),
|
|
153
|
+
})
|
|
154
|
+
if (!isCurrentSession()) return
|
|
155
|
+
menu = result.menu
|
|
156
|
+
api.ui.toast({ message: result.text })
|
|
157
|
+
} catch {
|
|
158
|
+
if (isCurrentSession())
|
|
159
|
+
api.ui.toast({ message: 'Menu apply unavailable; outcome unknown.' })
|
|
160
|
+
} finally {
|
|
161
|
+
for (const key of Object.keys(values)) delete values[key]
|
|
162
|
+
inFlight = false
|
|
163
|
+
if (isCurrentSession()) {
|
|
164
|
+
const refreshed = menu.sections.find(
|
|
165
|
+
(entry) => entry.id === section.id,
|
|
166
|
+
)
|
|
167
|
+
if (refreshed) showSection(refreshed)
|
|
168
|
+
else root()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const next = (index: number) => {
|
|
173
|
+
if (!active()) return
|
|
174
|
+
const knob = action.knobs[index]
|
|
175
|
+
if (!knob) {
|
|
176
|
+
if (!action.confirm) return void submit()
|
|
177
|
+
api.ui.dialog.replace(() => (
|
|
178
|
+
<DialogConfirm
|
|
179
|
+
title={action.label}
|
|
180
|
+
message={action.confirm?.message ?? ''}
|
|
181
|
+
onConfirm={() => void submit()}
|
|
182
|
+
onCancel={back}
|
|
183
|
+
/>
|
|
184
|
+
))
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
if (knob.kind === 'choice' || knob.kind === 'toggle') {
|
|
188
|
+
const choices =
|
|
189
|
+
knob.kind === 'choice'
|
|
190
|
+
? knob.choices
|
|
191
|
+
: [
|
|
192
|
+
{ value: 'on', label: 'On' },
|
|
193
|
+
{ value: 'off', label: 'Off' },
|
|
194
|
+
]
|
|
195
|
+
api.ui.dialog.replace(() => (
|
|
196
|
+
<DialogSelect
|
|
197
|
+
title={knob.label}
|
|
198
|
+
options={[
|
|
199
|
+
...choices.map((choice) => ({
|
|
200
|
+
title: choice.label,
|
|
201
|
+
value: choice.value,
|
|
202
|
+
})),
|
|
203
|
+
{ title: 'Cancel', value: '__cancel' },
|
|
204
|
+
]}
|
|
205
|
+
onSelect={(option) => {
|
|
206
|
+
if (!active()) return
|
|
207
|
+
if (option.value === '__cancel') return back()
|
|
208
|
+
values[knob.id] =
|
|
209
|
+
knob.kind === 'toggle' ? option.value === 'on' : option.value
|
|
210
|
+
next(index + 1)
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
))
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
// DialogPrompt has no masking capability in the host API. Never seed a
|
|
217
|
+
// protected input or claim it is hidden; warn before collecting it.
|
|
218
|
+
api.ui.dialog.replace(() => (
|
|
219
|
+
<DialogPrompt
|
|
220
|
+
title={knob.label}
|
|
221
|
+
description={() => (
|
|
222
|
+
<text>
|
|
223
|
+
{knob.kind === 'text' && knob.masked
|
|
224
|
+
? 'This host prompt displays input. Avoid screen sharing; the value is not echoed in menu results.'
|
|
225
|
+
: knob.required
|
|
226
|
+
? 'Required input'
|
|
227
|
+
: 'Optional; leave empty for the default'}
|
|
228
|
+
</text>
|
|
229
|
+
)}
|
|
230
|
+
value={
|
|
231
|
+
knob.kind === 'text' && knob.masked
|
|
232
|
+
? ''
|
|
233
|
+
: knob.value === undefined
|
|
234
|
+
? ''
|
|
235
|
+
: String(knob.value)
|
|
236
|
+
}
|
|
237
|
+
placeholder={
|
|
238
|
+
knob.kind === 'text'
|
|
239
|
+
? knob.placeholder
|
|
240
|
+
: `${knob.min ?? ''}-${knob.max ?? ''}`
|
|
241
|
+
}
|
|
242
|
+
onCancel={back}
|
|
243
|
+
onConfirm={(typed) => {
|
|
244
|
+
if (!active()) return
|
|
245
|
+
const text = typed.trim()
|
|
246
|
+
if (!text && knob.required) {
|
|
247
|
+
api.ui.toast({ message: `${knob.label} is required.` })
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
const value =
|
|
251
|
+
text === '' ? null : knob.kind === 'number' ? Number(text) : text
|
|
252
|
+
if (
|
|
253
|
+
knob.kind === 'number' &&
|
|
254
|
+
typeof value === 'number' &&
|
|
255
|
+
(!Number.isFinite(value) ||
|
|
256
|
+
(knob.min !== undefined && value < knob.min) ||
|
|
257
|
+
(knob.max !== undefined && value > knob.max))
|
|
258
|
+
) {
|
|
259
|
+
api.ui.toast({
|
|
260
|
+
message: `${knob.label} needs a number in range.`,
|
|
261
|
+
})
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
values[knob.id] = value
|
|
265
|
+
next(index + 1)
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
))
|
|
269
|
+
}
|
|
270
|
+
next(0)
|
|
271
|
+
}
|
|
272
|
+
root()
|
|
273
|
+
}
|
|
274
|
+
|
|
8
275
|
type ApplyFn = (
|
|
9
276
|
command: OpenDialogPayload['command'],
|
|
10
277
|
args: string,
|
|
@@ -16,6 +283,27 @@ type KillswitchDialogConfig = {
|
|
|
16
283
|
accounts?: Record<string, Record<string, number>>
|
|
17
284
|
}
|
|
18
285
|
|
|
286
|
+
type TuiAccountDialogAccount = Omit<
|
|
287
|
+
AccountDialogAccount,
|
|
288
|
+
'vaultReauth' | 'custodyState'
|
|
289
|
+
> &
|
|
290
|
+
Partial<Pick<AccountDialogAccount, 'vaultReauth' | 'custodyState'>>
|
|
291
|
+
|
|
292
|
+
type TuiAccountDialogPayload = {
|
|
293
|
+
accounts: TuiAccountDialogAccount[]
|
|
294
|
+
claustrumDetection: string
|
|
295
|
+
custodyMode?: TuiCustodyMode
|
|
296
|
+
enrollmentStatus?: string
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
type TuiCustodyMode = 'local' | 'claustrum' | `mismatch: ${string}`
|
|
300
|
+
|
|
301
|
+
type AccountDialogOption = {
|
|
302
|
+
title: string
|
|
303
|
+
value: string
|
|
304
|
+
description?: string
|
|
305
|
+
}
|
|
306
|
+
|
|
19
307
|
export const PRIME_DIALOG_OPTIONS = [
|
|
20
308
|
{ title: 'Enable', value: 'on' },
|
|
21
309
|
{ title: 'Disable', value: 'off' },
|
|
@@ -49,24 +337,189 @@ export function buildKillswitchThresholdSeed(
|
|
|
49
337
|
return seedParts.join(' ')
|
|
50
338
|
}
|
|
51
339
|
|
|
52
|
-
|
|
340
|
+
function normalizeTuiCustodyMode(value: string): TuiCustodyMode {
|
|
341
|
+
return value === 'local' || value === 'claustrum'
|
|
342
|
+
? value
|
|
343
|
+
: `mismatch: ${value}`
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function normalizeAccountDialogPayload(
|
|
347
|
+
value: unknown,
|
|
348
|
+
): TuiAccountDialogPayload {
|
|
349
|
+
const payload =
|
|
350
|
+
value && typeof value === 'object' ? (value as Record<string, unknown>) : {}
|
|
351
|
+
const rawAccounts = Array.isArray(payload.accounts) ? payload.accounts : []
|
|
352
|
+
const accounts = rawAccounts.flatMap((value) => {
|
|
353
|
+
if (!value || typeof value !== 'object') return []
|
|
354
|
+
const account = value as Record<string, unknown>
|
|
355
|
+
const quotaPercent = account.quotaPercent
|
|
356
|
+
if (
|
|
357
|
+
typeof account.id !== 'string' ||
|
|
358
|
+
typeof account.label !== 'string' ||
|
|
359
|
+
(account.role !== 'main' && account.role !== 'fallback') ||
|
|
360
|
+
typeof account.enabled !== 'boolean' ||
|
|
361
|
+
(quotaPercent !== null &&
|
|
362
|
+
(typeof quotaPercent !== 'number' || !Number.isFinite(quotaPercent))) ||
|
|
363
|
+
(account.claustrumGate !== 'on' &&
|
|
364
|
+
account.claustrumGate !== 'off' &&
|
|
365
|
+
account.claustrumGate !== 'na') ||
|
|
366
|
+
typeof account.vaultServed !== 'boolean'
|
|
367
|
+
) {
|
|
368
|
+
return []
|
|
369
|
+
}
|
|
370
|
+
const normalized = {
|
|
371
|
+
id: account.id,
|
|
372
|
+
label: account.label,
|
|
373
|
+
role: account.role,
|
|
374
|
+
enabled: account.enabled,
|
|
375
|
+
quotaPercent,
|
|
376
|
+
...(typeof account.tierLabel === 'string' && {
|
|
377
|
+
tierLabel: account.tierLabel,
|
|
378
|
+
}),
|
|
379
|
+
claustrumGate: account.claustrumGate,
|
|
380
|
+
vaultServed: account.vaultServed,
|
|
381
|
+
} satisfies TuiAccountDialogAccount
|
|
382
|
+
const custodyState = account.custodyState
|
|
383
|
+
if (
|
|
384
|
+
(custodyState === 'na' ||
|
|
385
|
+
custodyState === 'off' ||
|
|
386
|
+
custodyState === 'on-vault-served' ||
|
|
387
|
+
custodyState === 'on-vault-reauth' ||
|
|
388
|
+
custodyState === 'on-cold') &&
|
|
389
|
+
typeof account.vaultReauth === 'boolean'
|
|
390
|
+
) {
|
|
391
|
+
return [
|
|
392
|
+
{
|
|
393
|
+
...normalized,
|
|
394
|
+
custodyState,
|
|
395
|
+
vaultReauth: account.vaultReauth,
|
|
396
|
+
},
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
return [normalized]
|
|
400
|
+
})
|
|
401
|
+
const custodyMode: TuiCustodyMode | undefined =
|
|
402
|
+
payload.custodyModeKnown === true && typeof payload.custodyMode === 'string'
|
|
403
|
+
? normalizeTuiCustodyMode(payload.custodyMode)
|
|
404
|
+
: undefined
|
|
405
|
+
return {
|
|
406
|
+
accounts,
|
|
407
|
+
claustrumDetection:
|
|
408
|
+
typeof payload.claustrumDetection === 'string'
|
|
409
|
+
? payload.claustrumDetection
|
|
410
|
+
: 'unknown',
|
|
411
|
+
...(custodyMode && { custodyMode }),
|
|
412
|
+
...(typeof payload.enrollmentStatus === 'string' &&
|
|
413
|
+
payload.enrollmentStatus.length <= 2_000 && {
|
|
414
|
+
enrollmentStatus: payload.enrollmentStatus,
|
|
415
|
+
}),
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function buildAccountDialogOption(account: TuiAccountDialogAccount) {
|
|
53
420
|
const pct =
|
|
54
421
|
account.quotaPercent != null
|
|
55
422
|
? ` ${Math.round(account.quotaPercent)}%`
|
|
56
423
|
: ' \u2013%'
|
|
57
424
|
const status = !account.enabled ? ' (disabled)' : ''
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
425
|
+
const custody = account.custodyState
|
|
426
|
+
? account.custodyState === 'na'
|
|
427
|
+
? undefined
|
|
428
|
+
: custodyStatusLabel(account.custodyState).replace(
|
|
429
|
+
' (OpenCode managed)',
|
|
430
|
+
'',
|
|
431
|
+
)
|
|
432
|
+
: undefined
|
|
63
433
|
return {
|
|
64
|
-
title: `${account.label} [${account.role}]${status}${pct}${
|
|
434
|
+
title: `${account.label} [${account.role}]${status}${pct}${custody ? ` · custody ${custody}` : ''}`,
|
|
65
435
|
value: account.id,
|
|
66
436
|
...(account.tierLabel && { description: account.tierLabel }),
|
|
67
437
|
}
|
|
68
438
|
}
|
|
69
439
|
|
|
440
|
+
export function buildManageAccountOptions(account: TuiAccountDialogAccount) {
|
|
441
|
+
const options: AccountDialogOption[] = []
|
|
442
|
+
const toggleLabel = account.enabled ? 'Disable' : 'Enable'
|
|
443
|
+
options.push({
|
|
444
|
+
title: toggleLabel,
|
|
445
|
+
value: account.enabled ? 'disable' : 'enable',
|
|
446
|
+
description: account.enabled
|
|
447
|
+
? 'Stop using this fallback account'
|
|
448
|
+
: 'Allow this fallback account to be used',
|
|
449
|
+
})
|
|
450
|
+
options.push({
|
|
451
|
+
title: 'Move up',
|
|
452
|
+
value: 'move-up',
|
|
453
|
+
description: 'Higher priority in fallback order',
|
|
454
|
+
})
|
|
455
|
+
options.push({
|
|
456
|
+
title: 'Move down',
|
|
457
|
+
value: 'move-down',
|
|
458
|
+
description: 'Lower priority in fallback order',
|
|
459
|
+
})
|
|
460
|
+
options.push({
|
|
461
|
+
title: 'Remove\u2026',
|
|
462
|
+
value: 'remove',
|
|
463
|
+
description: 'Delete this account permanently',
|
|
464
|
+
})
|
|
465
|
+
options.push({ title: 'Back', value: 'back' })
|
|
466
|
+
return options
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function buildAccountDialogL1(value: unknown): {
|
|
470
|
+
header: string
|
|
471
|
+
options: AccountDialogOption[]
|
|
472
|
+
enrollmentStatus?: string
|
|
473
|
+
modeAction?: { command: 'claude-account'; arguments: 'local' | 'claustrum' }
|
|
474
|
+
} {
|
|
475
|
+
const payload = normalizeAccountDialogPayload(value)
|
|
476
|
+
const modeAction =
|
|
477
|
+
payload.custodyMode === 'local' || payload.custodyMode === 'claustrum'
|
|
478
|
+
? {
|
|
479
|
+
command: 'claude-account' as const,
|
|
480
|
+
arguments:
|
|
481
|
+
payload.custodyMode === 'local'
|
|
482
|
+
? ('claustrum' as const)
|
|
483
|
+
: ('local' as const),
|
|
484
|
+
}
|
|
485
|
+
: undefined
|
|
486
|
+
return {
|
|
487
|
+
header: payload.custodyMode
|
|
488
|
+
? `Custody mode: ${payload.custodyMode}`
|
|
489
|
+
: 'Custody mode: unavailable from older server',
|
|
490
|
+
options: [
|
|
491
|
+
...(modeAction
|
|
492
|
+
? [
|
|
493
|
+
{
|
|
494
|
+
title:
|
|
495
|
+
payload.custodyMode === 'local'
|
|
496
|
+
? 'Use Claustrum custody'
|
|
497
|
+
: 'Use local custody',
|
|
498
|
+
value: '__custody-mode__',
|
|
499
|
+
},
|
|
500
|
+
]
|
|
501
|
+
: []),
|
|
502
|
+
{
|
|
503
|
+
title: 'Add account…',
|
|
504
|
+
value: '__add__',
|
|
505
|
+
description: 'Add an API key or OAuth fallback account',
|
|
506
|
+
},
|
|
507
|
+
...payload.accounts.map(buildAccountDialogOption),
|
|
508
|
+
],
|
|
509
|
+
...(payload.enrollmentStatus && {
|
|
510
|
+
enrollmentStatus: payload.enrollmentStatus,
|
|
511
|
+
}),
|
|
512
|
+
...(modeAction && { modeAction }),
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export function retainAccountDialogProjection(
|
|
517
|
+
current: Record<string, unknown>,
|
|
518
|
+
incoming: Record<string, unknown>,
|
|
519
|
+
): Record<string, unknown> {
|
|
520
|
+
return Array.isArray(incoming.accounts) ? incoming : current
|
|
521
|
+
}
|
|
522
|
+
|
|
70
523
|
function showText(api: TuiPluginApi, text: string) {
|
|
71
524
|
api.ui.dialog.setSize('xlarge')
|
|
72
525
|
api.ui.dialog.replace(() => (
|
|
@@ -99,6 +552,8 @@ export function buildPrimeStatusRows(accounts: PrimeAccountStatus[]): string[] {
|
|
|
99
552
|
const time = formatPrimeTime(account.lastPrimedAt)
|
|
100
553
|
if (account.lastResult === 'error') {
|
|
101
554
|
rows.push(`${account.label} \u00b7 primed ${time} err`)
|
|
555
|
+
} else if (account.lastResult === 'skipped') {
|
|
556
|
+
rows.push(`${account.label} \u00b7 primed ${time} skipped`)
|
|
102
557
|
} else {
|
|
103
558
|
rows.push(`${account.label} \u00b7 primed ${time} \u2713`)
|
|
104
559
|
}
|
|
@@ -377,17 +832,17 @@ export function openCommandDialog(
|
|
|
377
832
|
}
|
|
378
833
|
|
|
379
834
|
if (payload.command === 'claude-account') {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
(payload.knobs.claustrumDetection as string | undefined) ?? 'unknown'
|
|
835
|
+
let accountKnobs: Record<string, unknown> = payload.knobs
|
|
836
|
+
const accounts = normalizeAccountDialogPayload(accountKnobs).accounts
|
|
837
|
+
let statusMessage: { text: string; error?: boolean } | undefined
|
|
384
838
|
|
|
385
839
|
const updateAccounts = (r: {
|
|
386
840
|
text: string
|
|
387
841
|
knobs: Record<string, unknown>
|
|
388
842
|
}) => {
|
|
389
|
-
|
|
390
|
-
|
|
843
|
+
accountKnobs = retainAccountDialogProjection(accountKnobs, r.knobs)
|
|
844
|
+
const updated = normalizeAccountDialogPayload(accountKnobs).accounts
|
|
845
|
+
if (updated.length > 0) {
|
|
391
846
|
accounts.length = 0
|
|
392
847
|
accounts.push(...updated)
|
|
393
848
|
}
|
|
@@ -395,31 +850,61 @@ export function openCommandDialog(
|
|
|
395
850
|
|
|
396
851
|
const buildL1 = () => {
|
|
397
852
|
const DialogSelect = api.ui.DialogSelect<string>
|
|
398
|
-
const
|
|
399
|
-
title: string
|
|
400
|
-
value: string
|
|
401
|
-
description?: string
|
|
402
|
-
}> = [
|
|
403
|
-
{
|
|
404
|
-
title: 'Add account\u2026',
|
|
405
|
-
value: '__add__',
|
|
406
|
-
description: 'Add an API key or OAuth fallback account',
|
|
407
|
-
},
|
|
408
|
-
...accounts.map(buildAccountDialogOption),
|
|
409
|
-
]
|
|
853
|
+
const l1 = buildAccountDialogL1({ ...accountKnobs, accounts })
|
|
410
854
|
api.ui.dialog.setSize('xlarge')
|
|
411
855
|
api.ui.dialog.replace(() => (
|
|
412
856
|
<box flexDirection='column' padding={1} width='100%'>
|
|
413
|
-
<text>{`Claustrum: ${claustrumDetection}`}</text>
|
|
857
|
+
<text>{`Claustrum: ${normalizeAccountDialogPayload(accountKnobs).claustrumDetection}`}</text>
|
|
858
|
+
<text>{l1.header}</text>
|
|
859
|
+
{l1.enrollmentStatus && (
|
|
860
|
+
<box marginTop={1}>
|
|
861
|
+
<text>{l1.enrollmentStatus}</text>
|
|
862
|
+
</box>
|
|
863
|
+
)}
|
|
864
|
+
{statusMessage && (
|
|
865
|
+
<box marginTop={1}>
|
|
866
|
+
<text fg={statusMessage.error ? '#ef4444' : '#22c55e'}>
|
|
867
|
+
{statusMessage.text}
|
|
868
|
+
</text>
|
|
869
|
+
</box>
|
|
870
|
+
)}
|
|
414
871
|
<box marginTop={1}>
|
|
415
872
|
<DialogSelect
|
|
416
873
|
title='Claude accounts'
|
|
417
|
-
options={
|
|
874
|
+
options={l1.options}
|
|
418
875
|
onSelect={(option) => {
|
|
419
876
|
if (option.value === '__add__') {
|
|
877
|
+
statusMessage = undefined
|
|
420
878
|
openAddType()
|
|
421
879
|
return
|
|
422
880
|
}
|
|
881
|
+
if (option.value === '__custody-mode__' && l1.modeAction) {
|
|
882
|
+
void apply(l1.modeAction.command, l1.modeAction.arguments)
|
|
883
|
+
.then((r) => {
|
|
884
|
+
const isRefusalOrError =
|
|
885
|
+
r.text.includes('refused') ||
|
|
886
|
+
r.text.includes('unavailable') ||
|
|
887
|
+
r.text.includes('failed') ||
|
|
888
|
+
r.text.includes('Error') ||
|
|
889
|
+
r.text.includes('error')
|
|
890
|
+
if (isRefusalOrError) {
|
|
891
|
+
statusMessage = { text: r.text, error: true }
|
|
892
|
+
} else {
|
|
893
|
+
statusMessage = { text: r.text, error: false }
|
|
894
|
+
api.ui.toast({ message: r.text })
|
|
895
|
+
}
|
|
896
|
+
updateAccounts(r)
|
|
897
|
+
buildL1()
|
|
898
|
+
})
|
|
899
|
+
.catch((err) => {
|
|
900
|
+
const msg =
|
|
901
|
+
err instanceof Error ? err.message : String(err)
|
|
902
|
+
statusMessage = { text: `Error: ${msg}`, error: true }
|
|
903
|
+
buildL1()
|
|
904
|
+
})
|
|
905
|
+
return
|
|
906
|
+
}
|
|
907
|
+
statusMessage = undefined
|
|
423
908
|
const account = accounts.find((a) => a.id === option.value)
|
|
424
909
|
if (!account) return
|
|
425
910
|
if (account.role === 'main') {
|
|
@@ -737,37 +1222,9 @@ export function openCommandDialog(
|
|
|
737
1222
|
const DialogConfirm = api.ui.DialogConfirm
|
|
738
1223
|
api.ui.dialog.setSize('xlarge')
|
|
739
1224
|
|
|
740
|
-
const options
|
|
741
|
-
title:
|
|
742
|
-
|
|
743
|
-
description?: string
|
|
744
|
-
}> = []
|
|
745
|
-
if (!isMain) {
|
|
746
|
-
const toggleLabel = account.enabled ? 'Disable' : 'Enable'
|
|
747
|
-
options.push({
|
|
748
|
-
title: toggleLabel,
|
|
749
|
-
value: account.enabled ? 'disable' : 'enable',
|
|
750
|
-
description: account.enabled
|
|
751
|
-
? 'Stop using this fallback account'
|
|
752
|
-
: 'Allow this fallback account to be used',
|
|
753
|
-
})
|
|
754
|
-
options.push({
|
|
755
|
-
title: 'Move up',
|
|
756
|
-
value: 'move-up',
|
|
757
|
-
description: 'Higher priority in fallback order',
|
|
758
|
-
})
|
|
759
|
-
options.push({
|
|
760
|
-
title: 'Move down',
|
|
761
|
-
value: 'move-down',
|
|
762
|
-
description: 'Lower priority in fallback order',
|
|
763
|
-
})
|
|
764
|
-
options.push({
|
|
765
|
-
title: 'Remove\u2026',
|
|
766
|
-
value: 'remove',
|
|
767
|
-
description: 'Delete this account permanently',
|
|
768
|
-
})
|
|
769
|
-
}
|
|
770
|
-
options.push({ title: 'Back', value: 'back' })
|
|
1225
|
+
const options = isMain
|
|
1226
|
+
? [{ title: 'Back', value: 'back' }]
|
|
1227
|
+
: buildManageAccountOptions(account)
|
|
771
1228
|
|
|
772
1229
|
api.ui.dialog.replace(() => (
|
|
773
1230
|
<DialogSelect
|
|
@@ -785,13 +1242,18 @@ export function openCommandDialog(
|
|
|
785
1242
|
title={`Remove ${account.label}?`}
|
|
786
1243
|
message={`Are you sure you want to remove the fallback account "${account.label}"?`}
|
|
787
1244
|
onConfirm={() => {
|
|
788
|
-
void apply('claude-account', `remove ${account.id}`)
|
|
789
|
-
(r) => {
|
|
1245
|
+
void apply('claude-account', `remove ${account.id}`)
|
|
1246
|
+
.then((r) => {
|
|
790
1247
|
api.ui.toast({ message: r.text })
|
|
791
1248
|
updateAccounts(r)
|
|
792
1249
|
buildL1()
|
|
793
|
-
}
|
|
794
|
-
|
|
1250
|
+
})
|
|
1251
|
+
.catch((err) => {
|
|
1252
|
+
const msg =
|
|
1253
|
+
err instanceof Error ? err.message : String(err)
|
|
1254
|
+
statusMessage = { text: `Error: ${msg}`, error: true }
|
|
1255
|
+
buildL1()
|
|
1256
|
+
})
|
|
795
1257
|
}}
|
|
796
1258
|
onCancel={() => openManage(account, isMain)}
|
|
797
1259
|
/>
|
|
@@ -799,18 +1261,25 @@ export function openCommandDialog(
|
|
|
799
1261
|
return
|
|
800
1262
|
}
|
|
801
1263
|
|
|
802
|
-
|
|
803
|
-
|
|
1264
|
+
const args = `${option.value} ${account.id}`
|
|
1265
|
+
void apply('claude-account', args)
|
|
1266
|
+
.then((r) => {
|
|
804
1267
|
api.ui.toast({ message: r.text })
|
|
805
1268
|
updateAccounts(r)
|
|
806
|
-
const updatedList =
|
|
1269
|
+
const updatedList = normalizeAccountDialogPayload(
|
|
1270
|
+
r.knobs,
|
|
1271
|
+
).accounts
|
|
807
1272
|
const refreshed =
|
|
808
1273
|
(updatedList && updatedList.length > 0
|
|
809
1274
|
? updatedList.find((a) => a.id === account.id)
|
|
810
1275
|
: undefined) ?? account
|
|
811
1276
|
openManage(refreshed, isMain)
|
|
812
|
-
}
|
|
813
|
-
|
|
1277
|
+
})
|
|
1278
|
+
.catch((err) => {
|
|
1279
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
1280
|
+
statusMessage = { text: `Error: ${msg}`, error: true }
|
|
1281
|
+
buildL1()
|
|
1282
|
+
})
|
|
814
1283
|
}}
|
|
815
1284
|
/>
|
|
816
1285
|
))
|