@cortexkit/opencode-openai-auth 0.11.0 → 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
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
|
|
3
|
+
// The `/openai` drawer: one generic renderer for the shared command menu's
|
|
4
|
+
// payload. The payload carries everything drawn here (sections with their
|
|
5
|
+
// lines, items, facts and actions; each action's typed inputs with their
|
|
6
|
+
// current values and its confirmation), so the drawer knows nothing about
|
|
7
|
+
// any one section. After every apply it redraws from the refreshed menu the
|
|
8
|
+
// result carries.
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
TuiDialogSelectOption,
|
|
12
|
+
TuiPluginApi,
|
|
13
|
+
} from "@opencode-ai/plugin/tui"
|
|
14
|
+
import { createLogger } from "../logger.js"
|
|
15
|
+
import type {
|
|
16
|
+
ApplyRequest,
|
|
17
|
+
ApplyResult,
|
|
18
|
+
OpenDialogPayload,
|
|
19
|
+
} from "../rpc/protocol.js"
|
|
20
|
+
|
|
21
|
+
const log = createLogger('rpc-tui')
|
|
22
|
+
|
|
23
|
+
type Menu = OpenDialogPayload['menu']
|
|
24
|
+
type Section = Menu['sections'][number]
|
|
25
|
+
type Item = Section['items'][number]
|
|
26
|
+
type Action = Section['actions'][number]
|
|
27
|
+
type Knob = Action['knobs'][number]
|
|
28
|
+
type KnobValue = string | number | boolean | null
|
|
29
|
+
|
|
30
|
+
/** Sends one action to the plugin; the session id is added by the caller. */
|
|
31
|
+
export type ApplyFn = (
|
|
32
|
+
request: Omit<ApplyRequest, 'sessionId'>,
|
|
33
|
+
) => Promise<ApplyResult>
|
|
34
|
+
|
|
35
|
+
export type DrawerOption = Omit<
|
|
36
|
+
TuiDialogSelectOption<string>,
|
|
37
|
+
'disabled' | 'onSelect'
|
|
38
|
+
>
|
|
39
|
+
|
|
40
|
+
const BACK = 'back'
|
|
41
|
+
|
|
42
|
+
/** One option per section, in the order the menu gives them. */
|
|
43
|
+
export function sectionListOptions(menu: Menu): DrawerOption[] {
|
|
44
|
+
return menu.sections.map((section) => ({
|
|
45
|
+
title: section.title,
|
|
46
|
+
value: `section:${section.id}`,
|
|
47
|
+
...(section.lines[0] ? { description: section.lines[0] } : {}),
|
|
48
|
+
}))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function factLines(facts: Record<string, unknown> | undefined): string[] {
|
|
52
|
+
return Object.entries(facts ?? {}).map(
|
|
53
|
+
([name, value]) =>
|
|
54
|
+
`${name}: ${typeof value === 'string' ? value : JSON.stringify(value)}`,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Read-only text is a category header; every option navigates or runs an action. */
|
|
59
|
+
export function sectionOptions(section: Section): DrawerOption[] {
|
|
60
|
+
const header = [
|
|
61
|
+
...section.lines,
|
|
62
|
+
...factLines(section.facts),
|
|
63
|
+
...section.items
|
|
64
|
+
.filter((item) => item.actions.length === 0)
|
|
65
|
+
.map((item) =>
|
|
66
|
+
[
|
|
67
|
+
item.group,
|
|
68
|
+
[item.label, item.status].filter(Boolean).join(' · '),
|
|
69
|
+
item.detail,
|
|
70
|
+
...factLines(item.facts),
|
|
71
|
+
]
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.join('\n'),
|
|
74
|
+
),
|
|
75
|
+
]
|
|
76
|
+
const options: DrawerOption[] = [
|
|
77
|
+
...section.items
|
|
78
|
+
.filter((item) => item.actions.length > 0)
|
|
79
|
+
.map((item) => ({
|
|
80
|
+
title: item.label,
|
|
81
|
+
value: `item:${item.id}`,
|
|
82
|
+
...(item.detail ? { description: item.detail } : {}),
|
|
83
|
+
...(item.group ? { category: item.group } : {}),
|
|
84
|
+
...(item.status ? { footer: item.status } : {}),
|
|
85
|
+
})),
|
|
86
|
+
...section.actions.map((action) => ({
|
|
87
|
+
title: action.label,
|
|
88
|
+
value: `action:${action.id}`,
|
|
89
|
+
...(action.description ? { description: action.description } : {}),
|
|
90
|
+
...(action.group ? { category: action.group } : {}),
|
|
91
|
+
})),
|
|
92
|
+
{ title: 'Back', value: BACK },
|
|
93
|
+
]
|
|
94
|
+
const first = options[0]
|
|
95
|
+
if (first && header.length > 0) {
|
|
96
|
+
const category = first.category
|
|
97
|
+
const text = [...header, category].filter(Boolean).join('\n')
|
|
98
|
+
for (const option of options) {
|
|
99
|
+
if (option.category !== category) break
|
|
100
|
+
option.category = text
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return options
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Item context is secondary text, never an option that cannot be acted on. */
|
|
107
|
+
export function itemOptions(item: Item): DrawerOption[] {
|
|
108
|
+
const detail = [item.detail, ...factLines(item.facts)]
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join(' · ')
|
|
111
|
+
return [
|
|
112
|
+
...item.actions.map((action) => ({
|
|
113
|
+
title: action.label,
|
|
114
|
+
value: `action:${action.id}`,
|
|
115
|
+
...(detail || action.description
|
|
116
|
+
? {
|
|
117
|
+
description: [detail, action.description]
|
|
118
|
+
.filter(Boolean)
|
|
119
|
+
.join(' · '),
|
|
120
|
+
}
|
|
121
|
+
: {}),
|
|
122
|
+
...(action.group ? { category: action.group } : {}),
|
|
123
|
+
})),
|
|
124
|
+
{ title: 'Back', value: BACK },
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The value a typed `number` or `text` input sends: empty is `null` (the
|
|
130
|
+
* action receives that as "leave unset"), a number input sends a number
|
|
131
|
+
* when the text parses as one and the text otherwise, so the plugin can say
|
|
132
|
+
* what is wrong with it.
|
|
133
|
+
*/
|
|
134
|
+
export function promptValue(knob: Knob, raw: string): KnobValue {
|
|
135
|
+
const text = raw.trim()
|
|
136
|
+
if (text === '') return null
|
|
137
|
+
if (knob.kind === 'number') {
|
|
138
|
+
const number = Number(text)
|
|
139
|
+
return Number.isFinite(number) ? number : text
|
|
140
|
+
}
|
|
141
|
+
return raw
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function knobDefault(knob: Knob): string {
|
|
145
|
+
if (knob.kind === 'toggle') return knob.value ? 'on' : 'off'
|
|
146
|
+
return knob.value === undefined ? '' : String(knob.value)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface DrawerContext {
|
|
150
|
+
api: TuiPluginApi
|
|
151
|
+
apply: ApplyFn
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Opens the drawer on a payload pushed by `/openai`. */
|
|
155
|
+
export function openCommandDialog(
|
|
156
|
+
api: TuiPluginApi,
|
|
157
|
+
payload: OpenDialogPayload,
|
|
158
|
+
apply: ApplyFn,
|
|
159
|
+
) {
|
|
160
|
+
showMenu({ api, apply }, payload.command, payload.menu)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function showMenu(context: DrawerContext, command: string, menu: Menu) {
|
|
164
|
+
const { api } = context
|
|
165
|
+
// A menu with one section (the not-migrated notice) opens on it directly.
|
|
166
|
+
if (menu.sections.length === 1) {
|
|
167
|
+
const [only] = menu.sections
|
|
168
|
+
if (only) {
|
|
169
|
+
showSection(context, command, menu, only.id)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const DialogSelect = api.ui.DialogSelect<string>
|
|
174
|
+
api.ui.dialog.setSize('xlarge')
|
|
175
|
+
api.ui.dialog.replace(() => (
|
|
176
|
+
<DialogSelect
|
|
177
|
+
title={menu.title}
|
|
178
|
+
options={sectionListOptions(menu)}
|
|
179
|
+
onSelect={(option) => {
|
|
180
|
+
const id = String(option.value).slice('section:'.length)
|
|
181
|
+
showSection(context, command, menu, id)
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function showSection(
|
|
188
|
+
context: DrawerContext,
|
|
189
|
+
command: string,
|
|
190
|
+
menu: Menu,
|
|
191
|
+
sectionId: string,
|
|
192
|
+
) {
|
|
193
|
+
const { api } = context
|
|
194
|
+
const section = menu.sections.find((entry) => entry.id === sectionId)
|
|
195
|
+
if (!section) {
|
|
196
|
+
showMenu(context, command, menu)
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
const DialogSelect = api.ui.DialogSelect<string>
|
|
200
|
+
api.ui.dialog.setSize('xlarge')
|
|
201
|
+
api.ui.dialog.replace(() => (
|
|
202
|
+
<DialogSelect
|
|
203
|
+
title={section.title}
|
|
204
|
+
options={sectionOptions(section)}
|
|
205
|
+
onSelect={(option) => {
|
|
206
|
+
const value = String(option.value)
|
|
207
|
+
if (value === BACK) {
|
|
208
|
+
if (menu.sections.length === 1) api.ui.dialog.clear()
|
|
209
|
+
else showMenu(context, command, menu)
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
if (value.startsWith('item:')) {
|
|
213
|
+
showItem(context, command, menu, section, value.slice(5))
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
if (value.startsWith('action:')) {
|
|
217
|
+
const action = section.actions.find(
|
|
218
|
+
(entry) => entry.id === value.slice(7),
|
|
219
|
+
)
|
|
220
|
+
if (action)
|
|
221
|
+
void runAction(context, command, menu, section, undefined, action)
|
|
222
|
+
}
|
|
223
|
+
}}
|
|
224
|
+
/>
|
|
225
|
+
))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function showItem(
|
|
229
|
+
context: DrawerContext,
|
|
230
|
+
command: string,
|
|
231
|
+
menu: Menu,
|
|
232
|
+
section: Section,
|
|
233
|
+
itemId: string,
|
|
234
|
+
) {
|
|
235
|
+
const { api } = context
|
|
236
|
+
const item = section.items.find((entry) => entry.id === itemId)
|
|
237
|
+
if (!item) {
|
|
238
|
+
showSection(context, command, menu, section.id)
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
const DialogSelect = api.ui.DialogSelect<string>
|
|
242
|
+
api.ui.dialog.setSize('xlarge')
|
|
243
|
+
api.ui.dialog.replace(() => (
|
|
244
|
+
<DialogSelect
|
|
245
|
+
title={`${section.title}: ${item.label}`}
|
|
246
|
+
options={itemOptions(item)}
|
|
247
|
+
onSelect={(option) => {
|
|
248
|
+
const value = String(option.value)
|
|
249
|
+
if (value === BACK) {
|
|
250
|
+
showSection(context, command, menu, section.id)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
if (value.startsWith('action:')) {
|
|
254
|
+
const action = item.actions.find(
|
|
255
|
+
(entry) => entry.id === value.slice(7),
|
|
256
|
+
)
|
|
257
|
+
if (action)
|
|
258
|
+
void runAction(context, command, menu, section, item, action)
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
/>
|
|
262
|
+
))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Asks for one input; resolves undefined when the user backs out. */
|
|
266
|
+
function askKnob(
|
|
267
|
+
api: TuiPluginApi,
|
|
268
|
+
title: string,
|
|
269
|
+
knob: Knob,
|
|
270
|
+
): Promise<KnobValue | undefined> {
|
|
271
|
+
return new Promise((resolve) => {
|
|
272
|
+
api.ui.dialog.setSize('xlarge')
|
|
273
|
+
if (knob.kind === 'choice' || knob.kind === 'toggle') {
|
|
274
|
+
const DialogSelect = api.ui.DialogSelect<string>
|
|
275
|
+
const options =
|
|
276
|
+
knob.kind === 'choice'
|
|
277
|
+
? knob.choices.map((choice) => ({
|
|
278
|
+
title: choice.label,
|
|
279
|
+
value: choice.value,
|
|
280
|
+
}))
|
|
281
|
+
: [
|
|
282
|
+
{ title: 'On', value: 'on' },
|
|
283
|
+
{ title: 'Off', value: 'off' },
|
|
284
|
+
]
|
|
285
|
+
api.ui.dialog.replace(() => (
|
|
286
|
+
<DialogSelect
|
|
287
|
+
title={`${title}: ${knob.label}`}
|
|
288
|
+
current={knobDefault(knob)}
|
|
289
|
+
options={[...options, { title: 'Cancel', value: `\u0000cancel` }]}
|
|
290
|
+
onSelect={(option) => {
|
|
291
|
+
const value = String(option.value)
|
|
292
|
+
if (value === `\u0000cancel`) resolve(undefined)
|
|
293
|
+
else resolve(knob.kind === 'toggle' ? value === 'on' : value)
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
))
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
const DialogPrompt = api.ui.DialogPrompt
|
|
300
|
+
api.ui.dialog.replace(() => (
|
|
301
|
+
<DialogPrompt
|
|
302
|
+
title={`${title}: ${knob.label}`}
|
|
303
|
+
placeholder={knob.kind === 'text' ? (knob.placeholder ?? '') : ''}
|
|
304
|
+
value={knobDefault(knob)}
|
|
305
|
+
onConfirm={(value: string) => resolve(promptValue(knob, value))}
|
|
306
|
+
onCancel={() => resolve(undefined)}
|
|
307
|
+
/>
|
|
308
|
+
))
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function askConfirmation(
|
|
313
|
+
api: TuiPluginApi,
|
|
314
|
+
title: string,
|
|
315
|
+
message: string,
|
|
316
|
+
): Promise<boolean> {
|
|
317
|
+
return new Promise((resolve) => {
|
|
318
|
+
const DialogConfirm = api.ui.DialogConfirm
|
|
319
|
+
api.ui.dialog.setSize('xlarge')
|
|
320
|
+
api.ui.dialog.replace(() => (
|
|
321
|
+
<DialogConfirm
|
|
322
|
+
title={title}
|
|
323
|
+
message={message}
|
|
324
|
+
onConfirm={() => resolve(true)}
|
|
325
|
+
onCancel={() => resolve(false)}
|
|
326
|
+
/>
|
|
327
|
+
))
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function showResult(api: TuiPluginApi, title: string, text: string) {
|
|
332
|
+
api.ui.dialog.setSize('xlarge')
|
|
333
|
+
api.ui.dialog.replace(() => (
|
|
334
|
+
<box flexDirection='column' padding={1} width='100%'>
|
|
335
|
+
<text>{title}</text>
|
|
336
|
+
<text>{text}</text>
|
|
337
|
+
</box>
|
|
338
|
+
))
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Collects an action's inputs, confirms it when it carries a confirmation,
|
|
343
|
+
* applies it and redraws the section from the refreshed menu. A long
|
|
344
|
+
* message (a sign-in URL, a reset result) is shown in full; a short one is a
|
|
345
|
+
* toast over the redrawn section.
|
|
346
|
+
*/
|
|
347
|
+
async function runAction(
|
|
348
|
+
context: DrawerContext,
|
|
349
|
+
command: string,
|
|
350
|
+
menu: Menu,
|
|
351
|
+
section: Section,
|
|
352
|
+
item: Item | undefined,
|
|
353
|
+
action: Action,
|
|
354
|
+
) {
|
|
355
|
+
const { api, apply } = context
|
|
356
|
+
const back = () =>
|
|
357
|
+
item
|
|
358
|
+
? showItem(context, command, menu, section, item.id)
|
|
359
|
+
: showSection(context, command, menu, section.id)
|
|
360
|
+
const values: Record<string, KnobValue> = {}
|
|
361
|
+
for (const knob of action.knobs) {
|
|
362
|
+
const value = await askKnob(api, action.label, knob)
|
|
363
|
+
if (value === undefined) {
|
|
364
|
+
back()
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
values[knob.id] = value
|
|
368
|
+
}
|
|
369
|
+
let confirmed = false
|
|
370
|
+
if (action.confirm) {
|
|
371
|
+
confirmed = await askConfirmation(api, action.label, action.confirm.message)
|
|
372
|
+
if (!confirmed) {
|
|
373
|
+
back()
|
|
374
|
+
return
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const request: Omit<ApplyRequest, 'sessionId'> = {
|
|
378
|
+
command,
|
|
379
|
+
sectionId: section.id,
|
|
380
|
+
...(item ? { itemId: item.id } : {}),
|
|
381
|
+
actionId: action.id,
|
|
382
|
+
values,
|
|
383
|
+
...(confirmed ? { confirmed: true } : {}),
|
|
384
|
+
}
|
|
385
|
+
let result: ApplyResult
|
|
386
|
+
try {
|
|
387
|
+
result = await apply(request)
|
|
388
|
+
} catch (error) {
|
|
389
|
+
log.warn('menu apply failed', {
|
|
390
|
+
error: error instanceof Error ? error.message : String(error),
|
|
391
|
+
})
|
|
392
|
+
api.ui.toast({ message: 'The action could not reach OpenAI auth.' })
|
|
393
|
+
back()
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
// The shared RPC client answers a failed call with a body of its own that
|
|
397
|
+
// carries no menu; redraw from the menu already shown then.
|
|
398
|
+
const next = result.menu ?? menu
|
|
399
|
+
if (result.needsConfirmation) {
|
|
400
|
+
const yes = await askConfirmation(api, action.label, result.text)
|
|
401
|
+
if (yes) {
|
|
402
|
+
const again = await apply({ ...request, confirmed: true })
|
|
403
|
+
finish(context, command, again.menu ?? next, section.id, again)
|
|
404
|
+
return
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
finish(context, command, next, section.id, result)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function finish(
|
|
411
|
+
context: DrawerContext,
|
|
412
|
+
command: string,
|
|
413
|
+
menu: Menu,
|
|
414
|
+
sectionId: string,
|
|
415
|
+
result: ApplyResult,
|
|
416
|
+
) {
|
|
417
|
+
const { api } = context
|
|
418
|
+
if (result.text.includes('\n') || result.text.length > 120) {
|
|
419
|
+
showResult(api, result.ok ? 'Done' : 'Not done', result.text)
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
api.ui.toast({ message: result.text })
|
|
423
|
+
showSection(context, command, menu, sectionId)
|
|
424
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
createTuiPreferenceWriter,
|
|
6
|
+
readTuiPreferencesFile as readCommonTuiPreferencesFile,
|
|
7
|
+
watchTuiPreferences as watchCommonTuiPreferences
|
|
8
|
+
} from "./shared/9968e3cade6969a1-index.js";
|
|
9
|
+
export const TUI_PREFS_FILE_ENV = "OPENCODE_TUI_PREFERENCES_FILE";
|
|
10
|
+
const FILE_NAME = "tui-preferences.jsonc";
|
|
11
|
+
export function getTuiPreferencesFile() {
|
|
12
|
+
const override = process.env[TUI_PREFS_FILE_ENV];
|
|
13
|
+
if (override)
|
|
14
|
+
return override;
|
|
15
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR || join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "opencode");
|
|
16
|
+
return join(configDir, FILE_NAME);
|
|
17
|
+
}
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
export function readTuiPreferencesFile() {
|
|
22
|
+
return readCommonTuiPreferencesFile(getTuiPreferencesFile());
|
|
23
|
+
}
|
|
24
|
+
export const PLUGIN_KEY = "openai-auth", DEFAULT_SLOT_ORDER = 160, DEFAULT_PREFS = {
|
|
25
|
+
forceToTop: !1,
|
|
26
|
+
order: DEFAULT_SLOT_ORDER,
|
|
27
|
+
startCollapsed: !1,
|
|
28
|
+
rememberCollapsed: !0,
|
|
29
|
+
collapsed: null,
|
|
30
|
+
pollMs: 1500,
|
|
31
|
+
refreshDebounceMs: 200,
|
|
32
|
+
header: { label: "OPENAI", showVersion: !0 },
|
|
33
|
+
sections: {
|
|
34
|
+
quota: !0,
|
|
35
|
+
fallbackAccounts: !0,
|
|
36
|
+
routing: !0,
|
|
37
|
+
health: !0,
|
|
38
|
+
pacing: !0
|
|
39
|
+
},
|
|
40
|
+
appearance: {
|
|
41
|
+
barWidth: 10,
|
|
42
|
+
barFilledChar: "\u2588",
|
|
43
|
+
barEmptyChar: "\u2591",
|
|
44
|
+
warnThreshold: 50,
|
|
45
|
+
errorThreshold: 80
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function bool(value, fallback) {
|
|
49
|
+
return typeof value === "boolean" ? value : fallback;
|
|
50
|
+
}
|
|
51
|
+
function int(value, fallback, min, max) {
|
|
52
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
53
|
+
return fallback;
|
|
54
|
+
return Math.min(Math.max(Math.round(value), min), max);
|
|
55
|
+
}
|
|
56
|
+
function label(value, fallback, maxLength) {
|
|
57
|
+
if (typeof value !== "string" || value.length === 0)
|
|
58
|
+
return fallback;
|
|
59
|
+
return value.slice(0, maxLength);
|
|
60
|
+
}
|
|
61
|
+
function char(value, fallback) {
|
|
62
|
+
if (typeof value !== "string")
|
|
63
|
+
return fallback;
|
|
64
|
+
return [...value][0] ?? fallback;
|
|
65
|
+
}
|
|
66
|
+
export function resolveOpenaiAuthPrefs(root) {
|
|
67
|
+
const entry = root[PLUGIN_KEY];
|
|
68
|
+
if (!isRecord(entry))
|
|
69
|
+
return structuredClone(DEFAULT_PREFS);
|
|
70
|
+
const d = DEFAULT_PREFS, header = isRecord(entry.header) ? entry.header : {}, sections = isRecord(entry.sections) ? entry.sections : {}, appearance = isRecord(entry.appearance) ? entry.appearance : {}, warnThreshold = int(appearance.warnThreshold, d.appearance.warnThreshold, 0, 99), errorThreshold = Math.max(int(appearance.errorThreshold, d.appearance.errorThreshold, 0, 100), Math.min(warnThreshold + 1, 100));
|
|
71
|
+
return {
|
|
72
|
+
forceToTop: bool(entry.forceToTop, d.forceToTop),
|
|
73
|
+
order: int(entry.order, d.order, -1e4, 1e4),
|
|
74
|
+
startCollapsed: bool(entry.startCollapsed, d.startCollapsed),
|
|
75
|
+
rememberCollapsed: bool(entry.rememberCollapsed, d.rememberCollapsed),
|
|
76
|
+
collapsed: typeof entry.collapsed === "boolean" ? entry.collapsed : null,
|
|
77
|
+
pollMs: int(entry.pollMs, d.pollMs, 500, 30000),
|
|
78
|
+
refreshDebounceMs: int(entry.refreshDebounceMs, d.refreshDebounceMs, 50, 5000),
|
|
79
|
+
header: {
|
|
80
|
+
label: label(header.label, d.header.label, 20),
|
|
81
|
+
showVersion: bool(header.showVersion, d.header.showVersion)
|
|
82
|
+
},
|
|
83
|
+
sections: {
|
|
84
|
+
quota: bool(sections.quota, d.sections.quota),
|
|
85
|
+
fallbackAccounts: bool(sections.fallbackAccounts, d.sections.fallbackAccounts),
|
|
86
|
+
routing: bool(sections.routing, d.sections.routing),
|
|
87
|
+
health: bool(sections.health, d.sections.health),
|
|
88
|
+
pacing: bool(sections.pacing, d.sections.pacing)
|
|
89
|
+
},
|
|
90
|
+
appearance: {
|
|
91
|
+
barWidth: int(appearance.barWidth, d.appearance.barWidth, 4, 40),
|
|
92
|
+
barFilledChar: char(appearance.barFilledChar, d.appearance.barFilledChar),
|
|
93
|
+
barEmptyChar: char(appearance.barEmptyChar, d.appearance.barEmptyChar),
|
|
94
|
+
warnThreshold,
|
|
95
|
+
errorThreshold
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const FORCE_TOP_BASE = -1e5;
|
|
100
|
+
export function computeEffectiveOrder(root, pluginKey, defaultOrder) {
|
|
101
|
+
const entry = root[pluginKey];
|
|
102
|
+
if (!isRecord(entry))
|
|
103
|
+
return defaultOrder;
|
|
104
|
+
if (entry.forceToTop === !0)
|
|
105
|
+
return FORCE_TOP_BASE + Object.keys(root).indexOf(pluginKey);
|
|
106
|
+
return int(entry.order, defaultOrder, -1e4, 1e4);
|
|
107
|
+
}
|
|
108
|
+
const TEMPLATE = `// Shared preferences for opencode TUI plugins.
|
|
109
|
+
// One top-level key per plugin (short name). See each plugin's README for
|
|
110
|
+
// its supported settings. This file is safe to hand-edit; plugins update
|
|
111
|
+
// individual keys in place and preserve comments.
|
|
112
|
+
{}
|
|
113
|
+
`, writers = new Map;
|
|
114
|
+
function writerFor(file, pluginKey) {
|
|
115
|
+
const key = JSON.stringify([file, pluginKey]);
|
|
116
|
+
let writer = writers.get(key);
|
|
117
|
+
if (!writer) {
|
|
118
|
+
writer = createTuiPreferenceWriter({ file, pluginKey });
|
|
119
|
+
writers.set(key, writer);
|
|
120
|
+
}
|
|
121
|
+
return writer;
|
|
122
|
+
}
|
|
123
|
+
async function seedPreferencesFile(file) {
|
|
124
|
+
await mkdir(dirname(file), { recursive: !0 });
|
|
125
|
+
await writeFile(file, TEMPLATE, { encoding: "utf8", flag: "wx" }).catch(() => {});
|
|
126
|
+
}
|
|
127
|
+
let writeChain = Promise.resolve();
|
|
128
|
+
export function queueTuiPreferenceUpdate(pluginKey, path, value) {
|
|
129
|
+
const file = getTuiPreferencesFile(), writer = writerFor(file, pluginKey);
|
|
130
|
+
writeChain = writeChain.then(() => seedPreferencesFile(file)).then(() => writer.queueTuiPreferenceUpdate(path, value)).catch(() => {});
|
|
131
|
+
return writeChain;
|
|
132
|
+
}
|
|
133
|
+
export function watchTuiPreferences(onChange) {
|
|
134
|
+
return watchCommonTuiPreferences(getTuiPreferencesFile(), onChange);
|
|
135
|
+
}
|