@lunora/cli 1.0.0-alpha.3 → 1.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/packem_chunks/handler.mjs +4 -3
- package/dist/packem_chunks/handler16.mjs +1 -1
- package/dist/packem_chunks/handler18.mjs +3 -5
- package/dist/packem_chunks/planDevCommand.mjs +3 -2
- package/dist/packem_chunks/runInitCommand.mjs +75 -21
- package/dist/packem_chunks/runResetCommand.mjs +3 -3
- package/dist/packem_shared/{COMMANDS-CHw4zOZ9.mjs → COMMANDS-DXaq12xm.mjs} +2 -2
- package/dist/packem_shared/{commands-DIQ3nf0C.mjs → commands-B9nASOYd.mjs} +65 -20
- package/dist/packem_shared/runAddCommand-BF7XreDW.mjs +4 -0
- package/dist/packem_shared/tui-prompts-Bm15GPJA.mjs +229 -0
- package/package.json +4 -1
- package/dist/packem_shared/runAddCommand-3I3JFZUG.mjs +0 -4
package/dist/bin.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -532,7 +532,7 @@ interface OfferDeps {
|
|
|
532
532
|
* maps to the `mail` item. Picked items are applied in selection order.
|
|
533
533
|
* Non-interactive: prints how to add them later and changes nothing.
|
|
534
534
|
*/
|
|
535
|
-
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
|
|
535
|
+
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite-react";
|
|
536
536
|
interface InitCommandOptions {
|
|
537
537
|
/**
|
|
538
538
|
* When true, accept `--source` values that don't start with `gh:` /
|
|
@@ -605,7 +605,7 @@ interface InitCommandResult {
|
|
|
605
605
|
* scaffold's exit code.
|
|
606
606
|
*/
|
|
607
607
|
declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
|
|
608
|
-
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
|
|
608
|
+
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite-react). */
|
|
609
609
|
interface MigrateGenerateCommandOptions {
|
|
610
610
|
cwd?: string;
|
|
611
611
|
logger: Logger;
|
package/dist/index.d.ts
CHANGED
|
@@ -532,7 +532,7 @@ interface OfferDeps {
|
|
|
532
532
|
* maps to the `mail` item. Picked items are applied in selection order.
|
|
533
533
|
* Non-interactive: prints how to add them later and changes nothing.
|
|
534
534
|
*/
|
|
535
|
-
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
|
|
535
|
+
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite-react";
|
|
536
536
|
interface InitCommandOptions {
|
|
537
537
|
/**
|
|
538
538
|
* When true, accept `--source` values that don't start with `gh:` /
|
|
@@ -605,7 +605,7 @@ interface InitCommandResult {
|
|
|
605
605
|
* scaffold's exit code.
|
|
606
606
|
*/
|
|
607
607
|
declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
|
|
608
|
-
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
|
|
608
|
+
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite-react). */
|
|
609
609
|
interface MigrateGenerateCommandOptions {
|
|
610
610
|
cwd?: string;
|
|
611
611
|
logger: Logger;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-
|
|
1
|
+
export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-DXaq12xm.mjs';
|
|
2
2
|
export { runCodegenCommand } from './packem_chunks/runCodegenCommand.mjs';
|
|
3
3
|
export { DEFAULT_IMPORT_BATCH_SIZE, runExportCommand, runImportCommand } from './packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
|
|
4
4
|
export { runDeployCommand } from './packem_chunks/runDeployCommand.mjs';
|
|
@@ -16,4 +16,4 @@ export { createRecordingSpawner, defaultSpawner } from './packem_shared/defaultS
|
|
|
16
16
|
export { default as parseManifest } from './packem_shared/parseManifest--vZf2FY1.mjs';
|
|
17
17
|
export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
|
|
18
18
|
export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-BcYe607_.mjs';
|
|
19
|
-
export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-
|
|
19
|
+
export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-B9nASOYd.mjs';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { findWranglerFile
|
|
2
|
+
import { findWranglerFile } from '@lunora/config';
|
|
3
3
|
import { join } from '@visulima/path';
|
|
4
4
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
5
|
+
import { t as tuiSelect } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
5
6
|
import { n as normalizeFeature, E as EMAIL_ITEM, D as DEFAULT_AUTH_ITEM, p as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS } from '../packem_shared/features-ocSSpZtS.mjs';
|
|
6
|
-
import { r as runAddCommand } from '../packem_shared/commands-
|
|
7
|
+
import { r as runAddCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
7
8
|
|
|
8
9
|
const providerToItem = (provider) => {
|
|
9
10
|
const value = provider.trim().toLowerCase();
|
|
@@ -24,7 +25,7 @@ const resolveAuthItem = async (options) => {
|
|
|
24
25
|
if (options.yes === true) {
|
|
25
26
|
return DEFAULT_AUTH_ITEM;
|
|
26
27
|
}
|
|
27
|
-
const select = options.promptSelect ?? ((message, choices, settings) =>
|
|
28
|
+
const select = options.promptSelect ?? ((message, choices, settings) => tuiSelect(message, choices, settings));
|
|
28
29
|
return promptAuthProvider(select);
|
|
29
30
|
};
|
|
30
31
|
const resolveFeatureItems = async (feature, options) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
2
|
-
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-
|
|
2
|
+
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
3
3
|
|
|
4
4
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
5
5
|
const subcommand = argument[0];
|
|
@@ -2,12 +2,12 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { discoverSchema, schemaFromIr } from '@lunora/codegen';
|
|
5
|
-
import { promptYesNo } from '@lunora/config';
|
|
6
5
|
import { seedPlan } from '@lunora/seed';
|
|
7
6
|
import { join } from '@visulima/path';
|
|
8
7
|
import { Project } from 'ts-morph';
|
|
9
8
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
10
9
|
import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
10
|
+
import { a as tuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
11
11
|
import { runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
|
|
12
12
|
import { runResetCommand } from './runResetCommand.mjs';
|
|
13
13
|
|
|
@@ -88,10 +88,8 @@ const confirmRemoteSeedTarget = async (options, generated) => {
|
|
|
88
88
|
options.logger.error("seed: refusing to insert into a non-local target without confirmation — re-run with --yes");
|
|
89
89
|
return seedFailure(1);
|
|
90
90
|
}
|
|
91
|
-
const confirmer = options.confirm ??
|
|
92
|
-
const confirmed = await confirmer(
|
|
93
|
-
`This will insert ${String(generated)} generated row(s) into ${options.url ?? "the production worker"}. Continue? [y/N] `
|
|
94
|
-
);
|
|
91
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
92
|
+
const confirmed = await confirmer(`This will insert ${String(generated)} generated row(s) into ${options.url ?? "the production worker"}. Continue?`);
|
|
95
93
|
if (!confirmed) {
|
|
96
94
|
options.logger.info("seed: aborted");
|
|
97
95
|
return seedFailure(1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { detectAgentRules, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables,
|
|
2
|
+
import { detectAgentRules, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables, isInteractive, DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, claimAgentRulesHint, AGENT_RULES_HINT, materializeRemoteWranglerConfig, formatLunoraEvent, resolveRemoteEnabled, readProjectRemotePreference } from '@lunora/config';
|
|
3
3
|
import { p as parseApiSpec } from '../packem_shared/api-spec-CtA6ilu4.mjs';
|
|
4
4
|
import { existsSync, watch, readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
@@ -9,6 +9,7 @@ import { dirname, join as join$1 } from '@visulima/path';
|
|
|
9
9
|
import { createServer, request } from 'node:http';
|
|
10
10
|
import { connect } from 'node:net';
|
|
11
11
|
import { loadStudioAssets, studioAssetsStamp, renderStudioHtml, resolveAdminToken, SCHEMA_EDIT_ENDPOINT, POLICY_SCAFFOLD_ENDPOINT, SEED_ENDPOINT, serveJsonHandler, handleSchemaEditRequest, handlePolicyScaffoldRequest, handleSeedRequest } from '@lunora/config/studio-host';
|
|
12
|
+
import { c as createTuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
12
13
|
|
|
13
14
|
const DEFAULT_DEBOUNCE_MS = 100;
|
|
14
15
|
const PATH_SEGMENT_SEPARATOR = /[/\\]/u;
|
|
@@ -441,7 +442,7 @@ const offerDevVariablesScaffold = async (options, cwd) => {
|
|
|
441
442
|
} catch {
|
|
442
443
|
}
|
|
443
444
|
const result = await (options.ensureEnv ?? ensureDevVariables)({
|
|
444
|
-
confirm:
|
|
445
|
+
confirm: createTuiConfirm(),
|
|
445
446
|
cwd,
|
|
446
447
|
info: (message) => {
|
|
447
448
|
options.logger.info(message);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync, readdirSync, mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import { detectFramework as detectFramework$1, isInteractive
|
|
3
|
+
import { detectFramework as detectFramework$1, isInteractive } from '@lunora/config';
|
|
4
4
|
import { walkSync } from '@visulima/fs';
|
|
5
|
-
import { resolve, join as join$1, relative, dirname as dirname$1 } from '@visulima/path';
|
|
5
|
+
import { resolve, join as join$1, relative, dirname as dirname$1, basename } from '@visulima/path';
|
|
6
6
|
import { downloadTemplate } from 'giget';
|
|
7
|
+
import { modify, applyEdits } from 'jsonc-parser';
|
|
7
8
|
import { join, dirname } from 'node:path';
|
|
8
9
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
9
10
|
import MagicString from 'magic-string';
|
|
10
11
|
import { Project, SyntaxKind } from 'ts-morph';
|
|
11
|
-
import { c as resolveSourceRef, r as runAddCommand } from '../packem_shared/commands-
|
|
12
|
+
import { c as resolveSourceRef, d as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
13
|
+
import { b as tuiIntro, d as tuiOutro, t as tuiSelect, e as tuiMultiSelect, w as withTuiSpinner } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
12
14
|
import { p as promptAuthProvider, E as EMAIL_ITEM } from '../packem_shared/features-ocSSpZtS.mjs';
|
|
13
15
|
|
|
14
16
|
const GITHUB_CONTENT = `name: Deploy
|
|
@@ -19,6 +21,10 @@ on:
|
|
|
19
21
|
pull_request:
|
|
20
22
|
workflow_dispatch:
|
|
21
23
|
|
|
24
|
+
# Prerequisite: commit your pnpm-lock.yaml. \`pnpm install --frozen-lockfile\`
|
|
25
|
+
# (below) and the pnpm cache both require it — run \`pnpm install\` locally and
|
|
26
|
+
# commit the lockfile before pushing, or the first CI run fails.
|
|
27
|
+
#
|
|
22
28
|
# Set these repository secrets (Settings → Secrets and variables → Actions):
|
|
23
29
|
# CLOUDFLARE_API_TOKEN — a Workers-scoped API token
|
|
24
30
|
# CLOUDFLARE_ACCOUNT_ID — your Cloudflare account id
|
|
@@ -65,6 +71,10 @@ jobs:
|
|
|
65
71
|
const GITLAB_CONTENT = `stages:
|
|
66
72
|
- deploy
|
|
67
73
|
|
|
74
|
+
# Prerequisite: commit your pnpm-lock.yaml. \`pnpm install --frozen-lockfile\`
|
|
75
|
+
# (below) requires it — run \`pnpm install\` locally and commit the lockfile
|
|
76
|
+
# before pushing, or the first pipeline fails.
|
|
77
|
+
#
|
|
68
78
|
# Set these as masked CI/CD variables (Settings → CI/CD → Variables):
|
|
69
79
|
# CLOUDFLARE_API_TOKEN — a Workers-scoped API token
|
|
70
80
|
# CLOUDFLARE_ACCOUNT_ID — your Cloudflare account id
|
|
@@ -126,6 +136,9 @@ const scaffoldCiWorkflow = (projectRoot, provider, logger, options = {}) => {
|
|
|
126
136
|
} else {
|
|
127
137
|
logger.success(`--ci ${provider}: wrote ${spec.file}`);
|
|
128
138
|
logger.info(`--ci ${provider}: set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as ${spec.secretsHint} to enable deploys.`);
|
|
139
|
+
logger.info(
|
|
140
|
+
`--ci ${provider}: run \`pnpm install\` and commit pnpm-lock.yaml before pushing — the pipeline runs \`pnpm install --frozen-lockfile\`.`
|
|
141
|
+
);
|
|
129
142
|
}
|
|
130
143
|
return result;
|
|
131
144
|
} catch (error) {
|
|
@@ -331,6 +344,26 @@ const isTextFile = (filePath) => {
|
|
|
331
344
|
return TEXT_EXTENSIONS.has(filePath.slice(lastDot));
|
|
332
345
|
};
|
|
333
346
|
const substitute = (content, name) => content.replaceAll("{{name}}", name);
|
|
347
|
+
const isLunoraDep = (name) => name === "lunorash" || name.startsWith("@lunora/");
|
|
348
|
+
const stampLunoraDeps = (packageJsonText, distTag) => {
|
|
349
|
+
let parsed;
|
|
350
|
+
try {
|
|
351
|
+
parsed = JSON.parse(packageJsonText);
|
|
352
|
+
} catch {
|
|
353
|
+
return packageJsonText;
|
|
354
|
+
}
|
|
355
|
+
let text = packageJsonText;
|
|
356
|
+
for (const section of ["dependencies", "devDependencies"]) {
|
|
357
|
+
for (const name of Object.keys(parsed[section] ?? {})) {
|
|
358
|
+
if (!isLunoraDep(name)) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const edits = modify(text, [section, name], distTag, { formattingOptions: { insertSpaces: true, tabSize: 4 } });
|
|
362
|
+
text = applyEdits(text, edits);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return text;
|
|
366
|
+
};
|
|
334
367
|
const collectFiles = (directory) => {
|
|
335
368
|
const out = [];
|
|
336
369
|
for (const entry of walkSync(directory, { includeDirs: false, includeFiles: true })) {
|
|
@@ -341,12 +374,16 @@ const collectFiles = (directory) => {
|
|
|
341
374
|
const copyTemplate = (sourceDirectory, target, name) => {
|
|
342
375
|
const files = collectFiles(sourceDirectory);
|
|
343
376
|
const written = [];
|
|
377
|
+
const distTag = resolveDistTag();
|
|
344
378
|
for (const source of files) {
|
|
345
379
|
const relativePath = relative(sourceDirectory, source);
|
|
346
380
|
const destination = join$1(target, relativePath);
|
|
347
381
|
mkdirSync(dirname$1(destination), { recursive: true });
|
|
348
382
|
const raw = readFileSync(source);
|
|
349
|
-
|
|
383
|
+
let text = isTextFile(source) ? substitute(raw.toString("utf8"), name) : void 0;
|
|
384
|
+
if (text !== void 0 && basename(source) === "package.json") {
|
|
385
|
+
text = stampLunoraDeps(text, distTag);
|
|
386
|
+
}
|
|
350
387
|
if (text === void 0) {
|
|
351
388
|
writeFileSync(destination, raw);
|
|
352
389
|
} else {
|
|
@@ -534,31 +571,48 @@ const offerIsInteractive = (options) => options.yes !== true && (options.prompt
|
|
|
534
571
|
const maybeOfferExtras = async (options, projectDirectory) => {
|
|
535
572
|
const interactive = offerIsInteractive(options);
|
|
536
573
|
const apply = async (names) => {
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
574
|
+
const applyLogger = isInteractive() ? {
|
|
575
|
+
error: (message) => {
|
|
576
|
+
options.logger.error(message);
|
|
577
|
+
},
|
|
578
|
+
info: () => {
|
|
579
|
+
},
|
|
580
|
+
success: () => {
|
|
581
|
+
},
|
|
582
|
+
warn: (message) => {
|
|
583
|
+
options.logger.warn(message);
|
|
584
|
+
}
|
|
585
|
+
} : options.logger;
|
|
586
|
+
const result = await withTuiSpinner(
|
|
587
|
+
`adding ${names.join(", ")}…`,
|
|
588
|
+
() => runAddCommand({
|
|
589
|
+
allowUnsafeSource: options.allowUnsafeSource,
|
|
590
|
+
cwd: projectDirectory,
|
|
591
|
+
from: options.registryFrom,
|
|
592
|
+
logger: applyLogger,
|
|
593
|
+
names: [...names],
|
|
594
|
+
ref: options.ref,
|
|
595
|
+
source: options.registrySource,
|
|
596
|
+
yes: true
|
|
597
|
+
})
|
|
598
|
+
);
|
|
547
599
|
return result.code === 0;
|
|
548
600
|
};
|
|
601
|
+
await tuiIntro("let's finish setting up your app");
|
|
549
602
|
await offerRegistryExtras({
|
|
550
603
|
apply,
|
|
551
604
|
interactive,
|
|
552
605
|
logger: options.logger,
|
|
553
|
-
multiSelect: options.prompt?.multiSelect ?? ((message, choices, settings) =>
|
|
554
|
-
select: options.prompt?.select ?? ((message, choices, settings) =>
|
|
606
|
+
multiSelect: options.prompt?.multiSelect ?? ((message, choices, settings) => tuiMultiSelect(message, choices, settings)),
|
|
607
|
+
select: options.prompt?.select ?? ((message, choices, settings) => tuiSelect(message, choices, settings))
|
|
555
608
|
});
|
|
609
|
+
await tuiOutro("you're all set — run `pnpm dev` to start.");
|
|
556
610
|
};
|
|
557
611
|
const scaffoldNewProject = async (options, cwd) => {
|
|
558
612
|
const name = options.name ?? "lunora-app";
|
|
559
|
-
const templateType = options.templateType ?? "vite";
|
|
613
|
+
const templateType = options.templateType ?? "vite-react";
|
|
560
614
|
if (templateType === "next") {
|
|
561
|
-
options.logger.warn('template "next" is not yet available — re-run with `-t vite` or `-t standalone`.');
|
|
615
|
+
options.logger.warn('template "next" is not yet available — re-run with `-t vite-react` or `-t standalone`.');
|
|
562
616
|
return { code: 1, files: [], target: "" };
|
|
563
617
|
}
|
|
564
618
|
if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
|
|
@@ -595,7 +649,7 @@ const runInitCommand = async (options) => {
|
|
|
595
649
|
}
|
|
596
650
|
return result;
|
|
597
651
|
};
|
|
598
|
-
const isTemplate = (value) => value === "astro" || value === "next" || value === "nuxt" || value === "standalone" || value === "sveltekit" || value === "tanstack-start-react" || value === "tanstack-start-solid" || value === "vite";
|
|
652
|
+
const isTemplate = (value) => value === "astro" || value === "next" || value === "nuxt" || value === "standalone" || value === "sveltekit" || value === "tanstack-start-react" || value === "tanstack-start-solid" || value === "vite-react";
|
|
599
653
|
const resolveCiProvider = (raw, logger) => {
|
|
600
654
|
if (raw === void 0) {
|
|
601
655
|
return void 0;
|
|
@@ -607,8 +661,8 @@ const resolveCiProvider = (raw, logger) => {
|
|
|
607
661
|
return void 0;
|
|
608
662
|
};
|
|
609
663
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
610
|
-
const templateRaw = options.template ?? "vite";
|
|
611
|
-
const template = isTemplate(templateRaw) ? templateRaw : "vite";
|
|
664
|
+
const templateRaw = options.template ?? "vite-react";
|
|
665
|
+
const template = isTemplate(templateRaw) ? templateRaw : "vite-react";
|
|
612
666
|
return runInitCommand({
|
|
613
667
|
allowUnsafeSource: options.allowUnsafeSource === true,
|
|
614
668
|
cwd,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync } from 'node:fs';
|
|
2
|
-
import { promptYesNo } from '@lunora/config';
|
|
3
2
|
import { join } from '@visulima/path';
|
|
4
3
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
4
|
+
import { a as tuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
5
5
|
|
|
6
6
|
const runResetCommand = async (options) => {
|
|
7
7
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -15,8 +15,8 @@ const runResetCommand = async (options) => {
|
|
|
15
15
|
options.logger.error("reset: stdin is not a TTY — re-run with --yes to confirm deleting .wrangler/state");
|
|
16
16
|
return { code: 1, removed: [] };
|
|
17
17
|
}
|
|
18
|
-
const confirmer = options.confirm ??
|
|
19
|
-
const confirmed = await confirmer("This will delete .wrangler/state. Continue?
|
|
18
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
19
|
+
const confirmed = await confirmer("This will delete .wrangler/state. Continue?");
|
|
20
20
|
if (!confirmed) {
|
|
21
21
|
options.logger.info("reset: aborted");
|
|
22
22
|
return { code: 1, removed: [] };
|
|
@@ -361,8 +361,8 @@ const initCommand = {
|
|
|
361
361
|
options: [
|
|
362
362
|
{
|
|
363
363
|
alias: "t",
|
|
364
|
-
defaultValue: "vite",
|
|
365
|
-
description: "Template to scaffold (vite | standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid)",
|
|
364
|
+
defaultValue: "vite-react",
|
|
365
|
+
description: "Template to scaffold (vite-react | standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid)",
|
|
366
366
|
name: "template",
|
|
367
367
|
type: String
|
|
368
368
|
},
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from '@visulima/path';
|
|
3
|
-
import { DEV_VARS_FILE, parseDevVariableEntries
|
|
3
|
+
import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
|
|
4
4
|
import { modify, applyEdits, parse } from 'jsonc-parser';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { a as tuiConfirm } from './tui-prompts-Bm15GPJA.mjs';
|
|
5
7
|
import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BcYe607_.mjs';
|
|
6
8
|
import { insertSchemaExtension } from './insertSchemaExtension-BuzF6-t2.mjs';
|
|
7
9
|
import { createHash } from 'node:crypto';
|
|
8
10
|
import { tmpdir } from 'node:os';
|
|
9
11
|
import { downloadTemplate } from 'giget';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import parseManifest from './parseManifest--vZf2FY1.mjs';
|
|
12
13
|
|
|
13
14
|
const DEFAULT_SOURCE_REF_FALLBACK = "alpha";
|
|
@@ -59,8 +60,38 @@ const resolveSourceRef = (ref) => {
|
|
|
59
60
|
}
|
|
60
61
|
return resolveVersionRef(resolveCliVersion());
|
|
61
62
|
};
|
|
63
|
+
const STABLE_DIST_TAG = "latest";
|
|
64
|
+
const resolveDistTag = () => {
|
|
65
|
+
const ref = resolveVersionRef(resolveCliVersion());
|
|
66
|
+
return ref === STABLE_BRANCH ? STABLE_DIST_TAG : ref;
|
|
67
|
+
};
|
|
62
68
|
|
|
63
|
-
const
|
|
69
|
+
const resolveDepRange = (range) => {
|
|
70
|
+
if (!range.startsWith("workspace:")) {
|
|
71
|
+
return range;
|
|
72
|
+
}
|
|
73
|
+
const rest = range.slice("workspace:".length);
|
|
74
|
+
if (rest === "" || rest === "*" || rest === "^" || rest === "~") {
|
|
75
|
+
return resolveDistTag();
|
|
76
|
+
}
|
|
77
|
+
return rest;
|
|
78
|
+
};
|
|
79
|
+
const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
|
|
80
|
+
const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|runtime|server|values)(\/[^'"]*)?\1/gu;
|
|
81
|
+
const projectUsesUmbrella = (projectRoot) => {
|
|
82
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
83
|
+
if (!existsSync(packageJsonPath)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
88
|
+
return parsed.dependencies?.lunorash !== void 0 || parsed.devDependencies?.lunorash !== void 0;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const rewriteUmbrellaImports = (source) => source.replaceAll(UMBRELLA_IMPORT_RE, (_match, quote, base, subpath) => `${quote}lunorash/${base}${subpath ?? ""}${quote}`);
|
|
94
|
+
const applyDeps = (deps, projectRoot, logger, section = "dependencies", useUmbrella = false) => {
|
|
64
95
|
const entries = Object.entries(deps);
|
|
65
96
|
if (entries.length === 0) {
|
|
66
97
|
return [];
|
|
@@ -74,11 +105,15 @@ const applyDeps = (deps, projectRoot, logger, section = "dependencies") => {
|
|
|
74
105
|
const parsed = JSON.parse(text);
|
|
75
106
|
const added = [];
|
|
76
107
|
for (const [name, range] of entries) {
|
|
108
|
+
if (useUmbrella && UMBRELLA_REEXPORTED_DEPS.has(name)) {
|
|
109
|
+
logger.info(`dep provided by the lunorash umbrella, skipping: ${name}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
77
112
|
if (parsed.dependencies?.[name] !== void 0 || parsed.devDependencies?.[name] !== void 0) {
|
|
78
113
|
logger.info(`dep already present: ${name}`);
|
|
79
114
|
continue;
|
|
80
115
|
}
|
|
81
|
-
const edits = modify(text, [section, name], range, {
|
|
116
|
+
const edits = modify(text, [section, name], resolveDepRange(range), {
|
|
82
117
|
formattingOptions: { insertSpaces: true, tabSize: 4 }
|
|
83
118
|
});
|
|
84
119
|
text = applyEdits(text, edits);
|
|
@@ -199,14 +234,14 @@ const applyBindings = (bindings, projectRoot, logger) => {
|
|
|
199
234
|
}
|
|
200
235
|
return applied;
|
|
201
236
|
};
|
|
202
|
-
const applyItemResources = (manifest, cwd, logger) => {
|
|
237
|
+
const applyItemResources = (manifest, cwd, logger, useUmbrella = false) => {
|
|
203
238
|
const deps = [];
|
|
204
239
|
const bindings = [];
|
|
205
240
|
if (manifest.deps) {
|
|
206
|
-
deps.push(...applyDeps(manifest.deps, cwd, logger));
|
|
241
|
+
deps.push(...applyDeps(manifest.deps, cwd, logger, "dependencies", useUmbrella));
|
|
207
242
|
}
|
|
208
243
|
if (manifest.devDependencies) {
|
|
209
|
-
deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies"));
|
|
244
|
+
deps.push(...applyDeps(manifest.devDependencies, cwd, logger, "devDependencies", useUmbrella));
|
|
210
245
|
}
|
|
211
246
|
if (manifest.bindings) {
|
|
212
247
|
bindings.push(...applyBindings(manifest.bindings, cwd, logger));
|
|
@@ -238,8 +273,8 @@ const confirmDepMutation = async (items, options) => {
|
|
|
238
273
|
options.logger.error(`add: stdin is not a TTY and the requested items ${reasonText} — re-run with --yes to confirm`);
|
|
239
274
|
return false;
|
|
240
275
|
}
|
|
241
|
-
const confirmer = options.confirm ??
|
|
242
|
-
const confirmed = await confirmer(`The requested items ${reasonText}. Continue
|
|
276
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
277
|
+
const confirmed = await confirmer(`The requested items ${reasonText}. Continue?`);
|
|
243
278
|
if (!confirmed) {
|
|
244
279
|
options.logger.info("add: aborted");
|
|
245
280
|
}
|
|
@@ -318,7 +353,12 @@ const renderDiff = (oldText, newText) => {
|
|
|
318
353
|
return out;
|
|
319
354
|
};
|
|
320
355
|
|
|
321
|
-
const
|
|
356
|
+
const CODE_FILE_RE = /\.[cm]?[jt]sx?$/u;
|
|
357
|
+
const readItemFile = (itemDirectory, file, useUmbrella) => {
|
|
358
|
+
const source = readFileSync(join(itemDirectory, file.from), "utf8");
|
|
359
|
+
return useUmbrella && CODE_FILE_RE.test(file.to) ? rewriteUmbrellaImports(source) : source;
|
|
360
|
+
};
|
|
361
|
+
const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, logger, diff, useUmbrella) => {
|
|
322
362
|
const schemaPath = join(projectRoot, "lunora", "schema.ts");
|
|
323
363
|
if (diff) {
|
|
324
364
|
logger.info(`~ would merge .extend(${itemKey}.extension) into lunora/schema.ts (and create ${file.to} if absent)`);
|
|
@@ -327,9 +367,13 @@ const reconcileSchemaExtension = (file, itemKey, itemDirectory, projectRoot, log
|
|
|
327
367
|
const destinationPath = join(projectRoot, file.to);
|
|
328
368
|
if (!existsSync(destinationPath)) {
|
|
329
369
|
mkdirSync(dirname(destinationPath), { recursive: true });
|
|
330
|
-
writeFileSync(destinationPath,
|
|
370
|
+
writeFileSync(destinationPath, readItemFile(itemDirectory, file, useUmbrella), "utf8");
|
|
331
371
|
}
|
|
332
|
-
const
|
|
372
|
+
const baseModule = useUmbrella ? "lunorash/server" : "@lunora/server";
|
|
373
|
+
const existingSchema = existsSync(schemaPath) ? readFileSync(schemaPath, "utf8") : `import { defineSchema } from "${baseModule}";
|
|
374
|
+
|
|
375
|
+
export const schema = defineSchema({});
|
|
376
|
+
`;
|
|
333
377
|
const result = insertSchemaExtension(existingSchema, itemKey);
|
|
334
378
|
if (result.ok) {
|
|
335
379
|
mkdirSync(dirname(schemaPath), { recursive: true });
|
|
@@ -359,9 +403,9 @@ const previewWholeFile = (file, current, incoming, exists, logger) => {
|
|
|
359
403
|
logger.info(` ${line}`);
|
|
360
404
|
}
|
|
361
405
|
};
|
|
362
|
-
const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions) => {
|
|
406
|
+
const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella) => {
|
|
363
407
|
const destinationPath = join(projectRoot, file.to);
|
|
364
|
-
const incoming =
|
|
408
|
+
const incoming = readItemFile(itemDirectory, file, useUmbrella);
|
|
365
409
|
const exists = existsSync(destinationPath);
|
|
366
410
|
const current = exists ? readFileSync(destinationPath, "utf8") : "";
|
|
367
411
|
const write = (message) => {
|
|
@@ -399,11 +443,11 @@ const reconcileWholeFile = (file, itemKey, itemDirectory, projectRoot, logger, l
|
|
|
399
443
|
logger.warn(`conflict: ${file.to} has local edits and an upstream update — wrote ${file.to}.new (use --overwrite to take theirs)`);
|
|
400
444
|
return { kind: "skipped", path: destinationPath };
|
|
401
445
|
};
|
|
402
|
-
const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}) => {
|
|
446
|
+
const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions = {}, useUmbrella = false) => {
|
|
403
447
|
if (file.merge === "schema-extension") {
|
|
404
|
-
return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true);
|
|
448
|
+
return reconcileSchemaExtension(file, itemKey, itemDirectory, projectRoot, logger, reconcileOptions.diff === true, useUmbrella);
|
|
405
449
|
}
|
|
406
|
-
return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions);
|
|
450
|
+
return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella);
|
|
407
451
|
};
|
|
408
452
|
const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
409
453
|
const written = [];
|
|
@@ -411,15 +455,16 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
|
411
455
|
const depsAdded = [];
|
|
412
456
|
const bindingsApplied = [];
|
|
413
457
|
const lock = readLock(cwd);
|
|
458
|
+
const useUmbrella = projectUsesUmbrella(cwd);
|
|
414
459
|
for (const { directory, manifest } of items) {
|
|
415
460
|
for (const file of manifest.files) {
|
|
416
|
-
const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions);
|
|
461
|
+
const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
|
|
417
462
|
(outcome.kind === "written" ? written : skipped).push(outcome.path);
|
|
418
463
|
}
|
|
419
464
|
if (reconcileOptions.diff) {
|
|
420
465
|
continue;
|
|
421
466
|
}
|
|
422
|
-
const applied = applyItemResources(manifest, cwd, logger);
|
|
467
|
+
const applied = applyItemResources(manifest, cwd, logger, useUmbrella);
|
|
423
468
|
depsAdded.push(...applied.deps);
|
|
424
469
|
bindingsApplied.push(...applied.bindings);
|
|
425
470
|
}
|
|
@@ -740,4 +785,4 @@ const runBuildIndexCommand = async (options) => {
|
|
|
740
785
|
return empty;
|
|
741
786
|
};
|
|
742
787
|
|
|
743
|
-
export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveSourceRef as c,
|
|
788
|
+
export { runBuildIndexCommand as a, runRegistryViewCommand as b, resolveSourceRef as c, resolveDistTag as d, runListCommand as e, runAddCommand as r };
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { isInteractive } from '@lunora/config';
|
|
3
|
+
import { render } from '@visulima/tui';
|
|
4
|
+
import { Box } from '@visulima/tui/components/box';
|
|
5
|
+
import { CommandPalette } from '@visulima/tui/components/command-palette';
|
|
6
|
+
import { ConfirmInput } from '@visulima/tui/components/confirm-input';
|
|
7
|
+
import { MultiSelect } from '@visulima/tui/components/multi-select';
|
|
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 { useApp } from '@visulima/tui/hooks/use-app';
|
|
12
|
+
import { useInput } from '@visulima/tui/hooks/use-input';
|
|
13
|
+
import { useEffect } from 'react';
|
|
14
|
+
|
|
15
|
+
const ACCENT = "#a855f7";
|
|
16
|
+
const SCROLL_LIMIT = 10;
|
|
17
|
+
const FILTER_THRESHOLD = 8;
|
|
18
|
+
const PromptFrame = ({ children }) => jsx(Box, { borderColor: ACCENT, borderStyle: "round", flexDirection: "column", paddingX: 1, children });
|
|
19
|
+
const runInkPrompt = async (build, fallback) => {
|
|
20
|
+
let result = fallback;
|
|
21
|
+
const instance = render(
|
|
22
|
+
build((value) => {
|
|
23
|
+
result = value;
|
|
24
|
+
}),
|
|
25
|
+
{ exitOnCtrlC: true }
|
|
26
|
+
);
|
|
27
|
+
try {
|
|
28
|
+
await instance.waitUntilExit();
|
|
29
|
+
} catch {
|
|
30
|
+
} finally {
|
|
31
|
+
instance.unmount();
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
const itemLabel = (option) => option.description === void 0 ? option.label : `${option.label} — ${option.description}`;
|
|
36
|
+
const useCommit = (finish) => {
|
|
37
|
+
const { exit } = useApp();
|
|
38
|
+
return (result) => {
|
|
39
|
+
finish(result);
|
|
40
|
+
exit();
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const useEscapeToExit = () => {
|
|
44
|
+
const { exit } = useApp();
|
|
45
|
+
useInput((_input, key) => {
|
|
46
|
+
if (key.escape) {
|
|
47
|
+
exit();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const SelectView = ({ finish, initialIndex, message, options }) => {
|
|
52
|
+
const commit = useCommit(finish);
|
|
53
|
+
useEscapeToExit();
|
|
54
|
+
return jsxs(PromptFrame, { children: [
|
|
55
|
+
jsx(Text, { bold: true, children: message }),
|
|
56
|
+
jsx(
|
|
57
|
+
SelectInput,
|
|
58
|
+
{
|
|
59
|
+
accentColor: ACCENT,
|
|
60
|
+
initialIndex,
|
|
61
|
+
items: options.map((option) => {
|
|
62
|
+
return { key: option.value, label: itemLabel(option), value: option.value };
|
|
63
|
+
}),
|
|
64
|
+
limit: SCROLL_LIMIT,
|
|
65
|
+
onSelect: (item) => {
|
|
66
|
+
commit(item.value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] });
|
|
71
|
+
};
|
|
72
|
+
const PaletteView = ({ finish, message, options }) => {
|
|
73
|
+
const { exit } = useApp();
|
|
74
|
+
const commit = useCommit(finish);
|
|
75
|
+
return jsxs(Box, { flexDirection: "column", children: [
|
|
76
|
+
jsx(Text, { bold: true, children: message }),
|
|
77
|
+
jsx(
|
|
78
|
+
CommandPalette,
|
|
79
|
+
{
|
|
80
|
+
accentColor: ACCENT,
|
|
81
|
+
autoFocus: true,
|
|
82
|
+
commands: options.map((option) => {
|
|
83
|
+
return { description: option.description, id: option.value, label: option.label };
|
|
84
|
+
}),
|
|
85
|
+
limit: SCROLL_LIMIT,
|
|
86
|
+
onCancel: () => {
|
|
87
|
+
exit();
|
|
88
|
+
},
|
|
89
|
+
onSelect: (id) => {
|
|
90
|
+
commit(id);
|
|
91
|
+
},
|
|
92
|
+
placeholder: "Type to filter…"
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] });
|
|
96
|
+
};
|
|
97
|
+
const tuiSelect = async (message, options, settings) => {
|
|
98
|
+
if (!isInteractive() || options.length === 0) {
|
|
99
|
+
return settings?.default;
|
|
100
|
+
}
|
|
101
|
+
if (options.length > FILTER_THRESHOLD) {
|
|
102
|
+
return runInkPrompt((finish) => jsx(PaletteView, { finish, message, options }), settings?.default);
|
|
103
|
+
}
|
|
104
|
+
const defaultIndex = settings?.default === void 0 ? -1 : options.findIndex((option) => option.value === settings.default);
|
|
105
|
+
return runInkPrompt(
|
|
106
|
+
(finish) => jsx(SelectView, { finish, initialIndex: defaultIndex >= 0 ? defaultIndex : void 0, message, options }),
|
|
107
|
+
settings?.default
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
const MultiSelectView = ({ defaults, finish, message, options }) => {
|
|
111
|
+
const commit = useCommit(finish);
|
|
112
|
+
useEscapeToExit();
|
|
113
|
+
return jsxs(PromptFrame, { children: [
|
|
114
|
+
jsx(Text, { bold: true, children: message }),
|
|
115
|
+
jsx(Text, { dimColor: true, children: "space toggles · enter confirms · esc cancels" }),
|
|
116
|
+
jsx(
|
|
117
|
+
MultiSelect,
|
|
118
|
+
{
|
|
119
|
+
accentColor: ACCENT,
|
|
120
|
+
defaultValue: [...defaults],
|
|
121
|
+
limit: SCROLL_LIMIT,
|
|
122
|
+
onSubmit: (values) => {
|
|
123
|
+
const chosen = new Set(values);
|
|
124
|
+
commit(options.filter((option) => chosen.has(option.value)).map((option) => option.value));
|
|
125
|
+
},
|
|
126
|
+
options: options.map((option) => {
|
|
127
|
+
return { key: option.value, label: itemLabel(option), value: option.value };
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] });
|
|
132
|
+
};
|
|
133
|
+
const tuiMultiSelect = async (message, options, settings) => {
|
|
134
|
+
const defaults = settings?.defaults ?? [];
|
|
135
|
+
if (!isInteractive() || options.length === 0) {
|
|
136
|
+
return [...defaults];
|
|
137
|
+
}
|
|
138
|
+
return runInkPrompt((finish) => jsx(MultiSelectView, { defaults, finish, message, options }), [...defaults]);
|
|
139
|
+
};
|
|
140
|
+
const ConfirmView = ({ defaultYes, finish, message }) => {
|
|
141
|
+
const commit = useCommit(finish);
|
|
142
|
+
return jsx(PromptFrame, { children: jsxs(Box, { children: [
|
|
143
|
+
jsxs(Text, { bold: true, children: [
|
|
144
|
+
message,
|
|
145
|
+
" "
|
|
146
|
+
] }),
|
|
147
|
+
jsx(
|
|
148
|
+
ConfirmInput,
|
|
149
|
+
{
|
|
150
|
+
defaultChoice: defaultYes ? "confirm" : "cancel",
|
|
151
|
+
onCancel: () => {
|
|
152
|
+
commit(false);
|
|
153
|
+
},
|
|
154
|
+
onConfirm: () => {
|
|
155
|
+
commit(true);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] }) });
|
|
160
|
+
};
|
|
161
|
+
const tuiConfirm = async (message, options) => {
|
|
162
|
+
const defaultYes = options?.defaultYes === true;
|
|
163
|
+
if (!isInteractive()) {
|
|
164
|
+
return defaultYes;
|
|
165
|
+
}
|
|
166
|
+
return runInkPrompt((finish) => jsx(ConfirmView, { defaultYes, finish, message }), false);
|
|
167
|
+
};
|
|
168
|
+
const createTuiConfirm = () => isInteractive() ? (message) => tuiConfirm(message, { defaultYes: true }) : () => Promise.resolve(false);
|
|
169
|
+
const SelfExit = ({ children }) => {
|
|
170
|
+
const { exit } = useApp();
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
exit();
|
|
173
|
+
}, [exit]);
|
|
174
|
+
return children;
|
|
175
|
+
};
|
|
176
|
+
const printFrame = async (element) => {
|
|
177
|
+
const instance = render(jsx(SelfExit, { children: element }));
|
|
178
|
+
try {
|
|
179
|
+
await instance.waitUntilExit();
|
|
180
|
+
} catch {
|
|
181
|
+
} finally {
|
|
182
|
+
instance.unmount();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const tuiIntro = async (message) => {
|
|
186
|
+
if (!isInteractive()) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
await printFrame(
|
|
190
|
+
jsxs(Box, { borderColor: ACCENT, borderStyle: "round", paddingX: 1, children: [
|
|
191
|
+
jsxs(Text, { bold: true, color: ACCENT, children: [
|
|
192
|
+
"◆ Lunora",
|
|
193
|
+
" "
|
|
194
|
+
] }),
|
|
195
|
+
jsx(Text, { children: message })
|
|
196
|
+
] })
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
const tuiOutro = async (message) => {
|
|
200
|
+
if (!isInteractive()) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
await printFrame(
|
|
204
|
+
jsxs(Box, { paddingX: 1, children: [
|
|
205
|
+
jsx(Text, { color: "green", children: "✔ " }),
|
|
206
|
+
jsx(Text, { children: message })
|
|
207
|
+
] })
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
const SpinnerView = ({ label }) => jsxs(Box, { children: [
|
|
211
|
+
jsx(Text, { color: ACCENT, children: jsx(Spinner, { type: "dots" }) }),
|
|
212
|
+
jsxs(Text, { children: [
|
|
213
|
+
" ",
|
|
214
|
+
label
|
|
215
|
+
] })
|
|
216
|
+
] });
|
|
217
|
+
const withTuiSpinner = async (label, task) => {
|
|
218
|
+
if (!isInteractive()) {
|
|
219
|
+
return task();
|
|
220
|
+
}
|
|
221
|
+
const instance = render(jsx(SpinnerView, { label }));
|
|
222
|
+
try {
|
|
223
|
+
return await task();
|
|
224
|
+
} finally {
|
|
225
|
+
instance.unmount();
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export { tuiConfirm as a, tuiIntro as b, createTuiConfirm as c, tuiOutro as d, tuiMultiSelect as e, tuiSelect as t, withTuiSpinner as w };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.5",
|
|
4
4
|
"description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -65,9 +65,12 @@
|
|
|
65
65
|
"@visulima/pail": "4.0.0-alpha.22",
|
|
66
66
|
"@visulima/path": "3.0.0-alpha.13",
|
|
67
67
|
"@visulima/spinner": "1.0.0-alpha.4",
|
|
68
|
+
"@visulima/tui": "1.0.0-alpha.26",
|
|
68
69
|
"giget": "3.3.0",
|
|
69
70
|
"jsonc-parser": "^3.3.1",
|
|
70
71
|
"magic-string": "^0.30.21",
|
|
72
|
+
"react": "^19.2.7",
|
|
73
|
+
"react-reconciler": "^0.33.0",
|
|
71
74
|
"ts-morph": "^28.0.0"
|
|
72
75
|
},
|
|
73
76
|
"engines": {
|