@lunora/cli 1.0.0-alpha.9 → 1.0.0-alpha.91

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 (59) hide show
  1. package/LICENSE.md +6 -0
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/bin.mjs +1 -1
  4. package/dist/index.d.mts +358 -300
  5. package/dist/index.d.ts +358 -300
  6. package/dist/index.mjs +8 -9
  7. package/dist/packem_chunks/handler.mjs +15 -5
  8. package/dist/packem_chunks/handler10.mjs +8 -14
  9. package/dist/packem_chunks/handler11.mjs +20 -189
  10. package/dist/packem_chunks/handler12.mjs +176 -115
  11. package/dist/packem_chunks/handler13.mjs +118 -52
  12. package/dist/packem_chunks/handler14.mjs +50 -43
  13. package/dist/packem_chunks/handler15.mjs +48 -67
  14. package/dist/packem_chunks/handler16.mjs +94 -37
  15. package/dist/packem_chunks/handler17.mjs +39 -100
  16. package/dist/packem_chunks/handler18.mjs +87 -152
  17. package/dist/packem_chunks/handler19.mjs +148 -67
  18. package/dist/packem_chunks/handler2.mjs +6 -4
  19. package/dist/packem_chunks/handler20.mjs +77 -75
  20. package/dist/packem_chunks/handler21.mjs +71 -288
  21. package/dist/packem_chunks/handler3.mjs +8 -4
  22. package/dist/packem_chunks/handler4.mjs +2 -2
  23. package/dist/packem_chunks/handler5.mjs +7 -4
  24. package/dist/packem_chunks/handler6.mjs +10 -7
  25. package/dist/packem_chunks/handler7.mjs +2 -2
  26. package/dist/packem_chunks/handler8.mjs +1 -1
  27. package/dist/packem_chunks/handler9.mjs +330 -12
  28. package/dist/packem_chunks/planDevCommand.mjs +762 -81
  29. package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
  30. package/dist/packem_chunks/runDeployCommand.mjs +137 -22
  31. package/dist/packem_chunks/runInitCommand.mjs +1648 -188
  32. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +94 -47
  33. package/dist/packem_chunks/runResetCommand.mjs +2 -2
  34. package/dist/packem_chunks/runRpcCommand.mjs +3 -2
  35. package/dist/packem_shared/{COMMANDS-Dh0bSERi.mjs → COMMANDS-DZ0NnuqI.mjs} +103 -27
  36. package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs} +7 -2
  37. package/dist/packem_shared/{api-spec-CtA6ilu4.mjs → api-spec-Bx0iKbxA.mjs} +3 -1
  38. package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
  39. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +31 -0
  40. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-lYnl4QyF.mjs} +6 -1
  41. package/dist/packem_shared/{commands-CkkATMMx.mjs → commands-D5Yxt9VY.mjs} +199 -20
  42. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-CIWSHrTL.mjs} +40 -8
  43. package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-WuSn20kb.mjs} +23 -2
  44. package/dist/packem_shared/detect-package-manager-v4hHpQd0.mjs +62 -0
  45. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  46. package/dist/packem_shared/{open-url-Dfq6fAyT.mjs → open-url-4PBLY9X0.mjs} +3 -2
  47. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-B4642rjE.mjs} +1 -1
  48. package/dist/packem_shared/{parseManifest--vZf2FY1.mjs → parseManifest-Dbp-Q2q3.mjs} +36 -9
  49. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
  50. package/dist/packem_shared/runAddCommand-D1hgfqFQ.mjs +4 -0
  51. package/dist/packem_shared/{storage-Bjo35hPa.mjs → storage-BXU4ax4O.mjs} +1 -1
  52. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  53. package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +49 -0
  54. package/package.json +17 -14
  55. package/skills/lunora-quickstart/SKILL.md +26 -5
  56. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  57. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  58. package/dist/packem_shared/runAddCommand-DepS9W9W.mjs +0 -4
  59. package/dist/packem_shared/tui-prompts-CA9lngSS.mjs +0 -269
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.9",
3
+ "version": "1.0.0-alpha.91",
4
4
  "description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -51,22 +51,25 @@
51
51
  "access": "public"
52
52
  },
53
53
  "dependencies": {
54
- "@bomb.sh/tab": "0.0.16",
55
- "@lunora/codegen": "1.0.0-alpha.3",
56
- "@lunora/config": "1.0.0-alpha.4",
57
- "@lunora/container": "1.0.0-alpha.1",
58
- "@lunora/d1": "1.0.0-alpha.4",
59
- "@lunora/seed": "1.0.0-alpha.1",
60
- "@visulima/cerebro": "3.0.0-alpha.32",
61
- "@visulima/fs": "5.0.0-alpha.32",
62
- "@visulima/pail": "4.0.0-alpha.22",
63
- "@visulima/path": "3.0.0-alpha.13",
64
- "@visulima/spinner": "1.0.0-alpha.4",
65
- "@visulima/tui": "1.0.0-alpha.26",
54
+ "@bomb.sh/tab": "0.0.19",
55
+ "@lunora/codegen": "1.0.0-alpha.48",
56
+ "@lunora/config": "1.0.0-alpha.72",
57
+ "@lunora/container": "1.0.0-alpha.13",
58
+ "@lunora/d1": "1.0.0-alpha.34",
59
+ "@lunora/errors": "1.0.0-alpha.6",
60
+ "@lunora/seed": "1.0.0-alpha.28",
61
+ "@visulima/cerebro": "3.0.0",
62
+ "@visulima/error": "6.0.0",
63
+ "@visulima/fs": "5.0.4",
64
+ "@visulima/package": "5.0.4",
65
+ "@visulima/pail": "4.0.0",
66
+ "@visulima/path": "3.0.0",
67
+ "@visulima/spinner": "1.0.0",
68
+ "@visulima/tui": "1.0.4",
66
69
  "cfonts": "^3.3.1",
67
70
  "giget": "3.3.0",
68
71
  "jsonc-parser": "^3.3.1",
69
- "magic-string": "^0.30.21",
72
+ "magic-string": "^1.0.0",
70
73
  "react": "^19.2.7",
71
74
  "react-reconciler": "^0.33.0",
72
75
  "ts-morph": "^28.0.0"
@@ -31,9 +31,12 @@ Set up a working Lunora project as fast as possible.
31
31
  Lunora into the current project.
32
32
  4. Run `lunora codegen` to generate `lunora/_generated/` and typecheck the
33
33
  schema + functions. This is the agent's feedback loop.
34
- 5. Start the dev loop with `lunora dev` (ask the user to run it locally, or
35
- start it in the background for cloud/headless agents it is long-running and
36
- does not exit).
34
+ 5. Start the dev loop. As an agent, run `lunora dev --background` it starts
35
+ the server as a managed detached process, blocks until it accepts requests,
36
+ prints the URL + PID, and returns (under a detected AI agent, plain
37
+ `lunora dev` does this automatically, with JSON logs). Never leave a bare
38
+ `lunora dev` running in your own shell — it is long-running and does not
39
+ exit.
37
40
  6. Verify a query/mutation round-trip works end to end.
38
41
 
39
42
  ## Path 1: New Project (Recommended)
@@ -54,6 +57,7 @@ pnpm install
54
57
  | `vite` | React + Vite (the simplest full-stack starter) |
55
58
  | `standalone` | Worker-only Lunora backend, no frontend |
56
59
  | `astro` | Astro integration |
60
+ | `next` | Next.js (App Router, OpenNext on Cloudflare) |
57
61
  | `nuxt` | Nuxt (Vue) |
58
62
  | `sveltekit` | SvelteKit |
59
63
  | `tanstack-start-react` | TanStack Start (React) |
@@ -87,7 +91,23 @@ not exit, so:
87
91
 
88
92
  - **Local development (user at the keyboard):** ask the user to run `lunora dev`
89
93
  in a terminal.
90
- - **Cloud or headless agents:** start `lunora dev` in the background.
94
+ - **Agents:** run `lunora dev --background`. It detaches the server, waits until
95
+ it answers HTTP, prints `Dev server running at <url> (pid <n>)`, and exits —
96
+ no orphaned shell, no PID bookkeeping. When Lunora detects an AI agent
97
+ (Claude Code, Cursor, Codex, …), plain `lunora dev` flips into this mode
98
+ automatically with JSON logs; `LUNORA_AGENT_MODE=0` opts out.
99
+
100
+ Manage the running server afterwards:
101
+
102
+ ```bash
103
+ lunora dev status --json # machine-readable: url, pid, uptime, logFile
104
+ lunora dev logs --lines 50 # tail the captured output (.lunora/dev.log)
105
+ lunora dev stop # idempotent — succeeds even if nothing runs
106
+ ```
107
+
108
+ A second `lunora dev` never double-starts: it reports the existing instance
109
+ (`.lunora/dev.json` is the lockfile). Probe readiness or liveness at
110
+ `GET /_lunora/status` (`{"ok":true}`).
91
111
 
92
112
  Vite serves on `http://localhost:5173` by default; the Worker is served on the
93
113
  same origin via `@cloudflare/vite-plugin`.
@@ -236,5 +256,6 @@ ids, `.dev.vars` secrets, and container exports.
236
256
  - [ ] New project: scaffolded with `lunora init --template <t>`.
237
257
  - [ ] Existing app: ran `lunora init --here` and wired `LunoraProvider`.
238
258
  - [ ] Ran `lunora codegen`: `lunora/_generated/` exists and typecheck is clean.
239
- - [ ] `lunora dev` is running — user terminal, or background for cloud agents.
259
+ - [ ] Dev server is running — user terminal, or `lunora dev --background`
260
+ (check with `lunora dev status`).
240
261
  - [ ] Verified a query/mutation round-trip re-renders the client live.
@@ -1,61 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { dirname, join } from '@visulima/path';
4
-
5
- const FALLBACK = "pnpm";
6
- const KNOWN_MANAGERS = ["pnpm", "yarn", "npm", "bun"];
7
- const INSTALL_PREFERENCE = ["pnpm", "bun", "yarn", "npm"];
8
- const isManagerInstalled = (manager) => {
9
- try {
10
- return spawnSync(manager, ["--version"], { stdio: "ignore", timeout: 5e3 }).status === 0;
11
- } catch {
12
- return false;
13
- }
14
- };
15
- const detectInstalledManagers = (probe = isManagerInstalled) => INSTALL_PREFERENCE.filter((manager) => probe(manager));
16
- const installArgsFor = (manager) => {
17
- return { args: ["install"], command: manager };
18
- };
19
- const parseDeclaredManager = (declared) => {
20
- if (typeof declared !== "string") {
21
- return void 0;
22
- }
23
- return KNOWN_MANAGERS.find((manager) => declared.startsWith(`${manager}@`));
24
- };
25
- const readDeclaredManager = (directory) => {
26
- const candidate = join(directory, "package.json");
27
- if (!existsSync(candidate)) {
28
- return void 0;
29
- }
30
- try {
31
- const parsed = JSON.parse(readFileSync(candidate, "utf8"));
32
- return parseDeclaredManager(parsed.packageManager);
33
- } catch {
34
- return void 0;
35
- }
36
- };
37
- const detectPackageManager = (startDirectory) => {
38
- let directory = startDirectory;
39
- while (directory && directory !== dirname(directory)) {
40
- const declared = readDeclaredManager(directory);
41
- if (declared !== void 0) {
42
- return declared;
43
- }
44
- directory = dirname(directory);
45
- }
46
- return FALLBACK;
47
- };
48
- const execArgsFor = (manager, command, args) => {
49
- if (manager === "yarn") {
50
- return { args: [command, ...args], command: "yarn" };
51
- }
52
- if (manager === "bun") {
53
- return { args: ["x", command, ...args], command: "bun" };
54
- }
55
- if (manager === "npm") {
56
- return { args: ["--", command, ...args], command: "npx" };
57
- }
58
- return { args: ["exec", command, ...args], command: "pnpm" };
59
- };
60
-
61
- export { detectInstalledManagers as a, detectPackageManager as d, execArgsFor as e, installArgsFor as i };
@@ -1,56 +0,0 @@
1
- const consumeLongOption = (body, next, booleanFlags, accumulator) => {
2
- const eqIndex = body.indexOf("=");
3
- if (eqIndex !== -1) {
4
- accumulator.options[body.slice(0, eqIndex)] = body.slice(eqIndex + 1);
5
- return 1;
6
- }
7
- if (booleanFlags.has(body)) {
8
- accumulator.flags[body] = true;
9
- return 1;
10
- }
11
- if (next !== void 0 && !next.startsWith("-")) {
12
- accumulator.options[body] = next;
13
- return 2;
14
- }
15
- accumulator.flags[body] = true;
16
- return 1;
17
- };
18
- const consumeShortOption = (body, next, accumulator) => {
19
- if (body.length > 1) {
20
- accumulator.options[body[0]] = body.slice(1);
21
- return 1;
22
- }
23
- if (next !== void 0 && !next.startsWith("-")) {
24
- accumulator.options[body] = next;
25
- return 2;
26
- }
27
- accumulator.flags[body] = true;
28
- return 1;
29
- };
30
- const parseArgs = (argv, booleanFlags = /* @__PURE__ */ new Set()) => {
31
- const accumulator = { flags: {}, options: {}, positional: [] };
32
- let index = 0;
33
- let terminated = false;
34
- while (index < argv.length) {
35
- const token = argv[index];
36
- if (token === void 0) {
37
- index += 1;
38
- continue;
39
- }
40
- if (terminated || !token.startsWith("-") || token.length === 1) {
41
- accumulator.positional.push(token);
42
- index += 1;
43
- continue;
44
- }
45
- if (token === "--") {
46
- terminated = true;
47
- index += 1;
48
- continue;
49
- }
50
- const next = argv[index + 1];
51
- index += token.startsWith("--") ? consumeLongOption(token.slice(2), next, booleanFlags, accumulator) : consumeShortOption(token.slice(1), next, accumulator);
52
- }
53
- return { flags: accumulator.flags, options: accumulator.options, positional: accumulator.positional };
54
- };
55
-
56
- export { parseArgs as default };
@@ -1,4 +0,0 @@
1
- import 'node:fs';
2
- import '@visulima/path';
3
- export { r as runAddCommand, a as runBuildIndexCommand, e as runListCommand, b as runRegistryViewCommand } from './commands-CkkATMMx.mjs';
4
- import './buildRegistryIndex-BcYe607_.mjs';
@@ -1,269 +0,0 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { isInteractive } from '@lunora/config';
3
- import { render } from '@visulima/tui';
4
- import { BigText } from '@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 { MultiSelect } from '@visulima/tui/components/multi-select';
9
- import { SelectInput } from '@visulima/tui/components/select-input';
10
- import { Spinner } from '@visulima/tui/components/spinner';
11
- import { Text } from '@visulima/tui/components/text';
12
- import { TextInput } from '@visulima/tui/components/text-input';
13
- import { useApp } from '@visulima/tui/hooks/use-app';
14
- import { useInput } from '@visulima/tui/hooks/use-input';
15
- import { useEffect } from 'react';
16
-
17
- const ACCENT = "#a855f7";
18
- const SCROLL_LIMIT = 10;
19
- const FILTER_THRESHOLD = 8;
20
- const PromptFrame = ({ children }) => jsx(Box, { borderColor: ACCENT, borderStyle: "round", flexDirection: "column", paddingX: 1, children });
21
- const runInkPrompt = async (build, fallback) => {
22
- let result = fallback;
23
- const instance = render(
24
- build((value) => {
25
- result = value;
26
- }),
27
- { exitOnCtrlC: true }
28
- );
29
- try {
30
- await instance.waitUntilExit();
31
- } catch {
32
- } finally {
33
- instance.unmount();
34
- }
35
- return result;
36
- };
37
- const itemLabel = (option) => option.description === void 0 ? option.label : `${option.label} — ${option.description}`;
38
- const useCommit = (finish) => {
39
- const { exit } = useApp();
40
- return (result) => {
41
- finish(result);
42
- exit();
43
- };
44
- };
45
- const useEscapeToExit = () => {
46
- const { exit } = useApp();
47
- useInput((_input, key) => {
48
- if (key.escape) {
49
- exit();
50
- }
51
- });
52
- };
53
- const SelectView = ({ finish, initialIndex, message, options }) => {
54
- const commit = useCommit(finish);
55
- useEscapeToExit();
56
- return jsxs(PromptFrame, { children: [
57
- jsx(Text, { bold: true, children: message }),
58
- jsx(
59
- SelectInput,
60
- {
61
- accentColor: ACCENT,
62
- initialIndex,
63
- items: options.map((option) => {
64
- return { key: option.value, label: itemLabel(option), value: option.value };
65
- }),
66
- limit: SCROLL_LIMIT,
67
- onSelect: (item) => {
68
- commit(item.value);
69
- }
70
- }
71
- )
72
- ] });
73
- };
74
- const PaletteView = ({ finish, message, options }) => {
75
- const { exit } = useApp();
76
- const commit = useCommit(finish);
77
- return jsxs(Box, { flexDirection: "column", children: [
78
- jsx(Text, { bold: true, children: message }),
79
- jsx(
80
- CommandPalette,
81
- {
82
- accentColor: ACCENT,
83
- autoFocus: true,
84
- commands: options.map((option) => {
85
- return { description: option.description, id: option.value, label: option.label };
86
- }),
87
- limit: SCROLL_LIMIT,
88
- onCancel: () => {
89
- exit();
90
- },
91
- onSelect: (id) => {
92
- commit(id);
93
- },
94
- placeholder: "Type to filter…"
95
- }
96
- )
97
- ] });
98
- };
99
- const tuiSelect = async (message, options, settings) => {
100
- if (!isInteractive() || options.length === 0) {
101
- return settings?.default;
102
- }
103
- if (options.length > FILTER_THRESHOLD) {
104
- return runInkPrompt((finish) => jsx(PaletteView, { finish, message, options }), settings?.default);
105
- }
106
- const defaultIndex = settings?.default === void 0 ? -1 : options.findIndex((option) => option.value === settings.default);
107
- return runInkPrompt(
108
- (finish) => jsx(SelectView, { finish, initialIndex: defaultIndex >= 0 ? defaultIndex : void 0, message, options }),
109
- settings?.default
110
- );
111
- };
112
- const TextView = ({ defaultValue, finish, message, placeholder }) => {
113
- const commit = useCommit(finish);
114
- useEscapeToExit();
115
- return jsxs(PromptFrame, { children: [
116
- jsx(Text, { bold: true, children: message }),
117
- jsx(
118
- TextInput,
119
- {
120
- defaultValue,
121
- onSubmit: (value) => {
122
- commit(value.trim() === "" ? defaultValue : value.trim());
123
- },
124
- placeholder
125
- }
126
- )
127
- ] });
128
- };
129
- const tuiText = async (message, settings) => {
130
- const fallback = settings?.default ?? "";
131
- if (!isInteractive()) {
132
- return fallback;
133
- }
134
- return runInkPrompt(
135
- (finish) => jsx(TextView, { defaultValue: fallback, finish, message, placeholder: settings?.placeholder }),
136
- fallback
137
- );
138
- };
139
- const MultiSelectView = ({ defaults, finish, message, options }) => {
140
- const commit = useCommit(finish);
141
- useEscapeToExit();
142
- return jsxs(PromptFrame, { children: [
143
- jsx(Text, { bold: true, children: message }),
144
- jsx(Text, { dimColor: true, children: "space toggles · enter confirms · esc cancels" }),
145
- jsx(
146
- MultiSelect,
147
- {
148
- accentColor: ACCENT,
149
- defaultValue: [...defaults],
150
- limit: SCROLL_LIMIT,
151
- onSubmit: (values) => {
152
- const chosen = new Set(values);
153
- commit(options.filter((option) => chosen.has(option.value)).map((option) => option.value));
154
- },
155
- options: options.map((option) => {
156
- return { key: option.value, label: itemLabel(option), value: option.value };
157
- })
158
- }
159
- )
160
- ] });
161
- };
162
- const tuiMultiSelect = async (message, options, settings) => {
163
- const defaults = settings?.defaults ?? [];
164
- if (!isInteractive() || options.length === 0) {
165
- return [...defaults];
166
- }
167
- return runInkPrompt((finish) => jsx(MultiSelectView, { defaults, finish, message, options }), [...defaults]);
168
- };
169
- const ConfirmView = ({ defaultYes, finish, message }) => {
170
- const commit = useCommit(finish);
171
- return jsx(PromptFrame, { children: jsxs(Box, { children: [
172
- jsxs(Text, { bold: true, children: [
173
- message,
174
- " "
175
- ] }),
176
- jsx(
177
- ConfirmInput,
178
- {
179
- defaultChoice: defaultYes ? "confirm" : "cancel",
180
- onCancel: () => {
181
- commit(false);
182
- },
183
- onConfirm: () => {
184
- commit(true);
185
- }
186
- }
187
- )
188
- ] }) });
189
- };
190
- const tuiConfirm = async (message, options) => {
191
- const defaultYes = options?.defaultYes === true;
192
- if (!isInteractive()) {
193
- return defaultYes;
194
- }
195
- return runInkPrompt((finish) => jsx(ConfirmView, { defaultYes, finish, message }), false);
196
- };
197
- const createTuiConfirm = () => isInteractive() ? (message) => tuiConfirm(message, { defaultYes: true }) : () => Promise.resolve(false);
198
- const SelfExit = ({ children }) => {
199
- const { exit } = useApp();
200
- useEffect(() => {
201
- exit();
202
- }, [exit]);
203
- return children;
204
- };
205
- const printFrame = async (element) => {
206
- const instance = render(jsx(SelfExit, { children: element }));
207
- try {
208
- await instance.waitUntilExit();
209
- } catch {
210
- } finally {
211
- instance.unmount();
212
- }
213
- };
214
- const tuiIntro = async (message) => {
215
- if (!isInteractive()) {
216
- return;
217
- }
218
- await printFrame(
219
- jsxs(Box, { borderColor: ACCENT, borderStyle: "round", paddingX: 1, children: [
220
- jsxs(Text, { bold: true, color: ACCENT, children: [
221
- "◆ Lunora",
222
- " "
223
- ] }),
224
- jsx(Text, { children: message })
225
- ] })
226
- );
227
- };
228
- const tuiOutro = async (message) => {
229
- if (!isInteractive()) {
230
- return;
231
- }
232
- await printFrame(
233
- jsxs(Box, { paddingX: 1, children: [
234
- jsx(Text, { color: "green", children: "✔ " }),
235
- jsx(Text, { children: message })
236
- ] })
237
- );
238
- };
239
- const tuiBanner = async (subtitle) => {
240
- if (!isInteractive()) {
241
- return;
242
- }
243
- await printFrame(
244
- jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
245
- jsx(BigText, { colors: [ACCENT], font: "tiny", space: false, text: "LUNORA" }),
246
- jsx(Text, { dimColor: true, children: subtitle })
247
- ] })
248
- );
249
- };
250
- const SpinnerView = ({ label }) => jsxs(Box, { children: [
251
- jsx(Text, { color: ACCENT, children: jsx(Spinner, { type: "dots" }) }),
252
- jsxs(Text, { children: [
253
- " ",
254
- label
255
- ] })
256
- ] });
257
- const withTuiSpinner = async (label, task) => {
258
- if (!isInteractive()) {
259
- return task();
260
- }
261
- const instance = render(jsx(SpinnerView, { label }));
262
- try {
263
- return await task();
264
- } finally {
265
- instance.unmount();
266
- }
267
- };
268
-
269
- export { tuiSelect as a, tuiConfirm as b, createTuiConfirm as c, tuiOutro as d, tuiBanner as e, tuiIntro as f, tuiMultiSelect as g, tuiText as t, withTuiSpinner as w };