wepscli 0.1.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.
Files changed (130) hide show
  1. package/README.md +293 -0
  2. package/dist/WEPSCLI-shell/agent-runtime.js +824 -0
  3. package/dist/WEPSCLI-shell/agent-runtime.js.map +1 -0
  4. package/dist/WEPSCLI-shell/approval-overlay.js +275 -0
  5. package/dist/WEPSCLI-shell/approval-overlay.js.map +1 -0
  6. package/dist/WEPSCLI-shell/chat-components.js +760 -0
  7. package/dist/WEPSCLI-shell/chat-components.js.map +1 -0
  8. package/dist/WEPSCLI-shell/components.js +850 -0
  9. package/dist/WEPSCLI-shell/components.js.map +1 -0
  10. package/dist/WEPSCLI-shell/config-overlays.js +205 -0
  11. package/dist/WEPSCLI-shell/config-overlays.js.map +1 -0
  12. package/dist/WEPSCLI-shell/debug-log.js +16 -0
  13. package/dist/WEPSCLI-shell/debug-log.js.map +1 -0
  14. package/dist/WEPSCLI-shell/file-change-preview.js +261 -0
  15. package/dist/WEPSCLI-shell/file-change-preview.js.map +1 -0
  16. package/dist/WEPSCLI-shell/helpers.js +112 -0
  17. package/dist/WEPSCLI-shell/helpers.js.map +1 -0
  18. package/dist/WEPSCLI-shell/index.js +3 -0
  19. package/dist/WEPSCLI-shell/index.js.map +1 -0
  20. package/dist/WEPSCLI-shell/provider-add-flow.js +406 -0
  21. package/dist/WEPSCLI-shell/provider-add-flow.js.map +1 -0
  22. package/dist/WEPSCLI-shell/run-wepscli-shell.js +21 -0
  23. package/dist/WEPSCLI-shell/run-wepscli-shell.js.map +1 -0
  24. package/dist/WEPSCLI-shell/runtime-recovery.js +37 -0
  25. package/dist/WEPSCLI-shell/runtime-recovery.js.map +1 -0
  26. package/dist/WEPSCLI-shell/runtime-status.js +66 -0
  27. package/dist/WEPSCLI-shell/runtime-status.js.map +1 -0
  28. package/dist/WEPSCLI-shell/shell-app.js +1047 -0
  29. package/dist/WEPSCLI-shell/shell-app.js.map +1 -0
  30. package/dist/WEPSCLI-shell/shell-modes.js +77 -0
  31. package/dist/WEPSCLI-shell/shell-modes.js.map +1 -0
  32. package/dist/WEPSCLI-shell/slash-commands.js +135 -0
  33. package/dist/WEPSCLI-shell/slash-commands.js.map +1 -0
  34. package/dist/WEPSCLI-shell/theme.js +19 -0
  35. package/dist/WEPSCLI-shell/theme.js.map +1 -0
  36. package/dist/WEPSCLI-shell/tool-approval.js +85 -0
  37. package/dist/WEPSCLI-shell/tool-approval.js.map +1 -0
  38. package/dist/WEPSCLI-shell/tool-diff.js +76 -0
  39. package/dist/WEPSCLI-shell/tool-diff.js.map +1 -0
  40. package/dist/WEPSCLI-shell/tool-file-changes.js +268 -0
  41. package/dist/WEPSCLI-shell/tool-file-changes.js.map +1 -0
  42. package/dist/WEPSCLI-shell/tool-message-detail.js +138 -0
  43. package/dist/WEPSCLI-shell/tool-message-detail.js.map +1 -0
  44. package/dist/WEPSCLI-shell/tool-messages.js +145 -0
  45. package/dist/WEPSCLI-shell/tool-messages.js.map +1 -0
  46. package/dist/WEPSCLI-shell/transcript-panel.js +372 -0
  47. package/dist/WEPSCLI-shell/transcript-panel.js.map +1 -0
  48. package/dist/WEPSCLI-shell/transcript-state.js +62 -0
  49. package/dist/WEPSCLI-shell/transcript-state.js.map +1 -0
  50. package/dist/WEPSCLI-shell/types.js +1 -0
  51. package/dist/WEPSCLI-shell/types.js.map +1 -0
  52. package/dist/cli.js +11 -0
  53. package/dist/cli.js.map +1 -0
  54. package/dist/config.js +40 -0
  55. package/dist/config.js.map +1 -0
  56. package/dist/index.js +4 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/main.js +140 -0
  59. package/dist/main.js.map +1 -0
  60. package/dist/onboarding/action-screen.js +90 -0
  61. package/dist/onboarding/action-screen.js.map +1 -0
  62. package/dist/onboarding/framed-screen.js +35 -0
  63. package/dist/onboarding/framed-screen.js.map +1 -0
  64. package/dist/onboarding/onboarding-app.js +312 -0
  65. package/dist/onboarding/onboarding-app.js.map +1 -0
  66. package/dist/onboarding/run-onboarding.js +23 -0
  67. package/dist/onboarding/run-onboarding.js.map +1 -0
  68. package/dist/onboarding/select-screen.js +21 -0
  69. package/dist/onboarding/select-screen.js.map +1 -0
  70. package/dist/onboarding/summary-screen.js +23 -0
  71. package/dist/onboarding/summary-screen.js.map +1 -0
  72. package/dist/onboarding/text-input-screen.js +55 -0
  73. package/dist/onboarding/text-input-screen.js.map +1 -0
  74. package/dist/onboarding/theme.js +26 -0
  75. package/dist/onboarding/theme.js.map +1 -0
  76. package/dist/provider-profiles/api-key-store.js +51 -0
  77. package/dist/provider-profiles/api-key-store.js.map +1 -0
  78. package/dist/provider-profiles/defaults.js +18 -0
  79. package/dist/provider-profiles/defaults.js.map +1 -0
  80. package/dist/provider-profiles/fetch-models.js +53 -0
  81. package/dist/provider-profiles/fetch-models.js.map +1 -0
  82. package/dist/provider-profiles/index.js +6 -0
  83. package/dist/provider-profiles/index.js.map +1 -0
  84. package/dist/provider-profiles/provider-profile-service.js +223 -0
  85. package/dist/provider-profiles/provider-profile-service.js.map +1 -0
  86. package/dist/provider-profiles/providers-config-store.js +17 -0
  87. package/dist/provider-profiles/providers-config-store.js.map +1 -0
  88. package/dist/provider-profiles/types.js +1 -0
  89. package/dist/provider-profiles/types.js.map +1 -0
  90. package/dist/session-history/session-history-service.js +142 -0
  91. package/dist/session-history/session-history-service.js.map +1 -0
  92. package/dist/shell/animator.js +30 -0
  93. package/dist/shell/animator.js.map +1 -0
  94. package/dist/shell/clickables.js +101 -0
  95. package/dist/shell/clickables.js.map +1 -0
  96. package/dist/shell/dashboard-shell.js +292 -0
  97. package/dist/shell/dashboard-shell.js.map +1 -0
  98. package/dist/shell/index.js +5 -0
  99. package/dist/shell/index.js.map +1 -0
  100. package/dist/shell/keymap.js +14 -0
  101. package/dist/shell/keymap.js.map +1 -0
  102. package/dist/shell/mouse.js +39 -0
  103. package/dist/shell/mouse.js.map +1 -0
  104. package/dist/shell/render.js +122 -0
  105. package/dist/shell/render.js.map +1 -0
  106. package/dist/shell/run-shell.js +36 -0
  107. package/dist/shell/run-shell.js.map +1 -0
  108. package/dist/shell/theme.js +56 -0
  109. package/dist/shell/theme.js.map +1 -0
  110. package/dist/storage/locked-json-file.js +88 -0
  111. package/dist/storage/locked-json-file.js.map +1 -0
  112. package/dist/workbench/animator.js +30 -0
  113. package/dist/workbench/animator.js.map +1 -0
  114. package/dist/workbench/index.js +6 -0
  115. package/dist/workbench/index.js.map +1 -0
  116. package/dist/workbench/mouse.js +39 -0
  117. package/dist/workbench/mouse.js.map +1 -0
  118. package/dist/workbench/render.js +82 -0
  119. package/dist/workbench/render.js.map +1 -0
  120. package/dist/workbench/renderer.js +364 -0
  121. package/dist/workbench/renderer.js.map +1 -0
  122. package/dist/workbench/run-workbench.js +36 -0
  123. package/dist/workbench/run-workbench.js.map +1 -0
  124. package/dist/workbench/theme.js +63 -0
  125. package/dist/workbench/theme.js.map +1 -0
  126. package/dist/workbench/types.js +1 -0
  127. package/dist/workbench/types.js.map +1 -0
  128. package/dist/workbench/workbench-shell.js +649 -0
  129. package/dist/workbench/workbench-shell.js.map +1 -0
  130. package/package.json +65 -0
@@ -0,0 +1,312 @@
1
+ import { Container, getKeybindings, Spacer, Text } from "@mariozechner/pi-tui";
2
+ import { fetchModelsForProfile, getDefaultDialect } from "../provider-profiles/index.js";
3
+ import { ActionScreen } from "./action-screen.js";
4
+ import { FramedScreen } from "./framed-screen.js";
5
+ import { SelectScreen } from "./select-screen.js";
6
+ import { SummaryScreen } from "./summary-screen.js";
7
+ import { onboardingTheme } from "./theme.js";
8
+ import { TextInputScreen } from "./text-input-screen.js";
9
+ export class OnboardingApp extends Container {
10
+ draft = {
11
+ label: "",
12
+ baseUrl: "",
13
+ apiKey: "",
14
+ fetchedModels: []
15
+ };
16
+ constructor(ui, profiles, onComplete, onAbort) {
17
+ super();
18
+ this.ui = ui;
19
+ this.profiles = profiles;
20
+ this.onComplete = onComplete;
21
+ this.onAbort = onAbort;
22
+ this.showWelcome();
23
+ }
24
+ setScreen(screen) {
25
+ this.clear();
26
+ this.currentScreen = screen;
27
+ this.addChild(screen);
28
+ this.ui.setFocus(screen.getFocusTarget());
29
+ this.ui.requestRender();
30
+ }
31
+ buildTemporaryProfile() {
32
+ if (!this.draft.family) {
33
+ throw new Error("Provider family is not set");
34
+ }
35
+ const now = new Date().toISOString();
36
+ return {
37
+ id: "draft",
38
+ label: this.draft.label || "Draft profile",
39
+ family: this.draft.family,
40
+ apiDialect: getDefaultDialect(this.draft.family),
41
+ baseUrl: this.draft.baseUrl,
42
+ enabled: true,
43
+ models: [],
44
+ createdAt: now,
45
+ updatedAt: now,
46
+ lastValidationStatus: "unknown"
47
+ };
48
+ }
49
+ showWelcome() {
50
+ const screen = new class extends FramedScreen {
51
+ constructor(onContinue, onAbort) {
52
+ super("Step 1/8", "Welcome to WEPSCLI", "A guided first-run setup is required before entering the app.");
53
+ this.content.addChild(new Text(onboardingTheme.text("This wizard will create your first provider profile."), 0, 0));
54
+ this.content.addChild(new Spacer(1));
55
+ this.content.addChild(new Text(onboardingTheme.muted("You will choose a provider family, enter a base URL and API key, test the endpoint, and select a model."), 0, 0));
56
+ this.setFooter("Enter continue | Esc quit");
57
+ this.handleInput = data => {
58
+ const keybindings = getKeybindings();
59
+ if (keybindings.matches(data, "tui.select.confirm")) {
60
+ onContinue();
61
+ return;
62
+ }
63
+ if (keybindings.matches(data, "tui.select.cancel")) {
64
+ onAbort();
65
+ }
66
+ };
67
+ }
68
+ }(() => this.showFamilySelect(), this.onAbort);
69
+ this.setScreen(screen);
70
+ }
71
+ showFamilySelect() {
72
+ const items = [{
73
+ value: "openai",
74
+ label: "OpenAI-style",
75
+ description: "For third-party endpoints that expose an OpenAI-compatible API."
76
+ }, {
77
+ value: "anthropic",
78
+ label: "Anthropic-style",
79
+ description: "For third-party endpoints that expose an Anthropic-compatible API."
80
+ }];
81
+ this.setScreen(new SelectScreen({
82
+ stepLabel: "Step 2/8",
83
+ title: "Choose provider family",
84
+ description: "This decides which model-listing request shape WEPSCLI will use.",
85
+ items,
86
+ onSelect: item => {
87
+ this.draft.family = item.value;
88
+ this.showLabelInput();
89
+ },
90
+ onBack: () => this.showWelcome()
91
+ }));
92
+ }
93
+ showLabelInput() {
94
+ this.setScreen(new TextInputScreen({
95
+ stepLabel: "Step 3/8",
96
+ title: "Provider label",
97
+ description: "This is the friendly name shown inside WEPSCLI when you switch providers.",
98
+ label: "Label",
99
+ initialValue: this.draft.label,
100
+ placeholder: "Example: My OpenAI Proxy",
101
+ validate: value => !value ? "Provider label is required." : undefined,
102
+ onSubmit: value => {
103
+ this.draft.label = value;
104
+ this.showBaseUrlInput();
105
+ },
106
+ onBack: () => this.showFamilySelect()
107
+ }));
108
+ }
109
+ showBaseUrlInput() {
110
+ this.setScreen(new TextInputScreen({
111
+ stepLabel: "Step 4/8",
112
+ title: "Base URL",
113
+ description: "WEPSCLI will use this value exactly as entered. No automatic /v1 normalization will be applied.",
114
+ label: "Base URL",
115
+ initialValue: this.draft.baseUrl,
116
+ placeholder: "Example: https://your-provider.example.com/v1",
117
+ validate: value => {
118
+ if (!value) return "Base URL is required.";
119
+ try {
120
+ const parsed = new URL(value);
121
+ if (!parsed.protocol.startsWith("http")) {
122
+ return "Base URL must start with http:// or https://";
123
+ }
124
+ return undefined;
125
+ } catch {
126
+ return "Enter a valid absolute URL.";
127
+ }
128
+ },
129
+ onSubmit: value => {
130
+ this.draft.baseUrl = value;
131
+ this.showApiKeyInput();
132
+ },
133
+ onBack: () => this.showLabelInput()
134
+ }));
135
+ }
136
+ showApiKeyInput() {
137
+ this.setScreen(new TextInputScreen({
138
+ stepLabel: "Step 5/8",
139
+ title: "API key",
140
+ description: "The key is stored in ~/.wepscli/agent/auth.json in plain text, as previously agreed.",
141
+ label: "API key",
142
+ initialValue: this.draft.apiKey,
143
+ placeholder: "Paste the API key for this provider profile",
144
+ validate: value => !value ? "API key is required." : undefined,
145
+ onSubmit: value => {
146
+ this.draft.apiKey = value;
147
+ this.showConnectionTest();
148
+ },
149
+ onBack: () => this.showBaseUrlInput()
150
+ }));
151
+ }
152
+ showConnectionTest() {
153
+ this.setScreen(new ActionScreen({
154
+ stepLabel: "Step 6/8",
155
+ title: "Connection test",
156
+ description: "This checks whether the endpoint responds to the selected provider family and credentials.",
157
+ idleMessage: "WEPSCLI will attempt a model-list request as a connectivity probe.",
158
+ run: async () => {
159
+ try {
160
+ const models = await fetchModelsForProfile(this.buildTemporaryProfile(), this.draft.apiKey);
161
+ this.draft.probeModels = models;
162
+ return {
163
+ ok: true,
164
+ message: `Connection test succeeded. ${models.length} model(s) were discovered during the probe.`
165
+ };
166
+ } catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ this.draft.probeModels = undefined;
169
+ return {
170
+ ok: false,
171
+ message
172
+ };
173
+ }
174
+ },
175
+ formatResult: result => ({
176
+ message: result.ok ? onboardingTheme.success(result.message) : onboardingTheme.error(result.message),
177
+ detail: result.ok ? "Press Enter to continue to model fetching." : "Press Enter to continue anyway or Esc to go back."
178
+ }),
179
+ onContinue: () => this.showModelFetch(),
180
+ onBack: () => this.showApiKeyInput(),
181
+ requestRender: () => this.ui.requestRender()
182
+ }));
183
+ }
184
+ showModelFetch() {
185
+ this.setScreen(new ActionScreen({
186
+ stepLabel: "Step 7/8",
187
+ title: "Fetch models",
188
+ description: "This step loads models for selection. If it fails, WEPSCLI will let you enter one manually.",
189
+ idleMessage: "Press Enter to fetch models for this provider profile.",
190
+ run: async () => {
191
+ try {
192
+ const models = this.draft.probeModels && this.draft.probeModels.length > 0 ? this.draft.probeModels : await fetchModelsForProfile(this.buildTemporaryProfile(), this.draft.apiKey);
193
+ this.draft.fetchedModels = models;
194
+ return {
195
+ ok: true,
196
+ models,
197
+ message: `Fetched ${models.length} model(s).`
198
+ };
199
+ } catch (error) {
200
+ const message = error instanceof Error ? error.message : String(error);
201
+ this.draft.fetchedModels = [];
202
+ return {
203
+ ok: false,
204
+ models: [],
205
+ message
206
+ };
207
+ }
208
+ },
209
+ formatResult: result => ({
210
+ message: result.ok ? onboardingTheme.success(result.message) : onboardingTheme.error(result.message),
211
+ detail: result.ok ? "Press Enter to pick a model from the list." : "Press Enter to switch to manual model entry or Esc to go back."
212
+ }),
213
+ onContinue: result => {
214
+ if (result.ok && result.models.length > 0) {
215
+ this.showModelSelect();
216
+ return;
217
+ }
218
+ this.showManualModelInput();
219
+ },
220
+ onBack: () => this.showConnectionTest(),
221
+ requestRender: () => this.ui.requestRender()
222
+ }));
223
+ }
224
+ showModelSelect() {
225
+ const items = [...this.draft.fetchedModels.map(model => ({
226
+ value: model.id,
227
+ label: model.name,
228
+ description: model.id
229
+ })), {
230
+ value: "__manual__",
231
+ label: "Enter model manually",
232
+ description: "Use this if the fetched list is incomplete or you prefer to type the model id yourself."
233
+ }];
234
+ this.setScreen(new SelectScreen({
235
+ stepLabel: "Step 7/8",
236
+ title: "Choose default model",
237
+ description: "Select a discovered model or switch to manual entry.",
238
+ items,
239
+ onSelect: item => {
240
+ if (item.value === "__manual__") {
241
+ this.showManualModelInput();
242
+ return;
243
+ }
244
+ const selected = this.draft.fetchedModels.find(model => model.id === item.value);
245
+ if (!selected) {
246
+ throw new Error(`Unknown model selection: ${item.value}`);
247
+ }
248
+ this.draft.selectedModel = selected;
249
+ this.draft.selectedModelSource = "fetched";
250
+ this.showReview();
251
+ },
252
+ onBack: () => this.showModelFetch()
253
+ }));
254
+ }
255
+ showManualModelInput() {
256
+ this.setScreen(new TextInputScreen({
257
+ stepLabel: "Step 7/8",
258
+ title: "Manual model entry",
259
+ description: "Use this when the provider does not return a model list or you prefer to type the model id directly.",
260
+ label: "Model id",
261
+ initialValue: this.draft.selectedModel?.id,
262
+ placeholder: "Example: gpt-4.1, claude-3-7-sonnet, or your provider-specific model id",
263
+ validate: value => !value ? "Model id is required." : undefined,
264
+ onSubmit: value => {
265
+ this.draft.selectedModel = {
266
+ id: value,
267
+ name: value,
268
+ family: this.draft.family
269
+ };
270
+ this.draft.selectedModelSource = "manual";
271
+ this.showReview();
272
+ },
273
+ onBack: () => this.draft.fetchedModels.length > 0 ? this.showModelSelect() : this.showModelFetch()
274
+ }));
275
+ }
276
+ showReview() {
277
+ const selectedModel = this.draft.selectedModel;
278
+ if (!selectedModel || !this.draft.family) {
279
+ throw new Error("Onboarding review requires a selected provider family and model.");
280
+ }
281
+ this.setScreen(new SummaryScreen({
282
+ stepLabel: "Step 8/8",
283
+ title: "Confirm and save",
284
+ description: "This will create the provider profile, store the API key, and make the selected model active.",
285
+ summaryLines: [`Label: ${this.draft.label}`, `Family: ${this.draft.family}`, `Base URL: ${this.draft.baseUrl}`, `Model: ${selectedModel.id}`, `Fetched models: ${this.draft.fetchedModels.length > 0 ? String(this.draft.fetchedModels.length) : "manual only"}`],
286
+ onConfirm: () => this.finishOnboarding(),
287
+ onBack: () => this.draft.selectedModelSource === "fetched" ? this.showModelSelect() : this.showManualModelInput()
288
+ }));
289
+ }
290
+ finishOnboarding() {
291
+ if (!this.draft.family || !this.draft.selectedModel) {
292
+ throw new Error("Onboarding is missing required selections.");
293
+ }
294
+ const profile = this.profiles.createProfile({
295
+ label: this.draft.label,
296
+ family: this.draft.family,
297
+ baseUrl: this.draft.baseUrl,
298
+ apiKey: this.draft.apiKey,
299
+ apiDialect: getDefaultDialect(this.draft.family)
300
+ });
301
+ const modelsToStore = this.draft.fetchedModels.length > 0 ? this.draft.fetchedModels : [this.draft.selectedModel];
302
+ this.profiles.replaceModels(profile.id, modelsToStore, {
303
+ status: this.draft.fetchedModels.length > 0 ? "ok" : "unknown"
304
+ });
305
+ this.profiles.setActiveSelection(profile.id, this.draft.selectedModel.id);
306
+ const saved = this.profiles.getProfile(profile.id);
307
+ if (!saved) {
308
+ throw new Error("Failed to reload the saved provider profile.");
309
+ }
310
+ this.onComplete(saved);
311
+ }
312
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Container","getKeybindings","Spacer","Text","fetchModelsForProfile","getDefaultDialect","ActionScreen","FramedScreen","SelectScreen","SummaryScreen","onboardingTheme","TextInputScreen","OnboardingApp","draft","label","baseUrl","apiKey","fetchedModels","constructor","ui","profiles","onComplete","onAbort","showWelcome","setScreen","screen","clear","currentScreen","addChild","setFocus","getFocusTarget","requestRender","buildTemporaryProfile","family","Error","now","Date","toISOString","id","apiDialect","enabled","models","createdAt","updatedAt","lastValidationStatus","onContinue","content","text","muted","setFooter","handleInput","data","keybindings","matches","showFamilySelect","items","value","description","stepLabel","title","onSelect","item","showLabelInput","onBack","initialValue","placeholder","validate","undefined","onSubmit","showBaseUrlInput","parsed","URL","protocol","startsWith","showApiKeyInput","showConnectionTest","idleMessage","run","probeModels","ok","message","length","error","String","formatResult","result","success","detail","showModelFetch","showModelSelect","showManualModelInput","map","model","name","selected","find","selectedModel","selectedModelSource","showReview","summaryLines","onConfirm","finishOnboarding","profile","createProfile","modelsToStore","replaceModels","status","setActiveSelection","saved","getProfile"],"sources":["onboarding-app.ts"],"sourcesContent":["import { Container, getKeybindings, type SelectItem, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\r\nimport { fetchModelsForProfile, type DiscoveredModel, getDefaultDialect, ProviderProfileService } from \"../provider-profiles/index.js\";\r\nimport type { ProviderFamily, ProviderProfile } from \"../provider-profiles/types.js\";\r\nimport { ActionScreen } from \"./action-screen.js\";\r\nimport { FramedScreen, type WizardScreen } from \"./framed-screen.js\";\r\nimport { SelectScreen } from \"./select-screen.js\";\r\nimport { SummaryScreen } from \"./summary-screen.js\";\r\nimport { onboardingTheme } from \"./theme.js\";\r\nimport { TextInputScreen } from \"./text-input-screen.js\";\r\n\r\ninterface OnboardingDraft {\r\n\tfamily?: ProviderFamily;\r\n\tlabel: string;\r\n\tbaseUrl: string;\r\n\tapiKey: string;\r\n\tprobeModels?: DiscoveredModel[];\r\n\tfetchedModels: DiscoveredModel[];\r\n\tselectedModel?: DiscoveredModel;\r\n\tselectedModelSource?: \"fetched\" | \"manual\";\r\n}\r\n\r\nexport class OnboardingApp extends Container {\r\n\tprivate readonly draft: OnboardingDraft = {\r\n\t\tlabel: \"\",\r\n\t\tbaseUrl: \"\",\r\n\t\tapiKey: \"\",\r\n\t\tfetchedModels: [],\r\n\t};\r\n\tprivate currentScreen: WizardScreen | undefined;\r\n\r\n\tconstructor(\r\n\t\tprivate readonly ui: TUI,\r\n\t\tprivate readonly profiles: ProviderProfileService,\r\n\t\tprivate readonly onComplete: (profile: ProviderProfile) => void,\r\n\t\tprivate readonly onAbort: () => void,\r\n\t) {\r\n\t\tsuper();\r\n\t\tthis.showWelcome();\r\n\t}\r\n\r\n\tprivate setScreen(screen: WizardScreen): void {\r\n\t\tthis.clear();\r\n\t\tthis.currentScreen = screen;\r\n\t\tthis.addChild(screen);\r\n\t\tthis.ui.setFocus(screen.getFocusTarget());\r\n\t\tthis.ui.requestRender();\r\n\t}\r\n\r\n\tprivate buildTemporaryProfile(): ProviderProfile {\r\n\t\tif (!this.draft.family) {\r\n\t\t\tthrow new Error(\"Provider family is not set\");\r\n\t\t}\r\n\r\n\t\tconst now = new Date().toISOString();\r\n\t\treturn {\r\n\t\t\tid: \"draft\",\r\n\t\t\tlabel: this.draft.label || \"Draft profile\",\r\n\t\t\tfamily: this.draft.family,\r\n\t\t\tapiDialect: getDefaultDialect(this.draft.family),\r\n\t\t\tbaseUrl: this.draft.baseUrl,\r\n\t\t\tenabled: true,\r\n\t\t\tmodels: [],\r\n\t\t\tcreatedAt: now,\r\n\t\t\tupdatedAt: now,\r\n\t\t\tlastValidationStatus: \"unknown\",\r\n\t\t};\r\n\t}\r\n\r\n\tprivate showWelcome(): void {\r\n\t\tconst screen = new (class extends FramedScreen {\r\n\t\t\tconstructor(onContinue: () => void, onAbort: () => void) {\r\n\t\t\t\tsuper(\"Step 1/8\", \"Welcome to WEPSCLI\", \"A guided first-run setup is required before entering the app.\");\r\n\t\t\t\tthis.content.addChild(new Text(onboardingTheme.text(\"This wizard will create your first provider profile.\"), 0, 0));\r\n\t\t\t\tthis.content.addChild(new Spacer(1));\r\n\t\t\t\tthis.content.addChild(\r\n\t\t\t\t\tnew Text(\r\n\t\t\t\t\t\tonboardingTheme.muted(\"You will choose a provider family, enter a base URL and API key, test the endpoint, and select a model.\"),\r\n\t\t\t\t\t\t0,\r\n\t\t\t\t\t\t0,\r\n\t\t\t\t\t),\r\n\t\t\t\t);\r\n\t\t\t\tthis.setFooter(\"Enter continue | Esc quit\");\r\n\t\t\t\tthis.handleInput = (data: string) => {\r\n\t\t\t\t\tconst keybindings = getKeybindings();\r\n\t\t\t\t\tif (keybindings.matches(data, \"tui.select.confirm\")) {\r\n\t\t\t\t\t\tonContinue();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\r\n\t\t\t\t\t\tonAbort();\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\t\t\t}\r\n\t\t})(() => this.showFamilySelect(), this.onAbort);\r\n\r\n\t\tthis.setScreen(screen);\r\n\t}\r\n\r\n\tprivate showFamilySelect(): void {\r\n\t\tconst items: SelectItem[] = [\r\n\t\t\t{ value: \"openai\", label: \"OpenAI-style\", description: \"For third-party endpoints that expose an OpenAI-compatible API.\" },\r\n\t\t\t{ value: \"anthropic\", label: \"Anthropic-style\", description: \"For third-party endpoints that expose an Anthropic-compatible API.\" },\r\n\t\t];\r\n\r\n\t\tthis.setScreen(\r\n\t\t\tnew SelectScreen({\r\n\t\t\t\tstepLabel: \"Step 2/8\",\r\n\t\t\t\ttitle: \"Choose provider family\",\r\n\t\t\t\tdescription: \"This decides which model-listing request shape WEPSCLI will use.\",\r\n\t\t\t\titems,\r\n\t\t\t\tonSelect: (item) => {\r\n\t\t\t\t\tthis.draft.family = item.value as ProviderFamily;\r\n\t\t\t\t\tthis.showLabelInput();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showWelcome(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showLabelInput(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew TextInputScreen({\r\n\t\t\t\tstepLabel: \"Step 3/8\",\r\n\t\t\t\ttitle: \"Provider label\",\r\n\t\t\t\tdescription: \"This is the friendly name shown inside WEPSCLI when you switch providers.\",\r\n\t\t\t\tlabel: \"Label\",\r\n\t\t\t\tinitialValue: this.draft.label,\r\n\t\t\t\tplaceholder: \"Example: My OpenAI Proxy\",\r\n\t\t\t\tvalidate: (value) => (!value ? \"Provider label is required.\" : undefined),\r\n\t\t\t\tonSubmit: (value) => {\r\n\t\t\t\t\tthis.draft.label = value;\r\n\t\t\t\t\tthis.showBaseUrlInput();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showFamilySelect(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showBaseUrlInput(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew TextInputScreen({\r\n\t\t\t\tstepLabel: \"Step 4/8\",\r\n\t\t\t\ttitle: \"Base URL\",\r\n\t\t\t\tdescription: \"WEPSCLI will use this value exactly as entered. No automatic /v1 normalization will be applied.\",\r\n\t\t\t\tlabel: \"Base URL\",\r\n\t\t\t\tinitialValue: this.draft.baseUrl,\r\n\t\t\t\tplaceholder: \"Example: https://your-provider.example.com/v1\",\r\n\t\t\t\tvalidate: (value) => {\r\n\t\t\t\t\tif (!value) return \"Base URL is required.\";\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst parsed = new URL(value);\r\n\t\t\t\t\t\tif (!parsed.protocol.startsWith(\"http\")) {\r\n\t\t\t\t\t\t\treturn \"Base URL must start with http:// or https://\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn undefined;\r\n\t\t\t\t\t} catch {\r\n\t\t\t\t\t\treturn \"Enter a valid absolute URL.\";\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tonSubmit: (value) => {\r\n\t\t\t\t\tthis.draft.baseUrl = value;\r\n\t\t\t\t\tthis.showApiKeyInput();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showLabelInput(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showApiKeyInput(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew TextInputScreen({\r\n\t\t\t\tstepLabel: \"Step 5/8\",\r\n\t\t\t\ttitle: \"API key\",\r\n\t\t\t\tdescription: \"The key is stored in ~/.wepscli/agent/auth.json in plain text, as previously agreed.\",\r\n\t\t\t\tlabel: \"API key\",\r\n\t\t\t\tinitialValue: this.draft.apiKey,\r\n\t\t\t\tplaceholder: \"Paste the API key for this provider profile\",\r\n\t\t\t\tvalidate: (value) => (!value ? \"API key is required.\" : undefined),\r\n\t\t\t\tonSubmit: (value) => {\r\n\t\t\t\t\tthis.draft.apiKey = value;\r\n\t\t\t\t\tthis.showConnectionTest();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showBaseUrlInput(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showConnectionTest(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew ActionScreen({\r\n\t\t\t\tstepLabel: \"Step 6/8\",\r\n\t\t\t\ttitle: \"Connection test\",\r\n\t\t\t\tdescription: \"This checks whether the endpoint responds to the selected provider family and credentials.\",\r\n\t\t\t\tidleMessage: \"WEPSCLI will attempt a model-list request as a connectivity probe.\",\r\n\t\t\t\trun: async () => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst models = await fetchModelsForProfile(this.buildTemporaryProfile(), this.draft.apiKey);\r\n\t\t\t\t\t\tthis.draft.probeModels = models;\r\n\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\tok: true,\r\n\t\t\t\t\t\t\tmessage: `Connection test succeeded. ${models.length} model(s) were discovered during the probe.`,\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t} catch (error) {\r\n\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\r\n\t\t\t\t\t\tthis.draft.probeModels = undefined;\r\n\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\tok: false,\r\n\t\t\t\t\t\t\tmessage,\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tformatResult: (result) => ({\r\n\t\t\t\t\tmessage: result.ok ? onboardingTheme.success(result.message) : onboardingTheme.error(result.message),\r\n\t\t\t\t\tdetail: result.ok\r\n\t\t\t\t\t\t? \"Press Enter to continue to model fetching.\"\r\n\t\t\t\t\t\t: \"Press Enter to continue anyway or Esc to go back.\",\r\n\t\t\t\t}),\r\n\t\t\t\tonContinue: () => this.showModelFetch(),\r\n\t\t\t\tonBack: () => this.showApiKeyInput(),\r\n\t\t\t\trequestRender: () => this.ui.requestRender(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showModelFetch(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew ActionScreen({\r\n\t\t\t\tstepLabel: \"Step 7/8\",\r\n\t\t\t\ttitle: \"Fetch models\",\r\n\t\t\t\tdescription: \"This step loads models for selection. If it fails, WEPSCLI will let you enter one manually.\",\r\n\t\t\t\tidleMessage: \"Press Enter to fetch models for this provider profile.\",\r\n\t\t\t\trun: async () => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tconst models =\r\n\t\t\t\t\t\t\tthis.draft.probeModels && this.draft.probeModels.length > 0\r\n\t\t\t\t\t\t\t\t? this.draft.probeModels\r\n\t\t\t\t\t\t\t\t: await fetchModelsForProfile(this.buildTemporaryProfile(), this.draft.apiKey);\r\n\t\t\t\t\t\tthis.draft.fetchedModels = models;\r\n\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\tok: true,\r\n\t\t\t\t\t\t\tmodels,\r\n\t\t\t\t\t\t\tmessage: `Fetched ${models.length} model(s).`,\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t} catch (error) {\r\n\t\t\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\r\n\t\t\t\t\t\tthis.draft.fetchedModels = [];\r\n\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\tok: false,\r\n\t\t\t\t\t\t\tmodels: [] as DiscoveredModel[],\r\n\t\t\t\t\t\t\tmessage,\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tformatResult: (result) => ({\r\n\t\t\t\t\tmessage: result.ok ? onboardingTheme.success(result.message) : onboardingTheme.error(result.message),\r\n\t\t\t\t\tdetail: result.ok\r\n\t\t\t\t\t\t? \"Press Enter to pick a model from the list.\"\r\n\t\t\t\t\t\t: \"Press Enter to switch to manual model entry or Esc to go back.\",\r\n\t\t\t\t}),\r\n\t\t\t\tonContinue: (result) => {\r\n\t\t\t\t\tif (result.ok && result.models.length > 0) {\r\n\t\t\t\t\t\tthis.showModelSelect();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.showManualModelInput();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showConnectionTest(),\r\n\t\t\t\trequestRender: () => this.ui.requestRender(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showModelSelect(): void {\r\n\t\tconst items: SelectItem[] = [\r\n\t\t\t...this.draft.fetchedModels.map((model) => ({\r\n\t\t\t\tvalue: model.id,\r\n\t\t\t\tlabel: model.name,\r\n\t\t\t\tdescription: model.id,\r\n\t\t\t})),\r\n\t\t\t{\r\n\t\t\t\tvalue: \"__manual__\",\r\n\t\t\t\tlabel: \"Enter model manually\",\r\n\t\t\t\tdescription: \"Use this if the fetched list is incomplete or you prefer to type the model id yourself.\",\r\n\t\t\t},\r\n\t\t];\r\n\r\n\t\tthis.setScreen(\r\n\t\t\tnew SelectScreen({\r\n\t\t\t\tstepLabel: \"Step 7/8\",\r\n\t\t\t\ttitle: \"Choose default model\",\r\n\t\t\t\tdescription: \"Select a discovered model or switch to manual entry.\",\r\n\t\t\t\titems,\r\n\t\t\t\tonSelect: (item) => {\r\n\t\t\t\t\tif (item.value === \"__manual__\") {\r\n\t\t\t\t\t\tthis.showManualModelInput();\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tconst selected = this.draft.fetchedModels.find((model) => model.id === item.value);\r\n\t\t\t\t\tif (!selected) {\r\n\t\t\t\t\t\tthrow new Error(`Unknown model selection: ${item.value}`);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.draft.selectedModel = selected;\r\n\t\t\t\t\tthis.draft.selectedModelSource = \"fetched\";\r\n\t\t\t\t\tthis.showReview();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => this.showModelFetch(),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showManualModelInput(): void {\r\n\t\tthis.setScreen(\r\n\t\t\tnew TextInputScreen({\r\n\t\t\t\tstepLabel: \"Step 7/8\",\r\n\t\t\t\ttitle: \"Manual model entry\",\r\n\t\t\t\tdescription: \"Use this when the provider does not return a model list or you prefer to type the model id directly.\",\r\n\t\t\t\tlabel: \"Model id\",\r\n\t\t\t\tinitialValue: this.draft.selectedModel?.id,\r\n\t\t\t\tplaceholder: \"Example: gpt-4.1, claude-3-7-sonnet, or your provider-specific model id\",\r\n\t\t\t\tvalidate: (value) => (!value ? \"Model id is required.\" : undefined),\r\n\t\t\t\tonSubmit: (value) => {\r\n\t\t\t\t\tthis.draft.selectedModel = {\r\n\t\t\t\t\t\tid: value,\r\n\t\t\t\t\t\tname: value,\r\n\t\t\t\t\t\tfamily: this.draft.family!,\r\n\t\t\t\t\t};\r\n\t\t\t\t\tthis.draft.selectedModelSource = \"manual\";\r\n\t\t\t\t\tthis.showReview();\r\n\t\t\t\t},\r\n\t\t\t\tonBack: () => (this.draft.fetchedModels.length > 0 ? this.showModelSelect() : this.showModelFetch()),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate showReview(): void {\r\n\t\tconst selectedModel = this.draft.selectedModel;\r\n\t\tif (!selectedModel || !this.draft.family) {\r\n\t\t\tthrow new Error(\"Onboarding review requires a selected provider family and model.\");\r\n\t\t}\r\n\r\n\t\tthis.setScreen(\r\n\t\t\tnew SummaryScreen({\r\n\t\t\t\tstepLabel: \"Step 8/8\",\r\n\t\t\t\ttitle: \"Confirm and save\",\r\n\t\t\t\tdescription: \"This will create the provider profile, store the API key, and make the selected model active.\",\r\n\t\t\t\tsummaryLines: [\r\n\t\t\t\t\t`Label: ${this.draft.label}`,\r\n\t\t\t\t\t`Family: ${this.draft.family}`,\r\n\t\t\t\t\t`Base URL: ${this.draft.baseUrl}`,\r\n\t\t\t\t\t`Model: ${selectedModel.id}`,\r\n\t\t\t\t\t`Fetched models: ${this.draft.fetchedModels.length > 0 ? String(this.draft.fetchedModels.length) : \"manual only\"}`,\r\n\t\t\t\t],\r\n\t\t\t\tonConfirm: () => this.finishOnboarding(),\r\n\t\t\t\tonBack: () => (this.draft.selectedModelSource === \"fetched\" ? this.showModelSelect() : this.showManualModelInput()),\r\n\t\t\t}),\r\n\t\t);\r\n\t}\r\n\r\n\tprivate finishOnboarding(): void {\r\n\t\tif (!this.draft.family || !this.draft.selectedModel) {\r\n\t\t\tthrow new Error(\"Onboarding is missing required selections.\");\r\n\t\t}\r\n\r\n\t\tconst profile = this.profiles.createProfile({\r\n\t\t\tlabel: this.draft.label,\r\n\t\t\tfamily: this.draft.family,\r\n\t\t\tbaseUrl: this.draft.baseUrl,\r\n\t\t\tapiKey: this.draft.apiKey,\r\n\t\t\tapiDialect: getDefaultDialect(this.draft.family),\r\n\t\t});\r\n\r\n\t\tconst modelsToStore =\r\n\t\t\tthis.draft.fetchedModels.length > 0\r\n\t\t\t\t? this.draft.fetchedModels\r\n\t\t\t\t: [this.draft.selectedModel];\r\n\r\n\t\tthis.profiles.replaceModels(profile.id, modelsToStore, {\r\n\t\t\tstatus: this.draft.fetchedModels.length > 0 ? \"ok\" : \"unknown\",\r\n\t\t});\r\n\t\tthis.profiles.setActiveSelection(profile.id, this.draft.selectedModel.id);\r\n\r\n\t\tconst saved = this.profiles.getProfile(profile.id);\r\n\t\tif (!saved) {\r\n\t\t\tthrow new Error(\"Failed to reload the saved provider profile.\");\r\n\t\t}\r\n\r\n\t\tthis.onComplete(saved);\r\n\t}\r\n}\r\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,cAAc,EAAmBC,MAAM,EAAEC,IAAI,QAAkB,sBAAsB;AACzG,SAASC,qBAAqB,EAAwBC,iBAAiB,QAAgC,+BAA+B;AAEtI,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAASC,YAAY,QAA2B,oBAAoB;AACpE,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,eAAe,QAAQ,YAAY;AAC5C,SAASC,eAAe,QAAQ,wBAAwB;AAaxD,OAAO,MAAMC,aAAa,SAASZ,SAAS,CAAC;EAC3Ba,KAAK,GAAoB;IACzCC,KAAK,EAAE,EAAE;IACTC,OAAO,EAAE,EAAE;IACXC,MAAM,EAAE,EAAE;IACVC,aAAa,EAAE;EAChB,CAAC;EAGDC,WAAWA,CACOC,EAAO,EACPC,QAAgC,EAChCC,UAA8C,EAC9CC,OAAmB,EACnC;IACD,KAAK,CAAC,CAAC;IAAC,KALSH,EAAO,GAAPA,EAAO;IAAA,KACPC,QAAgC,GAAhCA,QAAgC;IAAA,KAChCC,UAA8C,GAA9CA,UAA8C;IAAA,KAC9CC,OAAmB,GAAnBA,OAAmB;IAGpC,IAAI,CAACC,WAAW,CAAC,CAAC;EACnB;EAEQC,SAASA,CAACC,MAAoB,EAAQ;IAC7C,IAAI,CAACC,KAAK,CAAC,CAAC;IACZ,IAAI,CAACC,aAAa,GAAGF,MAAM;IAC3B,IAAI,CAACG,QAAQ,CAACH,MAAM,CAAC;IACrB,IAAI,CAACN,EAAE,CAACU,QAAQ,CAACJ,MAAM,CAACK,cAAc,CAAC,CAAC,CAAC;IACzC,IAAI,CAACX,EAAE,CAACY,aAAa,CAAC,CAAC;EACxB;EAEQC,qBAAqBA,CAAA,EAAoB;IAChD,IAAI,CAAC,IAAI,CAACnB,KAAK,CAACoB,MAAM,EAAE;MACvB,MAAM,IAAIC,KAAK,CAAC,4BAA4B,CAAC;IAC9C;IAEA,MAAMC,GAAG,GAAG,IAAIC,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;IACpC,OAAO;MACNC,EAAE,EAAE,OAAO;MACXxB,KAAK,EAAE,IAAI,CAACD,KAAK,CAACC,KAAK,IAAI,eAAe;MAC1CmB,MAAM,EAAE,IAAI,CAACpB,KAAK,CAACoB,MAAM;MACzBM,UAAU,EAAElC,iBAAiB,CAAC,IAAI,CAACQ,KAAK,CAACoB,MAAM,CAAC;MAChDlB,OAAO,EAAE,IAAI,CAACF,KAAK,CAACE,OAAO;MAC3ByB,OAAO,EAAE,IAAI;MACbC,MAAM,EAAE,EAAE;MACVC,SAAS,EAAEP,GAAG;MACdQ,SAAS,EAAER,GAAG;MACdS,oBAAoB,EAAE;IACvB,CAAC;EACF;EAEQrB,WAAWA,CAAA,EAAS;IAC3B,MAAME,MAAM,GAAG,IAAK,cAAclB,YAAY,CAAC;MAC9CW,WAAWA,CAAC2B,UAAsB,EAAEvB,OAAmB,EAAE;QACxD,KAAK,CAAC,UAAU,EAAE,oBAAoB,EAAE,+DAA+D,CAAC;QACxG,IAAI,CAACwB,OAAO,CAAClB,QAAQ,CAAC,IAAIzB,IAAI,CAACO,eAAe,CAACqC,IAAI,CAAC,sDAAsD,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACnH,IAAI,CAACD,OAAO,CAAClB,QAAQ,CAAC,IAAI1B,MAAM,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC4C,OAAO,CAAClB,QAAQ,CACpB,IAAIzB,IAAI,CACPO,eAAe,CAACsC,KAAK,CAAC,yGAAyG,CAAC,EAChI,CAAC,EACD,CACD,CACD,CAAC;QACD,IAAI,CAACC,SAAS,CAAC,2BAA2B,CAAC;QAC3C,IAAI,CAACC,WAAW,GAAIC,IAAY,IAAK;UACpC,MAAMC,WAAW,GAAGnD,cAAc,CAAC,CAAC;UACpC,IAAImD,WAAW,CAACC,OAAO,CAACF,IAAI,EAAE,oBAAoB,CAAC,EAAE;YACpDN,UAAU,CAAC,CAAC;YACZ;UACD;UACA,IAAIO,WAAW,CAACC,OAAO,CAACF,IAAI,EAAE,mBAAmB,CAAC,EAAE;YACnD7B,OAAO,CAAC,CAAC;UACV;QACD,CAAC;MACF;IACD,CAAC,CAAE,MAAM,IAAI,CAACgC,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAChC,OAAO,CAAC;IAE/C,IAAI,CAACE,SAAS,CAACC,MAAM,CAAC;EACvB;EAEQ6B,gBAAgBA,CAAA,EAAS;IAChC,MAAMC,KAAmB,GAAG,CAC3B;MAAEC,KAAK,EAAE,QAAQ;MAAE1C,KAAK,EAAE,cAAc;MAAE2C,WAAW,EAAE;IAAkE,CAAC,EAC1H;MAAED,KAAK,EAAE,WAAW;MAAE1C,KAAK,EAAE,iBAAiB;MAAE2C,WAAW,EAAE;IAAqE,CAAC,CACnI;IAED,IAAI,CAACjC,SAAS,CACb,IAAIhB,YAAY,CAAC;MAChBkD,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,wBAAwB;MAC/BF,WAAW,EAAE,kEAAkE;MAC/EF,KAAK;MACLK,QAAQ,EAAGC,IAAI,IAAK;QACnB,IAAI,CAAChD,KAAK,CAACoB,MAAM,GAAG4B,IAAI,CAACL,KAAuB;QAChD,IAAI,CAACM,cAAc,CAAC,CAAC;MACtB,CAAC;MACDC,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACxC,WAAW,CAAC;IAChC,CAAC,CACF,CAAC;EACF;EAEQuC,cAAcA,CAAA,EAAS;IAC9B,IAAI,CAACtC,SAAS,CACb,IAAIb,eAAe,CAAC;MACnB+C,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,gBAAgB;MACvBF,WAAW,EAAE,2EAA2E;MACxF3C,KAAK,EAAE,OAAO;MACdkD,YAAY,EAAE,IAAI,CAACnD,KAAK,CAACC,KAAK;MAC9BmD,WAAW,EAAE,0BAA0B;MACvCC,QAAQ,EAAGV,KAAK,IAAM,CAACA,KAAK,GAAG,6BAA6B,GAAGW,SAAU;MACzEC,QAAQ,EAAGZ,KAAK,IAAK;QACpB,IAAI,CAAC3C,KAAK,CAACC,KAAK,GAAG0C,KAAK;QACxB,IAAI,CAACa,gBAAgB,CAAC,CAAC;MACxB,CAAC;MACDN,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACT,gBAAgB,CAAC;IACrC,CAAC,CACF,CAAC;EACF;EAEQe,gBAAgBA,CAAA,EAAS;IAChC,IAAI,CAAC7C,SAAS,CACb,IAAIb,eAAe,CAAC;MACnB+C,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,UAAU;MACjBF,WAAW,EAAE,iGAAiG;MAC9G3C,KAAK,EAAE,UAAU;MACjBkD,YAAY,EAAE,IAAI,CAACnD,KAAK,CAACE,OAAO;MAChCkD,WAAW,EAAE,+CAA+C;MAC5DC,QAAQ,EAAGV,KAAK,IAAK;QACpB,IAAI,CAACA,KAAK,EAAE,OAAO,uBAAuB;QAC1C,IAAI;UACH,MAAMc,MAAM,GAAG,IAAIC,GAAG,CAACf,KAAK,CAAC;UAC7B,IAAI,CAACc,MAAM,CAACE,QAAQ,CAACC,UAAU,CAAC,MAAM,CAAC,EAAE;YACxC,OAAO,8CAA8C;UACtD;UACA,OAAON,SAAS;QACjB,CAAC,CAAC,MAAM;UACP,OAAO,6BAA6B;QACrC;MACD,CAAC;MACDC,QAAQ,EAAGZ,KAAK,IAAK;QACpB,IAAI,CAAC3C,KAAK,CAACE,OAAO,GAAGyC,KAAK;QAC1B,IAAI,CAACkB,eAAe,CAAC,CAAC;MACvB,CAAC;MACDX,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACD,cAAc,CAAC;IACnC,CAAC,CACF,CAAC;EACF;EAEQY,eAAeA,CAAA,EAAS;IAC/B,IAAI,CAAClD,SAAS,CACb,IAAIb,eAAe,CAAC;MACnB+C,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,SAAS;MAChBF,WAAW,EAAE,sFAAsF;MACnG3C,KAAK,EAAE,SAAS;MAChBkD,YAAY,EAAE,IAAI,CAACnD,KAAK,CAACG,MAAM;MAC/BiD,WAAW,EAAE,6CAA6C;MAC1DC,QAAQ,EAAGV,KAAK,IAAM,CAACA,KAAK,GAAG,sBAAsB,GAAGW,SAAU;MAClEC,QAAQ,EAAGZ,KAAK,IAAK;QACpB,IAAI,CAAC3C,KAAK,CAACG,MAAM,GAAGwC,KAAK;QACzB,IAAI,CAACmB,kBAAkB,CAAC,CAAC;MAC1B,CAAC;MACDZ,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACM,gBAAgB,CAAC;IACrC,CAAC,CACF,CAAC;EACF;EAEQM,kBAAkBA,CAAA,EAAS;IAClC,IAAI,CAACnD,SAAS,CACb,IAAIlB,YAAY,CAAC;MAChBoD,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,iBAAiB;MACxBF,WAAW,EAAE,4FAA4F;MACzGmB,WAAW,EAAE,oEAAoE;MACjFC,GAAG,EAAE,MAAAA,CAAA,KAAY;QAChB,IAAI;UACH,MAAMpC,MAAM,GAAG,MAAMrC,qBAAqB,CAAC,IAAI,CAAC4B,qBAAqB,CAAC,CAAC,EAAE,IAAI,CAACnB,KAAK,CAACG,MAAM,CAAC;UAC3F,IAAI,CAACH,KAAK,CAACiE,WAAW,GAAGrC,MAAM;UAC/B,OAAO;YACNsC,EAAE,EAAE,IAAI;YACRC,OAAO,EAAE,8BAA8BvC,MAAM,CAACwC,MAAM;UACrD,CAAC;QACF,CAAC,CAAC,OAAOC,KAAK,EAAE;UACf,MAAMF,OAAO,GAAGE,KAAK,YAAYhD,KAAK,GAAGgD,KAAK,CAACF,OAAO,GAAGG,MAAM,CAACD,KAAK,CAAC;UACtE,IAAI,CAACrE,KAAK,CAACiE,WAAW,GAAGX,SAAS;UAClC,OAAO;YACNY,EAAE,EAAE,KAAK;YACTC;UACD,CAAC;QACF;MACD,CAAC;MACDI,YAAY,EAAGC,MAAM,KAAM;QAC1BL,OAAO,EAAEK,MAAM,CAACN,EAAE,GAAGrE,eAAe,CAAC4E,OAAO,CAACD,MAAM,CAACL,OAAO,CAAC,GAAGtE,eAAe,CAACwE,KAAK,CAACG,MAAM,CAACL,OAAO,CAAC;QACpGO,MAAM,EAAEF,MAAM,CAACN,EAAE,GACd,4CAA4C,GAC5C;MACJ,CAAC,CAAC;MACFlC,UAAU,EAAEA,CAAA,KAAM,IAAI,CAAC2C,cAAc,CAAC,CAAC;MACvCzB,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACW,eAAe,CAAC,CAAC;MACpC3C,aAAa,EAAEA,CAAA,KAAM,IAAI,CAACZ,EAAE,CAACY,aAAa,CAAC;IAC5C,CAAC,CACF,CAAC;EACF;EAEQyD,cAAcA,CAAA,EAAS;IAC9B,IAAI,CAAChE,SAAS,CACb,IAAIlB,YAAY,CAAC;MAChBoD,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,cAAc;MACrBF,WAAW,EAAE,6FAA6F;MAC1GmB,WAAW,EAAE,wDAAwD;MACrEC,GAAG,EAAE,MAAAA,CAAA,KAAY;QAChB,IAAI;UACH,MAAMpC,MAAM,GACX,IAAI,CAAC5B,KAAK,CAACiE,WAAW,IAAI,IAAI,CAACjE,KAAK,CAACiE,WAAW,CAACG,MAAM,GAAG,CAAC,GACxD,IAAI,CAACpE,KAAK,CAACiE,WAAW,GACtB,MAAM1E,qBAAqB,CAAC,IAAI,CAAC4B,qBAAqB,CAAC,CAAC,EAAE,IAAI,CAACnB,KAAK,CAACG,MAAM,CAAC;UAChF,IAAI,CAACH,KAAK,CAACI,aAAa,GAAGwB,MAAM;UACjC,OAAO;YACNsC,EAAE,EAAE,IAAI;YACRtC,MAAM;YACNuC,OAAO,EAAE,WAAWvC,MAAM,CAACwC,MAAM;UAClC,CAAC;QACF,CAAC,CAAC,OAAOC,KAAK,EAAE;UACf,MAAMF,OAAO,GAAGE,KAAK,YAAYhD,KAAK,GAAGgD,KAAK,CAACF,OAAO,GAAGG,MAAM,CAACD,KAAK,CAAC;UACtE,IAAI,CAACrE,KAAK,CAACI,aAAa,GAAG,EAAE;UAC7B,OAAO;YACN8D,EAAE,EAAE,KAAK;YACTtC,MAAM,EAAE,EAAuB;YAC/BuC;UACD,CAAC;QACF;MACD,CAAC;MACDI,YAAY,EAAGC,MAAM,KAAM;QAC1BL,OAAO,EAAEK,MAAM,CAACN,EAAE,GAAGrE,eAAe,CAAC4E,OAAO,CAACD,MAAM,CAACL,OAAO,CAAC,GAAGtE,eAAe,CAACwE,KAAK,CAACG,MAAM,CAACL,OAAO,CAAC;QACpGO,MAAM,EAAEF,MAAM,CAACN,EAAE,GACd,4CAA4C,GAC5C;MACJ,CAAC,CAAC;MACFlC,UAAU,EAAGwC,MAAM,IAAK;QACvB,IAAIA,MAAM,CAACN,EAAE,IAAIM,MAAM,CAAC5C,MAAM,CAACwC,MAAM,GAAG,CAAC,EAAE;UAC1C,IAAI,CAACQ,eAAe,CAAC,CAAC;UACtB;QACD;QACA,IAAI,CAACC,oBAAoB,CAAC,CAAC;MAC5B,CAAC;MACD3B,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACY,kBAAkB,CAAC,CAAC;MACvC5C,aAAa,EAAEA,CAAA,KAAM,IAAI,CAACZ,EAAE,CAACY,aAAa,CAAC;IAC5C,CAAC,CACF,CAAC;EACF;EAEQ0D,eAAeA,CAAA,EAAS;IAC/B,MAAMlC,KAAmB,GAAG,CAC3B,GAAG,IAAI,CAAC1C,KAAK,CAACI,aAAa,CAAC0E,GAAG,CAAEC,KAAK,KAAM;MAC3CpC,KAAK,EAAEoC,KAAK,CAACtD,EAAE;MACfxB,KAAK,EAAE8E,KAAK,CAACC,IAAI;MACjBpC,WAAW,EAAEmC,KAAK,CAACtD;IACpB,CAAC,CAAC,CAAC,EACH;MACCkB,KAAK,EAAE,YAAY;MACnB1C,KAAK,EAAE,sBAAsB;MAC7B2C,WAAW,EAAE;IACd,CAAC,CACD;IAED,IAAI,CAACjC,SAAS,CACb,IAAIhB,YAAY,CAAC;MAChBkD,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,sBAAsB;MAC7BF,WAAW,EAAE,sDAAsD;MACnEF,KAAK;MACLK,QAAQ,EAAGC,IAAI,IAAK;QACnB,IAAIA,IAAI,CAACL,KAAK,KAAK,YAAY,EAAE;UAChC,IAAI,CAACkC,oBAAoB,CAAC,CAAC;UAC3B;QACD;QACA,MAAMI,QAAQ,GAAG,IAAI,CAACjF,KAAK,CAACI,aAAa,CAAC8E,IAAI,CAAEH,KAAK,IAAKA,KAAK,CAACtD,EAAE,KAAKuB,IAAI,CAACL,KAAK,CAAC;QAClF,IAAI,CAACsC,QAAQ,EAAE;UACd,MAAM,IAAI5D,KAAK,CAAC,4BAA4B2B,IAAI,CAACL,KAAK,EAAE,CAAC;QAC1D;QACA,IAAI,CAAC3C,KAAK,CAACmF,aAAa,GAAGF,QAAQ;QACnC,IAAI,CAACjF,KAAK,CAACoF,mBAAmB,GAAG,SAAS;QAC1C,IAAI,CAACC,UAAU,CAAC,CAAC;MAClB,CAAC;MACDnC,MAAM,EAAEA,CAAA,KAAM,IAAI,CAACyB,cAAc,CAAC;IACnC,CAAC,CACF,CAAC;EACF;EAEQE,oBAAoBA,CAAA,EAAS;IACpC,IAAI,CAAClE,SAAS,CACb,IAAIb,eAAe,CAAC;MACnB+C,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,oBAAoB;MAC3BF,WAAW,EAAE,sGAAsG;MACnH3C,KAAK,EAAE,UAAU;MACjBkD,YAAY,EAAE,IAAI,CAACnD,KAAK,CAACmF,aAAa,EAAE1D,EAAE;MAC1C2B,WAAW,EAAE,yEAAyE;MACtFC,QAAQ,EAAGV,KAAK,IAAM,CAACA,KAAK,GAAG,uBAAuB,GAAGW,SAAU;MACnEC,QAAQ,EAAGZ,KAAK,IAAK;QACpB,IAAI,CAAC3C,KAAK,CAACmF,aAAa,GAAG;UAC1B1D,EAAE,EAAEkB,KAAK;UACTqC,IAAI,EAAErC,KAAK;UACXvB,MAAM,EAAE,IAAI,CAACpB,KAAK,CAACoB;QACpB,CAAC;QACD,IAAI,CAACpB,KAAK,CAACoF,mBAAmB,GAAG,QAAQ;QACzC,IAAI,CAACC,UAAU,CAAC,CAAC;MAClB,CAAC;MACDnC,MAAM,EAAEA,CAAA,KAAO,IAAI,CAAClD,KAAK,CAACI,aAAa,CAACgE,MAAM,GAAG,CAAC,GAAG,IAAI,CAACQ,eAAe,CAAC,CAAC,GAAG,IAAI,CAACD,cAAc,CAAC;IACnG,CAAC,CACF,CAAC;EACF;EAEQU,UAAUA,CAAA,EAAS;IAC1B,MAAMF,aAAa,GAAG,IAAI,CAACnF,KAAK,CAACmF,aAAa;IAC9C,IAAI,CAACA,aAAa,IAAI,CAAC,IAAI,CAACnF,KAAK,CAACoB,MAAM,EAAE;MACzC,MAAM,IAAIC,KAAK,CAAC,kEAAkE,CAAC;IACpF;IAEA,IAAI,CAACV,SAAS,CACb,IAAIf,aAAa,CAAC;MACjBiD,SAAS,EAAE,UAAU;MACrBC,KAAK,EAAE,kBAAkB;MACzBF,WAAW,EAAE,+FAA+F;MAC5G0C,YAAY,EAAE,CACb,UAAU,IAAI,CAACtF,KAAK,CAACC,KAAK,EAAE,EAC5B,WAAW,IAAI,CAACD,KAAK,CAACoB,MAAM,EAAE,EAC9B,aAAa,IAAI,CAACpB,KAAK,CAACE,OAAO,EAAE,EACjC,UAAUiF,aAAa,CAAC1D,EAAE,EAAE,EAC5B,mBAAmB,IAAI,CAACzB,KAAK,CAACI,aAAa,CAACgE,MAAM,GAAG,CAAC,GAAGE,MAAM,CAAC,IAAI,CAACtE,KAAK,CAACI,aAAa,CAACgE,MAAM,CAAC,GAAG,aAAa,EAAE,CAClH;MACDmB,SAAS,EAAEA,CAAA,KAAM,IAAI,CAACC,gBAAgB,CAAC,CAAC;MACxCtC,MAAM,EAAEA,CAAA,KAAO,IAAI,CAAClD,KAAK,CAACoF,mBAAmB,KAAK,SAAS,GAAG,IAAI,CAACR,eAAe,CAAC,CAAC,GAAG,IAAI,CAACC,oBAAoB,CAAC;IAClH,CAAC,CACF,CAAC;EACF;EAEQW,gBAAgBA,CAAA,EAAS;IAChC,IAAI,CAAC,IAAI,CAACxF,KAAK,CAACoB,MAAM,IAAI,CAAC,IAAI,CAACpB,KAAK,CAACmF,aAAa,EAAE;MACpD,MAAM,IAAI9D,KAAK,CAAC,4CAA4C,CAAC;IAC9D;IAEA,MAAMoE,OAAO,GAAG,IAAI,CAAClF,QAAQ,CAACmF,aAAa,CAAC;MAC3CzF,KAAK,EAAE,IAAI,CAACD,KAAK,CAACC,KAAK;MACvBmB,MAAM,EAAE,IAAI,CAACpB,KAAK,CAACoB,MAAM;MACzBlB,OAAO,EAAE,IAAI,CAACF,KAAK,CAACE,OAAO;MAC3BC,MAAM,EAAE,IAAI,CAACH,KAAK,CAACG,MAAM;MACzBuB,UAAU,EAAElC,iBAAiB,CAAC,IAAI,CAACQ,KAAK,CAACoB,MAAM;IAChD,CAAC,CAAC;IAEF,MAAMuE,aAAa,GAClB,IAAI,CAAC3F,KAAK,CAACI,aAAa,CAACgE,MAAM,GAAG,CAAC,GAChC,IAAI,CAACpE,KAAK,CAACI,aAAa,GACxB,CAAC,IAAI,CAACJ,KAAK,CAACmF,aAAa,CAAC;IAE9B,IAAI,CAAC5E,QAAQ,CAACqF,aAAa,CAACH,OAAO,CAAChE,EAAE,EAAEkE,aAAa,EAAE;MACtDE,MAAM,EAAE,IAAI,CAAC7F,KAAK,CAACI,aAAa,CAACgE,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG;IACtD,CAAC,CAAC;IACF,IAAI,CAAC7D,QAAQ,CAACuF,kBAAkB,CAACL,OAAO,CAAChE,EAAE,EAAE,IAAI,CAACzB,KAAK,CAACmF,aAAa,CAAC1D,EAAE,CAAC;IAEzE,MAAMsE,KAAK,GAAG,IAAI,CAACxF,QAAQ,CAACyF,UAAU,CAACP,OAAO,CAAChE,EAAE,CAAC;IAClD,IAAI,CAACsE,KAAK,EAAE;MACX,MAAM,IAAI1E,KAAK,CAAC,8CAA8C,CAAC;IAChE;IAEA,IAAI,CAACb,UAAU,CAACuF,KAAK,CAAC;EACvB;AACD","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ import { ProcessTerminal, TUI } from "@mariozechner/pi-tui";
2
+ import { OnboardingApp } from "./onboarding-app.js";
3
+ export async function runOnboarding(profiles) {
4
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
5
+ throw new Error("First-run onboarding requires an interactive terminal.");
6
+ }
7
+ return new Promise((resolve, reject) => {
8
+ const ui = new TUI(new ProcessTerminal());
9
+ let settled = false;
10
+ const done = fn => {
11
+ if (settled) return;
12
+ settled = true;
13
+ try {
14
+ ui.stop();
15
+ } finally {
16
+ fn();
17
+ }
18
+ };
19
+ const app = new OnboardingApp(ui, profiles, profile => done(() => resolve(profile)), () => done(() => reject(new Error("Onboarding aborted by user"))));
20
+ ui.addChild(app);
21
+ ui.start();
22
+ });
23
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ProcessTerminal","TUI","OnboardingApp","runOnboarding","profiles","process","stdin","isTTY","stdout","Error","Promise","resolve","reject","ui","settled","done","fn","stop","app","profile","addChild","start"],"sources":["run-onboarding.ts"],"sourcesContent":["import { ProcessTerminal, TUI } from \"@mariozechner/pi-tui\";\r\nimport type { ProviderProfile } from \"../provider-profiles/types.js\";\r\nimport { ProviderProfileService } from \"../provider-profiles/index.js\";\r\nimport { OnboardingApp } from \"./onboarding-app.js\";\r\n\r\nexport async function runOnboarding(\r\n\tprofiles: ProviderProfileService,\r\n): Promise<ProviderProfile> {\r\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\r\n\t\tthrow new Error(\"First-run onboarding requires an interactive terminal.\");\r\n\t}\r\n\r\n\treturn new Promise<ProviderProfile>((resolve, reject) => {\r\n\t\tconst ui = new TUI(new ProcessTerminal());\r\n\t\tlet settled = false;\r\n\r\n\t\tconst done = (fn: () => void) => {\r\n\t\t\tif (settled) return;\r\n\t\t\tsettled = true;\r\n\t\t\ttry {\r\n\t\t\t\tui.stop();\r\n\t\t\t} finally {\r\n\t\t\t\tfn();\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tconst app = new OnboardingApp(\r\n\t\t\tui,\r\n\t\t\tprofiles,\r\n\t\t\t(profile) => done(() => resolve(profile)),\r\n\t\t\t() => done(() => reject(new Error(\"Onboarding aborted by user\"))),\r\n\t\t);\r\n\r\n\t\tui.addChild(app);\r\n\t\tui.start();\r\n\t});\r\n}\r\n"],"mappings":"AAAA,SAASA,eAAe,EAAEC,GAAG,QAAQ,sBAAsB;AAG3D,SAASC,aAAa,QAAQ,qBAAqB;AAEnD,OAAO,eAAeC,aAAaA,CAClCC,QAAgC,EACL;EAC3B,IAAI,CAACC,OAAO,CAACC,KAAK,CAACC,KAAK,IAAI,CAACF,OAAO,CAACG,MAAM,CAACD,KAAK,EAAE;IAClD,MAAM,IAAIE,KAAK,CAAC,wDAAwD,CAAC;EAC1E;EAEA,OAAO,IAAIC,OAAO,CAAkB,CAACC,OAAO,EAAEC,MAAM,KAAK;IACxD,MAAMC,EAAE,GAAG,IAAIZ,GAAG,CAAC,IAAID,eAAe,CAAC,CAAC,CAAC;IACzC,IAAIc,OAAO,GAAG,KAAK;IAEnB,MAAMC,IAAI,GAAIC,EAAc,IAAK;MAChC,IAAIF,OAAO,EAAE;MACbA,OAAO,GAAG,IAAI;MACd,IAAI;QACHD,EAAE,CAACI,IAAI,CAAC,CAAC;MACV,CAAC,SAAS;QACTD,EAAE,CAAC,CAAC;MACL;IACD,CAAC;IAED,MAAME,GAAG,GAAG,IAAIhB,aAAa,CAC5BW,EAAE,EACFT,QAAQ,EACPe,OAAO,IAAKJ,IAAI,CAAC,MAAMJ,OAAO,CAACQ,OAAO,CAAC,CAAC,EACzC,MAAMJ,IAAI,CAAC,MAAMH,MAAM,CAAC,IAAIH,KAAK,CAAC,4BAA4B,CAAC,CAAC,CACjE,CAAC;IAEDI,EAAE,CAACO,QAAQ,CAACF,GAAG,CAAC;IAChBL,EAAE,CAACQ,KAAK,CAAC,CAAC;EACX,CAAC,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,21 @@
1
+ import { SelectList, Spacer, Text } from "@mariozechner/pi-tui";
2
+ import { FramedScreen } from "./framed-screen.js";
3
+ import { createSelectListTheme, onboardingTheme } from "./theme.js";
4
+ export class SelectScreen extends FramedScreen {
5
+ constructor(options) {
6
+ super(options.stepLabel, options.title, options.description);
7
+ this.content.addChild(new Text(onboardingTheme.section("Options"), 0, 0));
8
+ this.content.addChild(new Spacer(1));
9
+ this.selectList = new SelectList(options.items, Math.min(10, options.items.length), createSelectListTheme());
10
+ if (typeof options.initialIndex === "number") {
11
+ this.selectList.setSelectedIndex(options.initialIndex);
12
+ }
13
+ this.selectList.onSelect = options.onSelect;
14
+ this.selectList.onCancel = options.onBack;
15
+ this.content.addChild(this.selectList);
16
+ this.setFooter("Up/Down move | Enter select | Esc back");
17
+ }
18
+ getFocusTarget() {
19
+ return this.selectList;
20
+ }
21
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["SelectList","Spacer","Text","FramedScreen","createSelectListTheme","onboardingTheme","SelectScreen","constructor","options","stepLabel","title","description","content","addChild","section","selectList","items","Math","min","length","initialIndex","setSelectedIndex","onSelect","onCancel","onBack","setFooter","getFocusTarget"],"sources":["select-screen.ts"],"sourcesContent":["import { type Component, SelectList, Spacer, Text, type SelectItem } from \"@mariozechner/pi-tui\";\r\nimport { FramedScreen } from \"./framed-screen.js\";\r\nimport { createSelectListTheme, onboardingTheme } from \"./theme.js\";\r\n\r\nexport class SelectScreen extends FramedScreen {\r\n\tprivate readonly selectList: SelectList;\r\n\r\n\tconstructor(options: {\r\n\t\tstepLabel: string;\r\n\t\ttitle: string;\r\n\t\tdescription: string;\r\n\t\titems: SelectItem[];\r\n\t\tinitialIndex?: number;\r\n\t\tonSelect: (item: SelectItem) => void;\r\n\t\tonBack: () => void;\r\n\t}) {\r\n\t\tsuper(options.stepLabel, options.title, options.description);\r\n\r\n\t\tthis.content.addChild(new Text(onboardingTheme.section(\"Options\"), 0, 0));\r\n\t\tthis.content.addChild(new Spacer(1));\r\n\r\n\t\tthis.selectList = new SelectList(options.items, Math.min(10, options.items.length), createSelectListTheme());\r\n\t\tif (typeof options.initialIndex === \"number\") {\r\n\t\t\tthis.selectList.setSelectedIndex(options.initialIndex);\r\n\t\t}\r\n\t\tthis.selectList.onSelect = options.onSelect;\r\n\t\tthis.selectList.onCancel = options.onBack;\r\n\r\n\t\tthis.content.addChild(this.selectList);\r\n\t\tthis.setFooter(\"Up/Down move | Enter select | Esc back\");\r\n\t}\r\n\r\n\toverride getFocusTarget(): Component {\r\n\t\treturn this.selectList;\r\n\t}\r\n}\r\n"],"mappings":"AAAA,SAAyBA,UAAU,EAAEC,MAAM,EAAEC,IAAI,QAAyB,sBAAsB;AAChG,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAASC,qBAAqB,EAAEC,eAAe,QAAQ,YAAY;AAEnE,OAAO,MAAMC,YAAY,SAASH,YAAY,CAAC;EAG9CI,WAAWA,CAACC,OAQX,EAAE;IACF,KAAK,CAACA,OAAO,CAACC,SAAS,EAAED,OAAO,CAACE,KAAK,EAAEF,OAAO,CAACG,WAAW,CAAC;IAE5D,IAAI,CAACC,OAAO,CAACC,QAAQ,CAAC,IAAIX,IAAI,CAACG,eAAe,CAACS,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzE,IAAI,CAACF,OAAO,CAACC,QAAQ,CAAC,IAAIZ,MAAM,CAAC,CAAC,CAAC,CAAC;IAEpC,IAAI,CAACc,UAAU,GAAG,IAAIf,UAAU,CAACQ,OAAO,CAACQ,KAAK,EAAEC,IAAI,CAACC,GAAG,CAAC,EAAE,EAAEV,OAAO,CAACQ,KAAK,CAACG,MAAM,CAAC,EAAEf,qBAAqB,CAAC,CAAC,CAAC;IAC5G,IAAI,OAAOI,OAAO,CAACY,YAAY,KAAK,QAAQ,EAAE;MAC7C,IAAI,CAACL,UAAU,CAACM,gBAAgB,CAACb,OAAO,CAACY,YAAY,CAAC;IACvD;IACA,IAAI,CAACL,UAAU,CAACO,QAAQ,GAAGd,OAAO,CAACc,QAAQ;IAC3C,IAAI,CAACP,UAAU,CAACQ,QAAQ,GAAGf,OAAO,CAACgB,MAAM;IAEzC,IAAI,CAACZ,OAAO,CAACC,QAAQ,CAAC,IAAI,CAACE,UAAU,CAAC;IACtC,IAAI,CAACU,SAAS,CAAC,wCAAwC,CAAC;EACzD;EAESC,cAAcA,CAAA,EAAc;IACpC,OAAO,IAAI,CAACX,UAAU;EACvB;AACD","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ import { getKeybindings, Spacer, Text } from "@mariozechner/pi-tui";
2
+ import { FramedScreen } from "./framed-screen.js";
3
+ import { onboardingTheme } from "./theme.js";
4
+ export class SummaryScreen extends FramedScreen {
5
+ constructor(options) {
6
+ super(options.stepLabel, options.title, options.description);
7
+ for (const line of options.summaryLines) {
8
+ this.content.addChild(new Text(onboardingTheme.text(line), 0, 0));
9
+ this.content.addChild(new Spacer(1));
10
+ }
11
+ this.setFooter("Enter save and continue | Esc back");
12
+ this.handleInput = data => {
13
+ const kb = getKeybindings();
14
+ if (kb.matches(data, "tui.select.confirm")) {
15
+ options.onConfirm();
16
+ return;
17
+ }
18
+ if (kb.matches(data, "tui.select.cancel")) {
19
+ options.onBack();
20
+ }
21
+ };
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getKeybindings","Spacer","Text","FramedScreen","onboardingTheme","SummaryScreen","constructor","options","stepLabel","title","description","line","summaryLines","content","addChild","text","setFooter","handleInput","data","kb","matches","onConfirm","onBack"],"sources":["summary-screen.ts"],"sourcesContent":["import { getKeybindings, Spacer, Text } from \"@mariozechner/pi-tui\";\r\nimport { FramedScreen } from \"./framed-screen.js\";\r\nimport { onboardingTheme } from \"./theme.js\";\r\n\r\nexport class SummaryScreen extends FramedScreen {\r\n\tconstructor(options: {\r\n\t\tstepLabel: string;\r\n\t\ttitle: string;\r\n\t\tdescription: string;\r\n\t\tsummaryLines: string[];\r\n\t\tonConfirm: () => void;\r\n\t\tonBack: () => void;\r\n\t}) {\r\n\t\tsuper(options.stepLabel, options.title, options.description);\r\n\r\n\t\tfor (const line of options.summaryLines) {\r\n\t\t\tthis.content.addChild(new Text(onboardingTheme.text(line), 0, 0));\r\n\t\t\tthis.content.addChild(new Spacer(1));\r\n\t\t}\r\n\r\n\t\tthis.setFooter(\"Enter save and continue | Esc back\");\r\n\r\n\t\tthis.handleInput = (data: string) => {\r\n\t\t\tconst kb = getKeybindings();\r\n\t\t\tif (kb.matches(data, \"tui.select.confirm\")) {\r\n\t\t\t\toptions.onConfirm();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (kb.matches(data, \"tui.select.cancel\")) {\r\n\t\t\t\toptions.onBack();\r\n\t\t\t}\r\n\t\t};\r\n\t}\r\n}\r\n"],"mappings":"AAAA,SAASA,cAAc,EAAEC,MAAM,EAAEC,IAAI,QAAQ,sBAAsB;AACnE,SAASC,YAAY,QAAQ,oBAAoB;AACjD,SAASC,eAAe,QAAQ,YAAY;AAE5C,OAAO,MAAMC,aAAa,SAASF,YAAY,CAAC;EAC/CG,WAAWA,CAACC,OAOX,EAAE;IACF,KAAK,CAACA,OAAO,CAACC,SAAS,EAAED,OAAO,CAACE,KAAK,EAAEF,OAAO,CAACG,WAAW,CAAC;IAE5D,KAAK,MAAMC,IAAI,IAAIJ,OAAO,CAACK,YAAY,EAAE;MACxC,IAAI,CAACC,OAAO,CAACC,QAAQ,CAAC,IAAIZ,IAAI,CAACE,eAAe,CAACW,IAAI,CAACJ,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;MACjE,IAAI,CAACE,OAAO,CAACC,QAAQ,CAAC,IAAIb,MAAM,CAAC,CAAC,CAAC,CAAC;IACrC;IAEA,IAAI,CAACe,SAAS,CAAC,oCAAoC,CAAC;IAEpD,IAAI,CAACC,WAAW,GAAIC,IAAY,IAAK;MACpC,MAAMC,EAAE,GAAGnB,cAAc,CAAC,CAAC;MAC3B,IAAImB,EAAE,CAACC,OAAO,CAACF,IAAI,EAAE,oBAAoB,CAAC,EAAE;QAC3CX,OAAO,CAACc,SAAS,CAAC,CAAC;QACnB;MACD;MACA,IAAIF,EAAE,CAACC,OAAO,CAACF,IAAI,EAAE,mBAAmB,CAAC,EAAE;QAC1CX,OAAO,CAACe,MAAM,CAAC,CAAC;MACjB;IACD,CAAC;EACF;AACD","ignoreList":[]}
@@ -0,0 +1,55 @@
1
+ import { Container, getKeybindings, Input, Spacer, Text } from "@mariozechner/pi-tui";
2
+ import { FramedScreen } from "./framed-screen.js";
3
+ import { onboardingTheme } from "./theme.js";
4
+ export class TextInputScreen extends FramedScreen {
5
+ input = new Input();
6
+ errorText = new Text("", 0, 0);
7
+ body = new Container();
8
+ _focused = false;
9
+ constructor(options) {
10
+ super(options.stepLabel, options.title, options.description);
11
+ this.body.addChild(new Text(onboardingTheme.section(options.label), 0, 0));
12
+ if (options.placeholder) {
13
+ this.body.addChild(new Spacer(1));
14
+ this.body.addChild(new Text(onboardingTheme.muted(options.placeholder), 0, 0));
15
+ }
16
+ this.body.addChild(new Spacer(1));
17
+ this.body.addChild(this.input);
18
+ this.body.addChild(new Spacer(1));
19
+ this.body.addChild(this.errorText);
20
+ this.content.addChild(this.body);
21
+ if (options.initialValue) {
22
+ this.input.setValue(options.initialValue);
23
+ }
24
+ this.input.onEscape = options.onBack;
25
+ this.input.onSubmit = () => {
26
+ const value = this.input.getValue().trim();
27
+ const error = options.validate?.(value);
28
+ if (error) {
29
+ this.errorText.setText(onboardingTheme.error(error));
30
+ return;
31
+ }
32
+ this.errorText.setText("");
33
+ options.onSubmit(value);
34
+ };
35
+ this.setFooter("Enter confirm | Esc back");
36
+ }
37
+ get focused() {
38
+ return this._focused;
39
+ }
40
+ set focused(value) {
41
+ this._focused = value;
42
+ this.input.focused = value;
43
+ }
44
+ getFocusTarget() {
45
+ return this;
46
+ }
47
+ handleInput(data) {
48
+ const kb = getKeybindings();
49
+ if (kb.matches(data, "tui.select.cancel")) {
50
+ this.input.onEscape?.();
51
+ return;
52
+ }
53
+ this.input.handleInput(data);
54
+ }
55
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Container","getKeybindings","Input","Spacer","Text","FramedScreen","onboardingTheme","TextInputScreen","input","errorText","body","_focused","constructor","options","stepLabel","title","description","addChild","section","label","placeholder","muted","content","initialValue","setValue","onEscape","onBack","onSubmit","value","getValue","trim","error","validate","setText","setFooter","focused","getFocusTarget","handleInput","data","kb","matches"],"sources":["text-input-screen.ts"],"sourcesContent":["import { Container, type Component, type Focusable, getKeybindings, Input, Spacer, Text } from \"@mariozechner/pi-tui\";\r\nimport { FramedScreen } from \"./framed-screen.js\";\r\nimport { onboardingTheme } from \"./theme.js\";\r\n\r\nexport class TextInputScreen extends FramedScreen implements Focusable {\r\n\tprivate readonly input = new Input();\r\n\tprivate readonly errorText = new Text(\"\", 0, 0);\r\n\tprivate readonly body = new Container();\r\n\tprivate _focused = false;\r\n\r\n\tconstructor(options: {\r\n\t\tstepLabel: string;\r\n\t\ttitle: string;\r\n\t\tdescription: string;\r\n\t\tlabel: string;\r\n\t\tinitialValue?: string;\r\n\t\tplaceholder?: string;\r\n\t\tvalidate?: (value: string) => string | undefined;\r\n\t\tonSubmit: (value: string) => void;\r\n\t\tonBack: () => void;\r\n\t}) {\r\n\t\tsuper(options.stepLabel, options.title, options.description);\r\n\r\n\t\tthis.body.addChild(new Text(onboardingTheme.section(options.label), 0, 0));\r\n\t\tif (options.placeholder) {\r\n\t\t\tthis.body.addChild(new Spacer(1));\r\n\t\t\tthis.body.addChild(new Text(onboardingTheme.muted(options.placeholder), 0, 0));\r\n\t\t}\r\n\t\tthis.body.addChild(new Spacer(1));\r\n\t\tthis.body.addChild(this.input);\r\n\t\tthis.body.addChild(new Spacer(1));\r\n\t\tthis.body.addChild(this.errorText);\r\n\t\tthis.content.addChild(this.body);\r\n\r\n\t\tif (options.initialValue) {\r\n\t\t\tthis.input.setValue(options.initialValue);\r\n\t\t}\r\n\r\n\t\tthis.input.onEscape = options.onBack;\r\n\t\tthis.input.onSubmit = () => {\r\n\t\t\tconst value = this.input.getValue().trim();\r\n\t\t\tconst error = options.validate?.(value);\r\n\t\t\tif (error) {\r\n\t\t\t\tthis.errorText.setText(onboardingTheme.error(error));\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tthis.errorText.setText(\"\");\r\n\t\t\toptions.onSubmit(value);\r\n\t\t};\r\n\r\n\t\tthis.setFooter(\"Enter confirm | Esc back\");\r\n\t}\r\n\r\n\tget focused(): boolean {\r\n\t\treturn this._focused;\r\n\t}\r\n\r\n\tset focused(value: boolean) {\r\n\t\tthis._focused = value;\r\n\t\tthis.input.focused = value;\r\n\t}\r\n\r\n\toverride getFocusTarget(): Component {\r\n\t\treturn this;\r\n\t}\r\n\r\n\thandleInput(data: string): void {\r\n\t\tconst kb = getKeybindings();\r\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\r\n\t\t\tthis.input.onEscape?.();\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tthis.input.handleInput(data);\r\n\t}\r\n}\r\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,26 @@
1
+ import chalk from "chalk";
2
+ const brandPrimary = chalk.hex("#7dd3fc");
3
+ const brandAccent = chalk.hex("#38bdf8");
4
+ const brandDim = chalk.hex("#94a3b8");
5
+ export const onboardingTheme = {
6
+ brand: text => chalk.bold(brandPrimary(text)),
7
+ accent: text => brandAccent(text),
8
+ title: text => chalk.bold(brandPrimary(text)),
9
+ text: text => chalk.white(text),
10
+ muted: text => brandDim(text),
11
+ success: text => chalk.greenBright(text),
12
+ error: text => chalk.redBright(text),
13
+ warning: text => chalk.yellowBright(text),
14
+ border: text => brandAccent(text),
15
+ key: text => chalk.bold(chalk.white(text)),
16
+ section: text => chalk.bold(brandAccent(text))
17
+ };
18
+ export function createSelectListTheme() {
19
+ return {
20
+ selectedPrefix: onboardingTheme.accent,
21
+ selectedText: onboardingTheme.accent,
22
+ description: onboardingTheme.muted,
23
+ scrollInfo: onboardingTheme.muted,
24
+ noMatch: onboardingTheme.warning
25
+ };
26
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["chalk","brandPrimary","hex","brandAccent","brandDim","onboardingTheme","brand","text","bold","accent","title","white","muted","success","greenBright","error","redBright","warning","yellowBright","border","key","section","createSelectListTheme","selectedPrefix","selectedText","description","scrollInfo","noMatch"],"sources":["theme.ts"],"sourcesContent":["import chalk from \"chalk\";\r\nimport type { SelectListTheme } from \"@mariozechner/pi-tui\";\r\n\r\nconst brandPrimary = chalk.hex(\"#7dd3fc\");\r\nconst brandAccent = chalk.hex(\"#38bdf8\");\r\nconst brandDim = chalk.hex(\"#94a3b8\");\r\n\r\nexport const onboardingTheme = {\r\n\tbrand: (text: string) => chalk.bold(brandPrimary(text)),\r\n\taccent: (text: string) => brandAccent(text),\r\n\ttitle: (text: string) => chalk.bold(brandPrimary(text)),\r\n\ttext: (text: string) => chalk.white(text),\r\n\tmuted: (text: string) => brandDim(text),\r\n\tsuccess: (text: string) => chalk.greenBright(text),\r\n\terror: (text: string) => chalk.redBright(text),\r\n\twarning: (text: string) => chalk.yellowBright(text),\r\n\tborder: (text: string) => brandAccent(text),\r\n\tkey: (text: string) => chalk.bold(chalk.white(text)),\r\n\tsection: (text: string) => chalk.bold(brandAccent(text)),\r\n};\r\n\r\nexport function createSelectListTheme(): SelectListTheme {\r\n\treturn {\r\n\t\tselectedPrefix: onboardingTheme.accent,\r\n\t\tselectedText: onboardingTheme.accent,\r\n\t\tdescription: onboardingTheme.muted,\r\n\t\tscrollInfo: onboardingTheme.muted,\r\n\t\tnoMatch: onboardingTheme.warning,\r\n\t};\r\n}\r\n"],"mappings":"AAAA,OAAOA,KAAK,MAAM,OAAO;AAGzB,MAAMC,YAAY,GAAGD,KAAK,CAACE,GAAG,CAAC,SAAS,CAAC;AACzC,MAAMC,WAAW,GAAGH,KAAK,CAACE,GAAG,CAAC,SAAS,CAAC;AACxC,MAAME,QAAQ,GAAGJ,KAAK,CAACE,GAAG,CAAC,SAAS,CAAC;AAErC,OAAO,MAAMG,eAAe,GAAG;EAC9BC,KAAK,EAAGC,IAAY,IAAKP,KAAK,CAACQ,IAAI,CAACP,YAAY,CAACM,IAAI,CAAC,CAAC;EACvDE,MAAM,EAAGF,IAAY,IAAKJ,WAAW,CAACI,IAAI,CAAC;EAC3CG,KAAK,EAAGH,IAAY,IAAKP,KAAK,CAACQ,IAAI,CAACP,YAAY,CAACM,IAAI,CAAC,CAAC;EACvDA,IAAI,EAAGA,IAAY,IAAKP,KAAK,CAACW,KAAK,CAACJ,IAAI,CAAC;EACzCK,KAAK,EAAGL,IAAY,IAAKH,QAAQ,CAACG,IAAI,CAAC;EACvCM,OAAO,EAAGN,IAAY,IAAKP,KAAK,CAACc,WAAW,CAACP,IAAI,CAAC;EAClDQ,KAAK,EAAGR,IAAY,IAAKP,KAAK,CAACgB,SAAS,CAACT,IAAI,CAAC;EAC9CU,OAAO,EAAGV,IAAY,IAAKP,KAAK,CAACkB,YAAY,CAACX,IAAI,CAAC;EACnDY,MAAM,EAAGZ,IAAY,IAAKJ,WAAW,CAACI,IAAI,CAAC;EAC3Ca,GAAG,EAAGb,IAAY,IAAKP,KAAK,CAACQ,IAAI,CAACR,KAAK,CAACW,KAAK,CAACJ,IAAI,CAAC,CAAC;EACpDc,OAAO,EAAGd,IAAY,IAAKP,KAAK,CAACQ,IAAI,CAACL,WAAW,CAACI,IAAI,CAAC;AACxD,CAAC;AAED,OAAO,SAASe,qBAAqBA,CAAA,EAAoB;EACxD,OAAO;IACNC,cAAc,EAAElB,eAAe,CAACI,MAAM;IACtCe,YAAY,EAAEnB,eAAe,CAACI,MAAM;IACpCgB,WAAW,EAAEpB,eAAe,CAACO,KAAK;IAClCc,UAAU,EAAErB,eAAe,CAACO,KAAK;IACjCe,OAAO,EAAEtB,eAAe,CAACY;EAC1B,CAAC;AACF","ignoreList":[]}
@@ -0,0 +1,51 @@
1
+ import { getAuthPath } from "../config.js";
2
+ import { LockedJsonFile } from "../storage/locked-json-file.js";
3
+ function isApiKeyCredential(value) {
4
+ return typeof value === "object" && value !== null && "type" in value && "valueOf" in Object.prototype && value.type === "api_key" && "key" in value && typeof value.key === "string";
5
+ }
6
+ export class ApiKeyStore {
7
+ constructor(authPath = getAuthPath()) {
8
+ this.storage = new LockedJsonFile(authPath, {}, {
9
+ fileMode: 0o600
10
+ });
11
+ }
12
+ ensureFile() {
13
+ this.storage.write(this.storage.read());
14
+ }
15
+ getApiKey(profileId) {
16
+ const value = this.storage.read()[profileId];
17
+ return isApiKeyCredential(value) ? value.key : undefined;
18
+ }
19
+ hasApiKey(profileId) {
20
+ return this.getApiKey(profileId) !== undefined;
21
+ }
22
+ setApiKey(profileId, apiKey) {
23
+ this.storage.withLock(current => ({
24
+ result: undefined,
25
+ next: {
26
+ ...current,
27
+ [profileId]: {
28
+ type: "api_key",
29
+ key: apiKey
30
+ }
31
+ }
32
+ }));
33
+ }
34
+ removeApiKey(profileId) {
35
+ this.storage.withLock(current => {
36
+ if (!(profileId in current)) {
37
+ return {
38
+ result: undefined
39
+ };
40
+ }
41
+ const next = {
42
+ ...current
43
+ };
44
+ delete next[profileId];
45
+ return {
46
+ result: undefined,
47
+ next
48
+ };
49
+ });
50
+ }
51
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getAuthPath","LockedJsonFile","isApiKeyCredential","value","Object","prototype","type","key","ApiKeyStore","constructor","authPath","storage","fileMode","ensureFile","write","read","getApiKey","profileId","undefined","hasApiKey","setApiKey","apiKey","withLock","current","result","next","removeApiKey"],"sources":["api-key-store.ts"],"sourcesContent":["import { getAuthPath } from \"../config.js\";\r\nimport { LockedJsonFile } from \"../storage/locked-json-file.js\";\r\n\r\ntype ApiKeyCredential = {\r\n\ttype: \"api_key\";\r\n\tkey: string;\r\n};\r\n\r\ntype AuthRecord = Record<string, unknown>;\r\n\r\nfunction isApiKeyCredential(value: unknown): value is ApiKeyCredential {\r\n\treturn (\r\n\t\ttypeof value === \"object\" &&\r\n\t\tvalue !== null &&\r\n\t\t\"type\" in value &&\r\n\t\t\"valueOf\" in Object.prototype &&\r\n\t\t(value as { type?: unknown }).type === \"api_key\" &&\r\n\t\t\"key\" in value &&\r\n\t\ttypeof (value as { key?: unknown }).key === \"string\"\r\n\t);\r\n}\r\n\r\nexport class ApiKeyStore {\r\n\tprivate readonly storage: LockedJsonFile<AuthRecord>;\r\n\r\n\tconstructor(authPath: string = getAuthPath()) {\r\n\t\tthis.storage = new LockedJsonFile<AuthRecord>(authPath, {}, { fileMode: 0o600 });\r\n\t}\r\n\r\n\tensureFile(): void {\r\n\t\tthis.storage.write(this.storage.read());\r\n\t}\r\n\r\n\tgetApiKey(profileId: string): string | undefined {\r\n\t\tconst value = this.storage.read()[profileId];\r\n\t\treturn isApiKeyCredential(value) ? value.key : undefined;\r\n\t}\r\n\r\n\thasApiKey(profileId: string): boolean {\r\n\t\treturn this.getApiKey(profileId) !== undefined;\r\n\t}\r\n\r\n\tsetApiKey(profileId: string, apiKey: string): void {\r\n\t\tthis.storage.withLock((current) => ({\r\n\t\t\tresult: undefined,\r\n\t\t\tnext: {\r\n\t\t\t\t...current,\r\n\t\t\t\t[profileId]: {\r\n\t\t\t\t\ttype: \"api_key\",\r\n\t\t\t\t\tkey: apiKey,\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t}));\r\n\t}\r\n\r\n\tremoveApiKey(profileId: string): void {\r\n\t\tthis.storage.withLock((current) => {\r\n\t\t\tif (!(profileId in current)) {\r\n\t\t\t\treturn { result: undefined };\r\n\t\t\t}\r\n\r\n\t\t\tconst next = { ...current };\r\n\t\t\tdelete next[profileId];\r\n\t\t\treturn { result: undefined, next };\r\n\t\t});\r\n\t}\r\n}\r\n"],"mappings":"AAAA,SAASA,WAAW,QAAQ,cAAc;AAC1C,SAASC,cAAc,QAAQ,gCAAgC;AAS/D,SAASC,kBAAkBA,CAACC,KAAc,EAA6B;EACtE,OACC,OAAOA,KAAK,KAAK,QAAQ,IACzBA,KAAK,KAAK,IAAI,IACd,MAAM,IAAIA,KAAK,IACf,SAAS,IAAIC,MAAM,CAACC,SAAS,IAC5BF,KAAK,CAAwBG,IAAI,KAAK,SAAS,IAChD,KAAK,IAAIH,KAAK,IACd,OAAQA,KAAK,CAAuBI,GAAG,KAAK,QAAQ;AAEtD;AAEA,OAAO,MAAMC,WAAW,CAAC;EAGxBC,WAAWA,CAACC,QAAgB,GAAGV,WAAW,CAAC,CAAC,EAAE;IAC7C,IAAI,CAACW,OAAO,GAAG,IAAIV,cAAc,CAAaS,QAAQ,EAAE,CAAC,CAAC,EAAE;MAAEE,QAAQ,EAAE;IAAM,CAAC,CAAC;EACjF;EAEAC,UAAUA,CAAA,EAAS;IAClB,IAAI,CAACF,OAAO,CAACG,KAAK,CAAC,IAAI,CAACH,OAAO,CAACI,IAAI,CAAC,CAAC,CAAC;EACxC;EAEAC,SAASA,CAACC,SAAiB,EAAsB;IAChD,MAAMd,KAAK,GAAG,IAAI,CAACQ,OAAO,CAACI,IAAI,CAAC,CAAC,CAACE,SAAS,CAAC;IAC5C,OAAOf,kBAAkB,CAACC,KAAK,CAAC,GAAGA,KAAK,CAACI,GAAG,GAAGW,SAAS;EACzD;EAEAC,SAASA,CAACF,SAAiB,EAAW;IACrC,OAAO,IAAI,CAACD,SAAS,CAACC,SAAS,CAAC,KAAKC,SAAS;EAC/C;EAEAE,SAASA,CAACH,SAAiB,EAAEI,MAAc,EAAQ;IAClD,IAAI,CAACV,OAAO,CAACW,QAAQ,CAAEC,OAAO,KAAM;MACnCC,MAAM,EAAEN,SAAS;MACjBO,IAAI,EAAE;QACL,GAAGF,OAAO;QACV,CAACN,SAAS,GAAG;UACZX,IAAI,EAAE,SAAS;UACfC,GAAG,EAAEc;QACN;MACD;IACD,CAAC,CAAC,CAAC;EACJ;EAEAK,YAAYA,CAACT,SAAiB,EAAQ;IACrC,IAAI,CAACN,OAAO,CAACW,QAAQ,CAAEC,OAAO,IAAK;MAClC,IAAI,EAAEN,SAAS,IAAIM,OAAO,CAAC,EAAE;QAC5B,OAAO;UAAEC,MAAM,EAAEN;QAAU,CAAC;MAC7B;MAEA,MAAMO,IAAI,GAAG;QAAE,GAAGF;MAAQ,CAAC;MAC3B,OAAOE,IAAI,CAACR,SAAS,CAAC;MACtB,OAAO;QAAEO,MAAM,EAAEN,SAAS;QAAEO;MAAK,CAAC;IACnC,CAAC,CAAC;EACH;AACD","ignoreList":[]}