@cortexkit/opencode-openai-auth 0.11.1 → 1.0.1

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.
Files changed (204) hide show
  1. package/README.md +15 -26
  2. package/dist/index-766sfrh5.js +1 -0
  3. package/dist/index-8kj7sa1p.js +12 -0
  4. package/dist/index-b3q3j39x.js +2 -0
  5. package/dist/index-bmd7x5aq.js +2 -0
  6. package/dist/index-eqh53k8v.js +2 -0
  7. package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
  8. package/dist/index-jyr8y8k4.js +7 -0
  9. package/dist/index-rv08jmd4.js +2 -0
  10. package/dist/index-ww91hyzr.js +221 -0
  11. package/dist/index.d.ts +578 -145
  12. package/dist/index.js +1 -110
  13. package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
  14. package/dist/port-file-Do3lQIEk.d.ts +29 -0
  15. package/dist/protocol-BOBdkqqm.d.ts +198 -0
  16. package/dist/rpc/port-file.d.ts +4 -17
  17. package/dist/rpc/port-file.js +1 -1
  18. package/dist/rpc/protocol.d.ts +2 -2
  19. package/dist/rpc/protocol.js +1 -0
  20. package/dist/rpc/rpc-client.d.ts +5 -4
  21. package/dist/rpc/rpc-client.js +1 -1
  22. package/dist/rpc/rpc-dir.d.ts +2 -0
  23. package/dist/rpc/rpc-dir.js +1 -1
  24. package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
  25. package/dist/sidebar-state.d.ts +2 -2
  26. package/dist/sidebar-state.js +1 -1
  27. package/dist/token-util-wdza526f.js +1 -0
  28. package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
  29. package/dist/tui-preferences.js +5 -2
  30. package/dist/tui.d.ts +9 -4
  31. package/dist/v2/server.d.ts +54 -0
  32. package/dist/v2/server.js +4 -0
  33. package/package.json +21 -9
  34. package/src/tui/entry.mjs +26 -25
  35. package/src/tui-compiled/raw/logger.js +24 -0
  36. package/src/tui-compiled/raw/rpc/protocol.js +3 -0
  37. package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
  38. package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
  39. package/src/tui-compiled/raw/selector.js +21 -0
  40. package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
  41. package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
  42. package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
  43. package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
  44. package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
  45. package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
  46. package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
  47. package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
  48. package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
  49. package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
  50. package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
  51. package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
  52. package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
  53. package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
  54. package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
  55. package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
  56. package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
  57. package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
  58. package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
  59. package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
  60. package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
  61. package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
  62. package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
  63. package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
  64. package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
  65. package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
  66. package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
  67. package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
  68. package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
  69. package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
  70. package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
  71. package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
  72. package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
  73. package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
  74. package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
  75. package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
  76. package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
  77. package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
  78. package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
  79. package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
  80. package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
  81. package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
  82. package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
  83. package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
  84. package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
  85. package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
  86. package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
  87. package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
  88. package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
  89. package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
  90. package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
  91. package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
  92. package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
  93. package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
  94. package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
  95. package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
  96. package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
  97. package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
  98. package/src/tui-compiled/raw/sidebar-state.js +960 -0
  99. package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
  100. package/src/tui-compiled/raw/tui-preferences.js +135 -0
  101. package/src/tui-compiled/raw/tui.tsx +135 -143
  102. package/src/tui-compiled/runtime/logger.js +24 -0
  103. package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
  104. package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
  105. package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
  106. package/src/tui-compiled/runtime/selector.js +21 -0
  107. package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
  108. package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
  109. package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
  110. package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
  111. package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
  112. package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
  113. package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
  114. package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
  115. package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
  116. package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
  117. package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
  118. package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
  119. package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
  120. package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
  121. package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
  122. package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
  123. package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
  124. package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
  125. package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
  126. package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
  127. package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
  128. package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
  129. package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
  130. package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
  131. package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
  132. package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
  133. package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
  134. package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
  135. package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
  136. package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
  137. package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
  138. package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
  139. package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
  140. package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
  141. package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
  142. package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
  143. package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
  144. package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
  145. package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
  146. package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
  147. package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
  148. package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
  149. package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
  150. package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
  151. package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
  152. package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
  153. package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
  154. package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
  155. package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
  156. package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
  157. package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
  158. package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
  159. package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
  160. package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
  161. package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
  162. package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
  163. package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
  164. package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
  165. package/src/tui-compiled/runtime/sidebar-state.js +960 -0
  166. package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
  167. package/src/tui-compiled/runtime/tui-preferences.js +135 -0
  168. package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
  169. package/dist/index-4effjmr6.js +0 -2
  170. package/dist/index-9ehhb051.js +0 -2
  171. package/dist/index-g3hsdjh4.js +0 -2
  172. package/dist/index-hh52v8sb.js +0 -2
  173. package/dist/index-jwn4r3sw.js +0 -1
  174. package/dist/index-sq36b120.js +0 -211
  175. package/dist/internal-BTndPmW2.d.ts +0 -914
  176. package/dist/quota-normalize-08hpp51v.js +0 -1
  177. package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
  178. package/dist/token-util-ypgz7rge.js +0 -1
  179. package/src/tui-compiled/raw/core-account-paths.ts +0 -114
  180. package/src/tui-compiled/raw/core-custody-state.ts +0 -243
  181. package/src/tui-compiled/raw/logger.ts +0 -53
  182. package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
  183. package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
  184. package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
  185. package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
  186. package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
  187. package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
  188. package/src/tui-compiled/raw/tui-preferences.ts +0 -328
  189. package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
  190. package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
  191. package/src/tui-compiled/runtime/logger.ts +0 -53
  192. package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
  193. package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
  194. package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
  195. package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
  196. package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
  197. package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
  198. package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
  199. package/src/tui-compiled/shared/internal.ts +0 -6
  200. package/src/tui-compiled/shared/logger.ts +0 -264
  201. package/src/tui-compiled/shared/paths.ts +0 -33
  202. package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
  203. package/src/tui-compiled/shared/util/error.ts +0 -9
  204. 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
+ }