@lunora/cli 1.0.0-alpha.3 → 1.0.0-alpha.30

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 (52) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/bin.mjs +1 -1
  3. package/dist/index.d.mts +243 -114
  4. package/dist/index.d.ts +243 -114
  5. package/dist/index.mjs +7 -7
  6. package/dist/packem_chunks/handler.mjs +89 -7
  7. package/dist/packem_chunks/handler10.mjs +8 -14
  8. package/dist/packem_chunks/handler11.mjs +19 -189
  9. package/dist/packem_chunks/handler12.mjs +176 -115
  10. package/dist/packem_chunks/handler13.mjs +118 -52
  11. package/dist/packem_chunks/handler14.mjs +50 -43
  12. package/dist/packem_chunks/handler15.mjs +46 -67
  13. package/dist/packem_chunks/handler16.mjs +73 -37
  14. package/dist/packem_chunks/handler17.mjs +38 -100
  15. package/dist/packem_chunks/handler18.mjs +87 -154
  16. package/dist/packem_chunks/handler19.mjs +148 -67
  17. package/dist/packem_chunks/handler2.mjs +2 -2
  18. package/dist/packem_chunks/handler20.mjs +71 -76
  19. package/dist/packem_chunks/handler21.mjs +71 -288
  20. package/dist/packem_chunks/handler3.mjs +1 -1
  21. package/dist/packem_chunks/handler4.mjs +1 -1
  22. package/dist/packem_chunks/handler5.mjs +2 -2
  23. package/dist/packem_chunks/handler6.mjs +2 -2
  24. package/dist/packem_chunks/handler7.mjs +1 -1
  25. package/dist/packem_chunks/handler8.mjs +1 -1
  26. package/dist/packem_chunks/handler9.mjs +311 -12
  27. package/dist/packem_chunks/planDevCommand.mjs +46 -49
  28. package/dist/packem_chunks/runCodegenCommand.mjs +2 -2
  29. package/dist/packem_chunks/runDeployCommand.mjs +105 -15
  30. package/dist/packem_chunks/runInitCommand.mjs +1980 -77
  31. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -5
  32. package/dist/packem_chunks/runResetCommand.mjs +4 -4
  33. package/dist/packem_chunks/runRpcCommand.mjs +1 -1
  34. package/dist/packem_shared/{COMMANDS-CHw4zOZ9.mjs → COMMANDS-B0ftFD_3.mjs} +47 -21
  35. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-D3lB_4Az.mjs} +6 -1
  36. package/dist/packem_shared/{commands-DIQ3nf0C.mjs → commands-B-gR09Z_.mjs} +124 -22
  37. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-B40gPzQo.mjs} +9 -4
  38. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +61 -0
  39. package/dist/packem_shared/{diffSnapshots-RR2ZE8Ya.mjs → diffSnapshots-BeDvvNiF.mjs} +1 -1
  40. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  41. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-wUvAN6AL.mjs} +1 -1
  42. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
  43. package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +4 -0
  44. package/dist/packem_shared/{schemaIrToSnapshot-aBTo7TM5.mjs → schemaIrToSnapshot-DdsljJT-.mjs} +1 -1
  45. package/dist/packem_shared/storage-BIsph-Vk.mjs +84 -0
  46. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  47. package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +47 -0
  48. package/package.json +12 -11
  49. package/skills/lunora-setup-storage/SKILL.md +7 -3
  50. package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
  51. package/dist/packem_shared/runAddCommand-3I3JFZUG.mjs +0 -4
  52. /package/dist/packem_shared/{defaultSpawner-DxI3mebw.mjs → createRecordingSpawner-DxI3mebw.mjs} +0 -0
@@ -0,0 +1,658 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { isInteractive, ACCENT, LUNA_ART, LUNA_NAME, LUNA_SIGNOFF, BADGE_COLUMN_WIDTH, badgeLead, padBadge, badgeWidth } from '@lunora/config';
3
+ import { render } from '@visulima/tui';
4
+ import '@visulima/tui/components/big-text';
5
+ import { Box } from '@visulima/tui/components/box';
6
+ import { CommandPalette } from '@visulima/tui/components/command-palette';
7
+ import { ConfirmInput } from '@visulima/tui/components/confirm-input';
8
+ import { SelectInput } from '@visulima/tui/components/select-input';
9
+ import { Spinner } from '@visulima/tui/components/spinner';
10
+ import { Text } from '@visulima/tui/components/text';
11
+ import { TextInput } from '@visulima/tui/components/text-input';
12
+ import { useApp } from '@visulima/tui/hooks/use-app';
13
+ import { useInput } from '@visulima/tui/hooks/use-input';
14
+ import { useInterval } from '@visulima/tui/hooks/use-interval';
15
+ import { useEffect, useState } from 'react';
16
+ import { P as PromptCancelledError } from './prompt-cancelled-APzX1Im-.mjs';
17
+
18
+ let listenerCapRaised = false;
19
+ const raiseListenerCap = () => {
20
+ if (listenerCapRaised) {
21
+ return;
22
+ }
23
+ listenerCapRaised = true;
24
+ if (process.getMaxListeners() < 64) {
25
+ process.setMaxListeners(64);
26
+ }
27
+ };
28
+ const CtrlCGuard = ({ children, onCancel }) => {
29
+ const { exit } = useApp();
30
+ useInput((input, key) => {
31
+ if (key.ctrl && input === "c") {
32
+ onCancel();
33
+ exit();
34
+ }
35
+ });
36
+ return children;
37
+ };
38
+ const toError = (failure) => {
39
+ if (failure instanceof Error) {
40
+ return failure;
41
+ }
42
+ return new Error(typeof failure === "string" ? failure : "task failed");
43
+ };
44
+ const SCROLL_LIMIT = 10;
45
+ const FILTER_THRESHOLD = 8;
46
+ const Badge = ({ spec }) => jsxs(Text, { children: [
47
+ jsx(Text, { children: badgeLead(spec.text) }),
48
+ jsx(Text, { backgroundColor: spec.bg, bold: true, color: spec.fg, children: padBadge(spec.text) })
49
+ ] });
50
+ const PromptHeader = ({ badge, message }) => badge === void 0 ? jsx(Text, { bold: true, children: message }) : jsxs(Box, { children: [
51
+ jsx(Badge, { spec: badge }),
52
+ jsx(Text, { bold: true, children: ` ${message}` })
53
+ ] });
54
+ const BODY_INDENT = 2;
55
+ const HEADER_INDENT = " ".repeat(BADGE_COLUMN_WIDTH - 1);
56
+ const badgeIndent = (badge) => " ".repeat(badgeWidth(badge) + 1);
57
+ const bodyIndent = (badge) => badge === void 0 ? BODY_INDENT : badgeWidth(badge) + 1;
58
+ const PromptShell = ({
59
+ badge,
60
+ children,
61
+ message
62
+ }) => jsxs(Box, { flexDirection: "column", children: [
63
+ jsx(PromptHeader, { badge, message }),
64
+ jsx(Box, { flexDirection: "column", marginLeft: bodyIndent(badge), children })
65
+ ] });
66
+ const AnsweredLine = ({ answer, badge, message }) => jsxs(Box, { flexDirection: "column", children: [
67
+ jsx(PromptHeader, { badge, message }),
68
+ answer === "" ? null : jsx(Text, { dimColor: true, children: `${badge === void 0 ? " ".repeat(BODY_INDENT) : badgeIndent(badge)}${answer}` })
69
+ ] });
70
+ const useAnswer = (finish) => {
71
+ const { exit } = useApp();
72
+ const [answer, setAnswer] = useState(void 0);
73
+ useEffect(() => {
74
+ if (answer !== void 0) {
75
+ exit();
76
+ }
77
+ }, [answer, exit]);
78
+ return {
79
+ answer,
80
+ submit: (result, display) => {
81
+ finish(result);
82
+ setAnswer(display);
83
+ }
84
+ };
85
+ };
86
+ const SelfExit = ({ children }) => {
87
+ const { exit } = useApp();
88
+ useEffect(() => {
89
+ exit();
90
+ }, [exit]);
91
+ return children;
92
+ };
93
+ const printFrame = async (element) => {
94
+ raiseListenerCap();
95
+ process.stdout.write("\n");
96
+ const instance = render(jsx(SelfExit, { children: element }));
97
+ try {
98
+ await instance.waitUntilExit();
99
+ } catch {
100
+ } finally {
101
+ instance.unmount();
102
+ }
103
+ };
104
+ const tuiStep = async (badge, message, answer) => {
105
+ if (!isInteractive()) {
106
+ return;
107
+ }
108
+ const answerLines = answer === void 0 || answer === "" ? [] : answer.split("\n");
109
+ await printFrame(
110
+ jsxs(Box, { flexDirection: "column", children: [
111
+ jsxs(Box, { children: [
112
+ jsx(Badge, { spec: badge }),
113
+ jsx(Text, { bold: true, children: ` ${message}` })
114
+ ] }),
115
+ answerLines.map((line) => jsx(Text, { dimColor: true, children: `${badgeIndent(badge)}${line}` }, line))
116
+ ] })
117
+ );
118
+ };
119
+ const runInkApp = async (element) => {
120
+ raiseListenerCap();
121
+ process.stdout.write("\n");
122
+ const cancel = { value: false };
123
+ const instance = render(
124
+ jsx(
125
+ CtrlCGuard,
126
+ {
127
+ onCancel: () => {
128
+ cancel.value = true;
129
+ },
130
+ children: element
131
+ }
132
+ ),
133
+ { exitOnCtrlC: false }
134
+ );
135
+ try {
136
+ await instance.waitUntilExit();
137
+ } finally {
138
+ instance.unmount();
139
+ }
140
+ if (cancel.value) {
141
+ throw new PromptCancelledError();
142
+ }
143
+ };
144
+ const runInkPrompt = async (build, fallback) => {
145
+ let result = fallback;
146
+ await runInkApp(
147
+ build((value) => {
148
+ result = value;
149
+ })
150
+ );
151
+ return result;
152
+ };
153
+ const itemLabel = (option) => option.description === void 0 ? option.label : `${option.label} — ${option.description}`;
154
+ const useEscapeToExit = () => {
155
+ const { exit } = useApp();
156
+ useInput((_input, key) => {
157
+ if (key.escape) {
158
+ exit();
159
+ }
160
+ });
161
+ };
162
+ const SelectView = ({ badge, finish, initialIndex, message, options }) => {
163
+ const { answer, submit } = useAnswer(finish);
164
+ useEscapeToExit();
165
+ if (answer !== void 0) {
166
+ return jsx(AnsweredLine, { answer, badge, message });
167
+ }
168
+ return jsx(PromptShell, { badge, message, children: jsx(
169
+ SelectInput,
170
+ {
171
+ accentColor: ACCENT,
172
+ initialIndex,
173
+ items: options.map((option) => {
174
+ return { key: option.value, label: itemLabel(option), value: option.value };
175
+ }),
176
+ limit: SCROLL_LIMIT,
177
+ onSelect: (item) => {
178
+ submit(item.value, options.find((option) => option.value === item.value)?.label ?? item.value);
179
+ }
180
+ }
181
+ ) });
182
+ };
183
+ const PaletteView = ({ badge, finish, message, options }) => {
184
+ const { exit } = useApp();
185
+ const { answer, submit } = useAnswer(finish);
186
+ if (answer !== void 0) {
187
+ return jsx(AnsweredLine, { answer, badge, message });
188
+ }
189
+ return jsx(PromptShell, { badge, message, children: jsx(
190
+ CommandPalette,
191
+ {
192
+ accentColor: ACCENT,
193
+ autoFocus: true,
194
+ commands: options.map((option) => {
195
+ return { description: option.description, id: option.value, label: option.label };
196
+ }),
197
+ limit: SCROLL_LIMIT,
198
+ onCancel: () => {
199
+ exit();
200
+ },
201
+ onSelect: (id) => {
202
+ submit(id, options.find((option) => option.value === id)?.label ?? id);
203
+ },
204
+ placeholder: "Type to filter…"
205
+ }
206
+ ) });
207
+ };
208
+ const tuiSelect = async (message, options, settings) => {
209
+ if (!isInteractive() || options.length === 0) {
210
+ return settings?.default;
211
+ }
212
+ const { badge } = settings ?? {};
213
+ if (options.length > FILTER_THRESHOLD) {
214
+ return runInkPrompt((finish) => jsx(PaletteView, { badge, finish, message, options }), settings?.default);
215
+ }
216
+ const defaultIndex = settings?.default === void 0 ? -1 : options.findIndex((option) => option.value === settings.default);
217
+ return runInkPrompt(
218
+ (finish) => jsx(SelectView, { badge, finish, initialIndex: defaultIndex >= 0 ? defaultIndex : void 0, message, options }),
219
+ settings?.default
220
+ );
221
+ };
222
+ const TextView = ({ badge, defaultValue, finish, message, placeholder }) => {
223
+ const { answer, submit } = useAnswer(finish);
224
+ useEscapeToExit();
225
+ if (answer !== void 0) {
226
+ return jsx(AnsweredLine, { answer, badge, message });
227
+ }
228
+ return jsx(PromptShell, { badge, message, children: jsx(
229
+ TextInput,
230
+ {
231
+ defaultValue,
232
+ onSubmit: (value) => {
233
+ const chosen = value.trim() === "" ? defaultValue : value.trim();
234
+ submit(chosen, chosen);
235
+ },
236
+ placeholder
237
+ }
238
+ ) });
239
+ };
240
+ const tuiText = async (message, settings) => {
241
+ const fallback = settings?.default ?? "";
242
+ if (!isInteractive()) {
243
+ return fallback;
244
+ }
245
+ const { badge } = settings ?? {};
246
+ return runInkPrompt(
247
+ (finish) => jsx(TextView, { badge, defaultValue: fallback, finish, message, placeholder: settings?.placeholder }),
248
+ fallback
249
+ );
250
+ };
251
+ const MultiSelectView = ({ badge, defaults, finish, message, options }) => {
252
+ const { answer, submit } = useAnswer(finish);
253
+ const [cursor, setCursor] = useState(0);
254
+ const [selected, setSelected] = useState(() => new Set(defaults));
255
+ useEscapeToExit();
256
+ useInput((input, key) => {
257
+ if (answer !== void 0 || options.length === 0) {
258
+ return;
259
+ }
260
+ if (key.upArrow) {
261
+ setCursor((current) => (current - 1 + options.length) % options.length);
262
+ } else if (key.downArrow) {
263
+ setCursor((current) => (current + 1) % options.length);
264
+ } else if (input === " ") {
265
+ const option = options[cursor];
266
+ if (option !== void 0) {
267
+ setSelected((previous) => {
268
+ const next = new Set(previous);
269
+ if (next.has(option.value)) {
270
+ next.delete(option.value);
271
+ } else {
272
+ next.add(option.value);
273
+ }
274
+ return next;
275
+ });
276
+ }
277
+ } else if (key.return) {
278
+ const picked = options.filter((option) => selected.has(option.value));
279
+ submit(
280
+ picked.map((option) => option.value),
281
+ picked.length > 0 ? picked.map((option) => option.label).join(", ") : "none"
282
+ );
283
+ }
284
+ });
285
+ if (answer !== void 0) {
286
+ return jsx(AnsweredLine, { answer, badge, message });
287
+ }
288
+ return jsx(PromptShell, { badge, message, children: jsxs(Box, { flexDirection: "column", children: [
289
+ jsx(Text, { dimColor: true, children: "space toggles · enter confirms · esc cancels" }),
290
+ options.map((option, index) => {
291
+ const onCursor = index === cursor;
292
+ return jsx(Text, { color: onCursor ? ACCENT : void 0, children: `${onCursor ? "▸ " : " "}${selected.has(option.value) ? "◼" : "◻"} ${itemLabel(option)}` }, option.value);
293
+ })
294
+ ] }) });
295
+ };
296
+ const tuiMultiSelect = async (message, options, settings) => {
297
+ const defaults = settings?.defaults ?? [];
298
+ if (!isInteractive() || options.length === 0) {
299
+ return [...defaults];
300
+ }
301
+ const { badge } = settings ?? {};
302
+ return runInkPrompt(
303
+ (finish) => jsx(MultiSelectView, { badge, defaults, finish, message, options }),
304
+ [...defaults]
305
+ );
306
+ };
307
+ const ConfirmView = ({ badge, defaultYes, finish, message }) => {
308
+ const { answer, submit } = useAnswer(finish);
309
+ if (answer !== void 0) {
310
+ return jsx(AnsweredLine, { answer, badge, message });
311
+ }
312
+ return jsxs(Box, { children: [
313
+ badge === void 0 ? null : jsx(Badge, { spec: badge }),
314
+ jsx(Text, { bold: true, children: badge === void 0 ? `${message} ` : ` ${message} ` }),
315
+ jsx(
316
+ ConfirmInput,
317
+ {
318
+ defaultChoice: defaultYes ? "confirm" : "cancel",
319
+ onCancel: () => {
320
+ submit(false, "No");
321
+ },
322
+ onConfirm: () => {
323
+ submit(true, "Yes");
324
+ }
325
+ }
326
+ )
327
+ ] });
328
+ };
329
+ const tuiConfirm = async (message, options) => {
330
+ const defaultYes = options?.defaultYes === true;
331
+ if (!isInteractive()) {
332
+ return defaultYes;
333
+ }
334
+ const { badge } = options ?? {};
335
+ return runInkPrompt((finish) => jsx(ConfirmView, { badge, defaultYes, finish, message }), false);
336
+ };
337
+ const createTuiConfirm = () => isInteractive() ? (message) => tuiConfirm(message, { defaultYes: true }) : () => Promise.resolve(false);
338
+ const STAR_BRIGHT = "#c8a8ff";
339
+ const MOON_LIGHT = "#d9c8ff";
340
+ const MOON_FACE = "#6b5b9a";
341
+ const MoonRow = ({ moon, prefix, suffix }) => jsxs(Box, { children: [
342
+ jsx(Text, { color: STAR_BRIGHT, dimColor: true, children: prefix }),
343
+ typeof moon === "string" ? jsx(Text, { color: MOON_LIGHT, children: moon }) : moon,
344
+ suffix === void 0 ? null : jsx(Text, { color: STAR_BRIGHT, children: suffix })
345
+ ] });
346
+ const MoonFace = ({ left, right }) => jsxs(Text, { color: MOON_LIGHT, children: [
347
+ left,
348
+ jsx(Text, { color: MOON_FACE, children: "●●" }),
349
+ right
350
+ ] });
351
+ const MoonriseHeader = () => jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
352
+ jsx(MoonRow, { moon: ".-‐-.", prefix: "· ✦ · ", suffix: " ✦" }),
353
+ jsx(MoonRow, { moon: jsx(MoonFace, { left: "/", right: "\\" }), prefix: " ✦ · ", suffix: " ·" }),
354
+ jsx(MoonRow, { moon: jsx(MoonFace, { left: "\\", right: "/" }), prefix: " . ✦ · " }),
355
+ jsxs(Box, { children: [
356
+ jsx(Text, { bold: true, color: ACCENT, children: " lunora init " }),
357
+ jsx(Text, { dimColor: true, children: "· moonrise sequence" }),
358
+ jsx(Text, { color: MOON_LIGHT })
359
+ ] })
360
+ ] });
361
+ const tuiMoonrise = async (subtitle) => {
362
+ if (!isInteractive()) {
363
+ return;
364
+ }
365
+ await printFrame(
366
+ jsxs(Box, { flexDirection: "column", children: [
367
+ jsx(MoonriseHeader, {}),
368
+ jsx(Box, { paddingX: 1, children: jsx(Text, { dimColor: true, children: subtitle }) })
369
+ ] })
370
+ );
371
+ };
372
+ const SpinnerView = ({ label }) => jsxs(Box, { children: [
373
+ jsx(Text, { color: ACCENT, children: jsx(Spinner, { type: "dots" }) }),
374
+ jsxs(Text, { children: [
375
+ " ",
376
+ label
377
+ ] })
378
+ ] });
379
+ const withTuiSpinner = async (label, task) => {
380
+ if (!isInteractive()) {
381
+ return task();
382
+ }
383
+ raiseListenerCap();
384
+ const instance = render(jsx(SpinnerView, { label }));
385
+ try {
386
+ return await task();
387
+ } finally {
388
+ instance.unmount();
389
+ }
390
+ };
391
+ const tuiMascot = async () => {
392
+ if (!isInteractive()) {
393
+ return;
394
+ }
395
+ await printFrame(
396
+ jsxs(Box, { paddingX: 1, children: [
397
+ jsx(Text, { color: MOON_LIGHT, children: LUNA_ART }),
398
+ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
399
+ jsx(Text, { bold: true, color: ACCENT, children: LUNA_NAME }),
400
+ jsx(Text, { children: LUNA_SIGNOFF })
401
+ ] })
402
+ ] })
403
+ );
404
+ };
405
+ const INFO_CYAN = "#06b6d4";
406
+ const PROSE_INDENT = " ".repeat(9);
407
+ const tuiHeadline = async (message) => {
408
+ if (!isInteractive()) {
409
+ return;
410
+ }
411
+ await printFrame(jsx(Text, { bold: true, children: `${PROSE_INDENT}${message}` }));
412
+ };
413
+ const tuiInfo = async (message) => {
414
+ if (!isInteractive()) {
415
+ return;
416
+ }
417
+ await printFrame(
418
+ jsxs(Box, { children: [
419
+ jsx(Text, { color: INFO_CYAN, children: `${HEADER_INDENT}◼` }),
420
+ jsx(Text, { children: ` ${message}` })
421
+ ] })
422
+ );
423
+ };
424
+ const tuiNextSteps = async (badge, header, steps, help) => {
425
+ if (!isInteractive()) {
426
+ return;
427
+ }
428
+ await printFrame(
429
+ jsxs(Box, { flexDirection: "column", children: [
430
+ jsxs(Box, { children: [
431
+ jsx(Badge, { spec: badge }),
432
+ jsx(Text, { bold: true, children: ` ${header}` })
433
+ ] }),
434
+ jsx(Box, { flexDirection: "column", marginTop: 1, children: steps.map((step) => jsxs(Text, { children: [
435
+ `${PROSE_INDENT}${step.lead} `,
436
+ jsx(Text, { color: ACCENT, children: step.code }),
437
+ step.tail ?? ""
438
+ ] }, step.lead)) }),
439
+ jsx(Box, { flexDirection: "column", marginTop: 1, children: help.map((line) => jsxs(Text, { children: [
440
+ `${PROSE_INDENT}${line.lead} `,
441
+ jsx(Text, { color: ACCENT, children: line.code }),
442
+ line.tail ?? ""
443
+ ] }, line.lead)) })
444
+ ] })
445
+ );
446
+ };
447
+ const TASK_INDENT = " ".repeat(BADGE_COLUMN_WIDTH - 1);
448
+ const SPINNER_FIRST = "#a855f7";
449
+ const SPINNER_LAST = "#06b6d4";
450
+ const SPINNER_COLORS = [SPINNER_FIRST, "#9b51f6", "#8a5cf6", "#6f86f6", "#4aa6ef", "#2bb6dd", "#12bcd0", SPINNER_LAST];
451
+ const SPINNER_BAR_WIDTH = SPINNER_COLORS.length - 2;
452
+ const repeatColor = (count, value) => Array.from({ length: Math.max(0, count) }, () => value);
453
+ const SPINNER_STRIP = [
454
+ ...repeatColor(SPINNER_COLORS.length - 1, SPINNER_FIRST),
455
+ ...SPINNER_COLORS,
456
+ ...repeatColor(SPINNER_COLORS.length - 1, SPINNER_LAST),
457
+ ...SPINNER_COLORS.toReversed()
458
+ ];
459
+ const SPINNER_FRAMES = SPINNER_STRIP.map((_, offset) => {
460
+ const window = SPINNER_STRIP.slice(offset, offset + SPINNER_BAR_WIDTH);
461
+ return [...window, ...repeatColor(SPINNER_BAR_WIDTH - window.length, SPINNER_FIRST)];
462
+ });
463
+ const SPINNER_LEAD = " ".repeat(Math.max(0, BADGE_COLUMN_WIDTH - SPINNER_BAR_WIDTH));
464
+ const GradientSpinner = ({ label }) => {
465
+ const [index, setIndex] = useState(0);
466
+ useInterval(() => {
467
+ setIndex((previous) => (previous + 1) % SPINNER_FRAMES.length);
468
+ }, 90);
469
+ const colors = SPINNER_FRAMES[index % SPINNER_FRAMES.length] ?? [];
470
+ return jsxs(Text, { children: [
471
+ SPINNER_LEAD,
472
+ colors.map((color, blockIndex) => (
473
+ // eslint-disable-next-line react-x/no-array-index-key -- fixed-length gradient bar that never reorders.
474
+ jsx(Text, { color, children: "█" }, blockIndex)
475
+ )),
476
+ ` ${label}`
477
+ ] });
478
+ };
479
+ const TaskRow = ({ label, status }) => {
480
+ if (status === "running") {
481
+ return jsx(Text, { color: ACCENT, children: `${TASK_INDENT}▶ ${label}` });
482
+ }
483
+ if (status === "failed") {
484
+ return jsx(Text, { color: "red", children: `${TASK_INDENT}✖ ${label}` });
485
+ }
486
+ if (status === "done") {
487
+ return jsx(Text, { dimColor: true, children: `${TASK_INDENT}■ ${label}` });
488
+ }
489
+ return jsx(Text, { dimColor: true, children: `${TASK_INDENT}□ ${label}` });
490
+ };
491
+ const runTaskList = async (tasks, mark) => {
492
+ const results = [];
493
+ let failure;
494
+ for (const [index, task] of tasks.entries()) {
495
+ mark(index, "running");
496
+ try {
497
+ results.push(await task.run());
498
+ mark(index, "done");
499
+ } catch (error) {
500
+ mark(index, "failed");
501
+ failure = error;
502
+ break;
503
+ }
504
+ }
505
+ return { failure, results };
506
+ };
507
+ const startTasks = (tasks, setStatuses, onSettle, exit) => {
508
+ let active = true;
509
+ const mark = (index, status) => {
510
+ if (active) {
511
+ setStatuses((previous) => previous.map((value, position) => position === index ? status : value));
512
+ }
513
+ };
514
+ runTaskList(tasks, mark).then(({ failure, results }) => {
515
+ setTimeout(() => {
516
+ if (active) {
517
+ onSettle(results, failure);
518
+ exit();
519
+ }
520
+ }, 400);
521
+ return void 0;
522
+ }).catch(() => {
523
+ });
524
+ return () => {
525
+ active = false;
526
+ };
527
+ };
528
+ const TasksView = ({ end, onSettle, start, tasks }) => {
529
+ const { exit } = useApp();
530
+ const [statuses, setStatuses] = useState(() => tasks.map(() => "pending"));
531
+ useEffect(() => startTasks(tasks, setStatuses, onSettle, exit), [exit, onSettle, tasks]);
532
+ const allDone = statuses.length > 0 && statuses.every((status) => status === "done");
533
+ return jsxs(Box, { flexDirection: "column", children: [
534
+ allDone ? jsxs(Box, { children: [
535
+ jsx(Text, { bold: true, color: "green", children: `${HEADER_INDENT}✔` }),
536
+ jsx(Text, { bold: true, color: "green", children: ` ${end}` })
537
+ ] }) : jsx(GradientSpinner, { label: start }),
538
+ tasks.map((task, index) => jsx(TaskRow, { label: task.label, status: statuses[index] ?? "pending" }, task.label))
539
+ ] });
540
+ };
541
+ const tuiTasks = async (tasks, labels) => {
542
+ if (!isInteractive()) {
543
+ const results2 = [];
544
+ for (const task of tasks) {
545
+ results2.push(await task.run());
546
+ }
547
+ return results2;
548
+ }
549
+ let results = [];
550
+ let failure;
551
+ await runInkApp(
552
+ jsx(
553
+ TasksView,
554
+ {
555
+ end: labels.end,
556
+ onSettle: (settledResults, settledFailure) => {
557
+ results = settledResults;
558
+ failure = settledFailure;
559
+ },
560
+ start: labels.start,
561
+ tasks
562
+ }
563
+ )
564
+ );
565
+ if (failure !== void 0) {
566
+ throw toError(failure);
567
+ }
568
+ return results;
569
+ };
570
+ const runProgressSteps = async (steps, mark) => {
571
+ const values = [];
572
+ let failure;
573
+ for (const [index, step] of steps.entries()) {
574
+ mark(index);
575
+ try {
576
+ values.push(await step.task());
577
+ } catch (error) {
578
+ failure = error;
579
+ break;
580
+ }
581
+ }
582
+ return { failure, values };
583
+ };
584
+ const BadgeProgressView = ({ badge, done, onSettle, steps }) => {
585
+ const { exit } = useApp();
586
+ const [index, setIndex] = useState(0);
587
+ const [finished, setFinished] = useState(false);
588
+ useEffect(() => {
589
+ let active = true;
590
+ let timer;
591
+ const mark = (next) => {
592
+ if (active) {
593
+ setIndex(next);
594
+ }
595
+ };
596
+ runProgressSteps(steps, mark).then(({ failure, values }) => {
597
+ if (!active) {
598
+ return void 0;
599
+ }
600
+ if (failure !== void 0) {
601
+ onSettle({ error: failure, ok: false });
602
+ exit();
603
+ return void 0;
604
+ }
605
+ setFinished(true);
606
+ timer = setTimeout(() => {
607
+ if (active) {
608
+ onSettle({ ok: true, values });
609
+ exit();
610
+ }
611
+ }, 400);
612
+ return void 0;
613
+ }).catch(() => {
614
+ });
615
+ return () => {
616
+ active = false;
617
+ if (timer !== void 0) {
618
+ clearTimeout(timer);
619
+ }
620
+ };
621
+ }, [exit, onSettle, steps]);
622
+ return finished ? jsxs(Box, { children: [
623
+ jsx(Badge, { spec: badge }),
624
+ jsx(Text, { bold: true, children: ` ${done}` })
625
+ ] }) : jsx(GradientSpinner, { label: steps[index]?.running ?? "" });
626
+ };
627
+ const withTuiBadgeProgress = async (badge, steps, done) => {
628
+ if (!isInteractive()) {
629
+ const values = [];
630
+ for (const step of steps) {
631
+ values.push(await step.task());
632
+ }
633
+ return values;
634
+ }
635
+ let outcome;
636
+ await runInkApp(
637
+ jsx(
638
+ BadgeProgressView,
639
+ {
640
+ badge,
641
+ done,
642
+ onSettle: (settled) => {
643
+ outcome = settled;
644
+ },
645
+ steps
646
+ }
647
+ )
648
+ );
649
+ if (outcome === void 0) {
650
+ throw new PromptCancelledError();
651
+ }
652
+ if (!outcome.ok) {
653
+ throw toError(outcome.error);
654
+ }
655
+ return outcome.values;
656
+ };
657
+
658
+ export { tuiSelect as a, tuiConfirm as b, createTuiConfirm as c, tuiMascot as d, tuiStep as e, tuiMoonrise as f, tuiHeadline as g, tuiInfo as h, tuiNextSteps as i, tuiTasks as j, tuiMultiSelect as k, withTuiBadgeProgress as l, tuiText as t, withTuiSpinner as w };
@@ -0,0 +1,47 @@
1
+ import { execFile } from 'node:child_process';
2
+
3
+ const execCode = (error) => {
4
+ if (!error) {
5
+ return 0;
6
+ }
7
+ return typeof error.code === "number" ? error.code : 1;
8
+ };
9
+ const defaultRunner = (command, args, cwd) => new Promise((resolve) => {
10
+ execFile(command, [...args], { cwd }, (error, stdout, stderr) => {
11
+ resolve({ code: execCode(error), stderr, stdout });
12
+ });
13
+ });
14
+ const parseSecretNames = (stdout) => {
15
+ let parsed;
16
+ try {
17
+ parsed = JSON.parse(stdout);
18
+ } catch {
19
+ return void 0;
20
+ }
21
+ if (!Array.isArray(parsed)) {
22
+ return void 0;
23
+ }
24
+ const names = parsed.map((entry) => entry !== null && typeof entry === "object" ? entry.name : void 0).filter((name) => typeof name === "string" && name.length > 0);
25
+ return [...names].toSorted((a, b) => a.localeCompare(b));
26
+ };
27
+ const listRemoteSecrets = async (inputs) => {
28
+ const args = ["exec", "wrangler", "secret", "list", "--format", "json"];
29
+ if (inputs.env !== void 0) {
30
+ args.push("--env", inputs.env);
31
+ }
32
+ if (inputs.temporary) {
33
+ args.push("--temporary");
34
+ }
35
+ const runner = inputs.runner ?? defaultRunner;
36
+ const result = await runner("pnpm", args, inputs.cwd);
37
+ if (result.code !== 0) {
38
+ return { error: result.stderr.trim() || `wrangler secret list exited ${String(result.code)}`, names: [], ok: false };
39
+ }
40
+ const names = parseSecretNames(result.stdout);
41
+ if (names === void 0) {
42
+ return { error: "could not parse `wrangler secret list --format json` output", names: [], ok: false };
43
+ }
44
+ return { names, ok: true };
45
+ };
46
+
47
+ export { listRemoteSecrets as l };