libretto 0.6.13 → 0.6.14

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 (55) hide show
  1. package/dist/cli/commands/auth.js +24 -33
  2. package/dist/cli/commands/billing.js +3 -5
  3. package/dist/cli/commands/browser.js +3 -6
  4. package/dist/cli/commands/deploy.js +54 -45
  5. package/dist/cli/commands/execution.js +6 -3
  6. package/dist/cli/commands/experiments.js +1 -1
  7. package/dist/cli/commands/setup.js +1 -1
  8. package/dist/cli/commands/shared.js +1 -1
  9. package/dist/cli/commands/snapshot.js +1 -1
  10. package/dist/cli/commands/status.js +1 -1
  11. package/dist/cli/core/auth-fetch.js +11 -6
  12. package/dist/cli/core/browser.js +10 -5
  13. package/dist/cli/core/daemon/daemon.js +63 -10
  14. package/dist/cli/core/daemon/exec-repl.js +133 -0
  15. package/dist/cli/core/daemon/exec.js +6 -21
  16. package/dist/cli/core/daemon/ipc.js +47 -4
  17. package/dist/cli/core/daemon/ipc.spec.js +21 -0
  18. package/dist/cli/core/exec-compiler.js +8 -3
  19. package/dist/cli/core/providers/index.js +13 -4
  20. package/dist/cli/core/providers/libretto-cloud.js +178 -26
  21. package/dist/cli/router.js +9 -4
  22. package/dist/shared/ipc/socket-transport.d.ts +2 -1
  23. package/dist/shared/ipc/socket-transport.js +16 -5
  24. package/dist/shared/ipc/socket-transport.spec.js +5 -0
  25. package/package.json +2 -2
  26. package/skills/libretto/SKILL.md +33 -29
  27. package/skills/libretto/references/code-generation-rules.md +6 -0
  28. package/skills/libretto/references/configuration-file-reference.md +8 -0
  29. package/skills/libretto/references/site-security-review.md +6 -6
  30. package/skills/libretto-readonly/SKILL.md +1 -1
  31. package/src/cli/commands/auth.ts +24 -33
  32. package/src/cli/commands/billing.ts +3 -5
  33. package/src/cli/commands/browser.ts +5 -9
  34. package/src/cli/commands/deploy.ts +55 -49
  35. package/src/cli/commands/execution.ts +6 -3
  36. package/src/cli/commands/experiments.ts +1 -1
  37. package/src/cli/commands/setup.ts +1 -1
  38. package/src/cli/commands/shared.ts +1 -1
  39. package/src/cli/commands/snapshot.ts +1 -1
  40. package/src/cli/commands/status.ts +1 -1
  41. package/src/cli/core/auth-fetch.ts +9 -4
  42. package/src/cli/core/browser.ts +12 -5
  43. package/src/cli/core/daemon/daemon.ts +81 -9
  44. package/src/cli/core/daemon/exec-repl.ts +189 -0
  45. package/src/cli/core/daemon/exec.ts +8 -43
  46. package/src/cli/core/daemon/ipc.spec.ts +27 -0
  47. package/src/cli/core/daemon/ipc.ts +76 -7
  48. package/src/cli/core/exec-compiler.ts +8 -3
  49. package/src/cli/core/providers/index.ts +17 -4
  50. package/src/cli/core/providers/libretto-cloud.ts +224 -36
  51. package/src/cli/router.ts +9 -4
  52. package/src/shared/ipc/socket-transport.spec.ts +6 -0
  53. package/src/shared/ipc/socket-transport.ts +20 -5
  54. package/dist/cli/framework/simple-cli.js +0 -880
  55. package/src/cli/framework/simple-cli.ts +0 -1459
@@ -1,880 +0,0 @@
1
- import { z } from "zod";
2
- const EXPERIMENTAL_COMMAND_PREFIX = "experimental";
3
- const EXPERIMENTAL_GROUP_DESCRIPTION = "Experimental commands";
4
- function toCamelCase(input2) {
5
- return input2.replace(
6
- /-([a-zA-Z0-9])/g,
7
- (_match, letter) => letter.toUpperCase()
8
- );
9
- }
10
- function toKebabCase(input2) {
11
- return input2.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
12
- }
13
- function zodObjectFromShape(shape) {
14
- return z.object(shape);
15
- }
16
- function schemaAcceptsUndefined(schema) {
17
- return schema.safeParse(void 0).success;
18
- }
19
- function pathToRouteKey(path) {
20
- return path.join(".");
21
- }
22
- function pathStartsWith(path, prefix) {
23
- if (prefix.length > path.length) return false;
24
- return prefix.every((token, index) => path[index] === token);
25
- }
26
- function formatListEntry(label, description) {
27
- return description ? ` ${label} ${description}` : ` ${label}`;
28
- }
29
- function isHelpFlag(arg) {
30
- return arg === "--help" || arg === "-h";
31
- }
32
- function parseBooleanFlagValue(rawValue, flagName) {
33
- if (rawValue === "true") return true;
34
- if (rawValue === "false") return false;
35
- throw new Error(`Invalid value for --${flagName}: expected true or false.`);
36
- }
37
- function buildUsagePositionalToken(positional2) {
38
- return schemaAcceptsUndefined(positional2.schema) ? `[${positional2.key}]` : `<${positional2.key}>`;
39
- }
40
- function buildNamedArgFlagName(key, spec) {
41
- if (spec.source === "--") return "--";
42
- return spec.name ?? toKebabCase(key);
43
- }
44
- function buildNamedArgHelpLabel(key, spec) {
45
- if (spec.source === "--") return "-- <args...>";
46
- const flagName = buildNamedArgFlagName(key, spec);
47
- if (spec.kind === "flag") {
48
- return `--${flagName}`;
49
- }
50
- return `--${flagName} <value>`;
51
- }
52
- function normalizeNamedArgToken(token) {
53
- return token.replace(/^-{1,2}/, "");
54
- }
55
- class SimpleCLIInput {
56
- constructor(normalize, schema, definition) {
57
- this.normalize = normalize;
58
- this.schema = schema;
59
- this.definition = definition;
60
- }
61
- parse(raw) {
62
- try {
63
- return this.schema.parse(this.normalize(raw));
64
- } catch (error) {
65
- if (error instanceof z.ZodError) {
66
- const messages = error.issues.map((issue) => issue.message).filter((message) => message.length > 0);
67
- if (messages.length > 0) {
68
- throw new Error(messages.join("\n"));
69
- }
70
- }
71
- throw error;
72
- }
73
- }
74
- getDefinition() {
75
- return this.definition;
76
- }
77
- refine(check, message) {
78
- const nextSchema = this.schema.refine(
79
- (value) => Boolean(check(value)),
80
- message ? { message } : void 0
81
- );
82
- return new SimpleCLIInput(this.normalize, nextSchema, this.definition);
83
- }
84
- superRefine(check) {
85
- const nextSchema = this.schema.superRefine(check);
86
- return new SimpleCLIInput(this.normalize, nextSchema, this.definition);
87
- }
88
- }
89
- class SimpleCLICommandBuilder {
90
- constructor(definition) {
91
- this.definition = definition;
92
- }
93
- input(input2) {
94
- return new SimpleCLICommandBuilder({
95
- config: this.definition.config,
96
- input: input2,
97
- middlewares: this.definition.middlewares,
98
- handler: this.definition.handler
99
- });
100
- }
101
- use(middleware) {
102
- return new SimpleCLICommandBuilder({
103
- config: this.definition.config,
104
- input: this.definition.input,
105
- middlewares: [...this.definition.middlewares, middleware],
106
- handler: this.definition.handler
107
- });
108
- }
109
- handle(handler) {
110
- return new SimpleCLICommandBuilder({
111
- config: this.definition.config,
112
- input: this.definition.input,
113
- middlewares: this.definition.middlewares,
114
- handler
115
- });
116
- }
117
- getDefinition() {
118
- return this.definition;
119
- }
120
- }
121
- class SimpleCLIApp {
122
- constructor(name, routes, config = {}) {
123
- this.name = name;
124
- const resolution = resolveRouteTree(routes);
125
- this.globalNamed = config.globalNamed ?? {};
126
- for (const group2 of resolution.groups) {
127
- if (this.resolvedGroups.has(group2.routeKey)) {
128
- throw new Error(`Duplicate group route key: ${group2.routeKey}`);
129
- }
130
- this.resolvedGroups.set(group2.routeKey, group2);
131
- }
132
- for (const command2 of resolution.commands) {
133
- if (this.resolvedCommands.has(command2.routeKey)) {
134
- throw new Error(`Duplicate command route key: ${command2.routeKey}`);
135
- }
136
- this.resolvedCommands.set(command2.routeKey, command2);
137
- }
138
- this.routeEntries = resolution.routeEntries;
139
- }
140
- resolvedCommands = /* @__PURE__ */ new Map();
141
- resolvedGroups = /* @__PURE__ */ new Map();
142
- routeEntries;
143
- globalNamed;
144
- getCommands() {
145
- return [...this.resolvedCommands.values()].map((command2) => ({
146
- routeKey: command2.routeKey,
147
- path: command2.path,
148
- description: command2.description
149
- }));
150
- }
151
- async invoke(routeKey, rawInput, initialContext = {}) {
152
- const command2 = this.resolvedCommands.get(routeKey);
153
- if (!command2) {
154
- throw new Error(`Unknown command route key "${routeKey}".`);
155
- }
156
- const input2 = command2.input ? command2.input.parse(rawInput) : rawInput;
157
- let ctx = { ...initialContext };
158
- const meta = {
159
- routeKey: command2.routeKey,
160
- path: command2.path,
161
- description: command2.description
162
- };
163
- for (const middleware of command2.middlewares) {
164
- const next = await middleware({ input: input2, ctx, command: meta });
165
- if (next !== void 0) {
166
- ctx = next;
167
- }
168
- }
169
- return command2.handler({ input: input2, ctx, command: meta });
170
- }
171
- async run(args) {
172
- const extractedGlobalArgs = this.extractGlobalArgs(args);
173
- const normalizedArgs = extractedGlobalArgs.args;
174
- const helpPath = this.resolveHelpPath(normalizedArgs);
175
- if (helpPath) {
176
- return this.renderHelp(helpPath);
177
- }
178
- const exactGroup = this.findGroupByPath(normalizedArgs);
179
- if (exactGroup) {
180
- return this.renderGroupHelp(exactGroup);
181
- }
182
- const parsed = this.parseInvocation(normalizedArgs);
183
- return this.invoke(
184
- parsed.routeKey,
185
- this.injectGlobalNamedArgs(
186
- parsed.routeKey,
187
- parsed.rawInput,
188
- extractedGlobalArgs.named
189
- )
190
- );
191
- }
192
- renderHelp(path = []) {
193
- if (path.length === 0) {
194
- return this.renderRootHelp();
195
- }
196
- const group2 = this.findGroupByPath(path);
197
- if (group2) {
198
- return this.renderGroupHelp(group2);
199
- }
200
- const command2 = this.findCommandByPath(path);
201
- if (command2) {
202
- return this.renderCommandHelp(command2);
203
- }
204
- throw new Error(`Unknown help topic "${path.join(" ")}".`);
205
- }
206
- resolveHelpPath(args) {
207
- if (args.length === 0) return [];
208
- if (args[0] === "help") {
209
- return args.slice(1);
210
- }
211
- const passthroughIndex = args.indexOf("--");
212
- const argsBeforePassthrough = passthroughIndex >= 0 ? args.slice(0, passthroughIndex) : args;
213
- if (argsBeforePassthrough.length === 0) {
214
- return null;
215
- }
216
- if (isHelpFlag(argsBeforePassthrough[0])) {
217
- return [];
218
- }
219
- const helpFlagIndex = argsBeforePassthrough.findIndex(
220
- (arg) => isHelpFlag(arg)
221
- );
222
- if (helpFlagIndex >= 0) {
223
- return argsBeforePassthrough.slice(0, helpFlagIndex);
224
- }
225
- return null;
226
- }
227
- parseInvocation(args) {
228
- const command2 = this.findBestMatchingCommand(args);
229
- if (!command2) {
230
- const exactGroup = this.findGroupByPath(args);
231
- if (exactGroup) {
232
- throw new Error(this.renderGroupHelp(exactGroup));
233
- }
234
- throw new Error(`Unknown command: ${args.join(" ")}`);
235
- }
236
- const rawInput = this.parseCommandInput(
237
- command2,
238
- args.slice(command2.path.length)
239
- );
240
- return {
241
- routeKey: command2.routeKey,
242
- rawInput
243
- };
244
- }
245
- parseCommandInput(command2, args) {
246
- const inputDefinition = command2.input?.getDefinition();
247
- if (!inputDefinition) {
248
- if (args.length > 0) {
249
- throw new Error(
250
- `Unexpected arguments for ${this.name} ${command2.path.join(" ")}.`
251
- );
252
- }
253
- return {
254
- positionals: [],
255
- named: {}
256
- };
257
- }
258
- const positionals = [];
259
- const named = {};
260
- const namedSpecs = buildNamedArgLookup(inputDefinition.named);
261
- const passthroughEntry = Object.entries(inputDefinition.named).find(
262
- ([, spec]) => spec.source === "--"
263
- );
264
- for (let index = 0; index < args.length; index++) {
265
- const arg = args[index];
266
- if (arg === "--") {
267
- if (!passthroughEntry) {
268
- throw new Error(
269
- `Unexpected "--" for ${this.name} ${command2.path.join(" ")}.`
270
- );
271
- }
272
- named["--"] = args.slice(index + 1);
273
- break;
274
- }
275
- if (arg === "-") {
276
- positionals.push(arg);
277
- continue;
278
- }
279
- if (arg.startsWith("--")) {
280
- const [rawName, inlineValue] = splitNamedArg(arg.slice(2));
281
- const namedEntry = namedSpecs.get(rawName);
282
- if (!namedEntry) {
283
- throw new Error(`Unknown option: --${rawName}`);
284
- }
285
- const storeKey = buildNamedArgFlagName(namedEntry.key, namedEntry.spec);
286
- named[storeKey] = readNamedArgValue(
287
- args,
288
- index,
289
- rawName,
290
- `--${rawName}`,
291
- namedEntry.spec,
292
- inlineValue,
293
- namedSpecs
294
- );
295
- if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
296
- index += 1;
297
- }
298
- continue;
299
- }
300
- if (arg.startsWith("-")) {
301
- const [rawName, inlineValue] = splitNamedArg(arg.slice(1));
302
- const namedEntry = namedSpecs.get(rawName);
303
- if (!namedEntry) {
304
- throw new Error(`Unknown option: ${arg}`);
305
- }
306
- const storeKey = buildNamedArgFlagName(namedEntry.key, namedEntry.spec);
307
- named[storeKey] = readNamedArgValue(
308
- args,
309
- index,
310
- rawName,
311
- `-${rawName}`,
312
- namedEntry.spec,
313
- inlineValue,
314
- namedSpecs
315
- );
316
- if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
317
- index += 1;
318
- }
319
- continue;
320
- }
321
- positionals.push(arg);
322
- }
323
- validateParsedPositionals(
324
- command2,
325
- inputDefinition.positionals,
326
- positionals
327
- );
328
- validateRequiredNamedArgs(inputDefinition.named, named);
329
- return {
330
- positionals,
331
- named
332
- };
333
- }
334
- extractGlobalArgs(args) {
335
- if (Object.keys(this.globalNamed).length === 0) {
336
- return {
337
- args,
338
- named: {}
339
- };
340
- }
341
- const remainingArgs = [];
342
- const named = {};
343
- const namedSpecs = buildNamedArgLookup(this.globalNamed);
344
- for (let index = 0; index < args.length; index += 1) {
345
- const arg = args[index];
346
- if (arg === "--") {
347
- remainingArgs.push(...args.slice(index));
348
- break;
349
- }
350
- if (arg.startsWith("--")) {
351
- const [rawName, inlineValue] = splitNamedArg(arg.slice(2));
352
- const namedEntry = namedSpecs.get(rawName);
353
- if (!namedEntry) {
354
- remainingArgs.push(arg);
355
- continue;
356
- }
357
- named[namedEntry.key] = readNamedArgValue(
358
- args,
359
- index,
360
- rawName,
361
- `--${rawName}`,
362
- namedEntry.spec,
363
- inlineValue,
364
- namedSpecs
365
- );
366
- if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
367
- index += 1;
368
- }
369
- continue;
370
- }
371
- if (arg.startsWith("-")) {
372
- const [rawName, inlineValue] = splitNamedArg(arg.slice(1));
373
- const namedEntry = namedSpecs.get(rawName);
374
- if (!namedEntry) {
375
- remainingArgs.push(arg);
376
- continue;
377
- }
378
- named[namedEntry.key] = readNamedArgValue(
379
- args,
380
- index,
381
- rawName,
382
- `-${rawName}`,
383
- namedEntry.spec,
384
- inlineValue,
385
- namedSpecs
386
- );
387
- if (inlineValue === void 0 && namedEntry.spec.kind !== "flag") {
388
- index += 1;
389
- }
390
- continue;
391
- }
392
- remainingArgs.push(arg);
393
- }
394
- return {
395
- args: remainingArgs,
396
- named
397
- };
398
- }
399
- injectGlobalNamedArgs(routeKey, rawInput, globalNamed) {
400
- if (Object.keys(globalNamed).length === 0) {
401
- return rawInput;
402
- }
403
- const inputDefinition = this.resolvedCommands.get(routeKey)?.input?.getDefinition();
404
- if (!inputDefinition) {
405
- return rawInput;
406
- }
407
- const named = { ...rawInput.named ?? {} };
408
- let changed = false;
409
- for (const key of Object.keys(inputDefinition.named)) {
410
- if (Object.prototype.hasOwnProperty.call(named, key)) continue;
411
- if (!Object.prototype.hasOwnProperty.call(globalNamed, key)) continue;
412
- named[key] = globalNamed[key];
413
- changed = true;
414
- }
415
- if (!changed) {
416
- return rawInput;
417
- }
418
- return {
419
- positionals: rawInput.positionals ?? [],
420
- named
421
- };
422
- }
423
- renderRootHelp() {
424
- const lines = [`Usage: ${this.name} <command>`, "", "Commands:"];
425
- for (const entry of this.getRootHelpEntries()) {
426
- lines.push(formatListEntry(entry.label, entry.description));
427
- }
428
- return lines.join("\n");
429
- }
430
- renderGroupHelp(group2) {
431
- const lines = [];
432
- if (group2.description) {
433
- lines.push(group2.description, "");
434
- }
435
- lines.push(`Usage: ${this.name} ${group2.path.join(" ")} <subcommand>`);
436
- lines.push("", "Commands:");
437
- for (const entry of this.getImmediateRouteEntries(group2.path)) {
438
- lines.push(formatListEntry(entry.label, entry.description));
439
- }
440
- return lines.join("\n");
441
- }
442
- renderCommandHelp(command2) {
443
- const lines = [
444
- command2.description,
445
- "",
446
- `Usage: ${this.buildCommandUsage(command2)}`
447
- ];
448
- const inputDefinition = command2.input?.getDefinition();
449
- if (!inputDefinition) {
450
- return lines.join("\n");
451
- }
452
- const argumentLines = inputDefinition.positionals.map(
453
- (positional2) => formatListEntry(buildUsagePositionalToken(positional2), positional2.help)
454
- );
455
- if (argumentLines.length > 0) {
456
- lines.push("", "Arguments:");
457
- lines.push(...argumentLines);
458
- }
459
- const optionLines = Object.entries(inputDefinition.named).map(
460
- ([key, spec]) => formatListEntry(buildNamedArgHelpLabel(key, spec), spec.help)
461
- );
462
- if (optionLines.length > 0) {
463
- lines.push("", "Options:");
464
- lines.push(...optionLines);
465
- }
466
- return lines.join("\n");
467
- }
468
- buildCommandUsage(command2) {
469
- const tokens = [this.name, ...command2.path];
470
- const inputDefinition = command2.input?.getDefinition();
471
- if (!inputDefinition) {
472
- return tokens.join(" ");
473
- }
474
- tokens.push(...inputDefinition.positionals.map(buildUsagePositionalToken));
475
- if (Object.keys(inputDefinition.named).length > 0) {
476
- tokens.push("[options]");
477
- }
478
- return tokens.join(" ");
479
- }
480
- getImmediateRouteEntries(path) {
481
- const seen = /* @__PURE__ */ new Set();
482
- const entries = [];
483
- for (const routeEntry of this.routeEntries) {
484
- if (!pathStartsWith(routeEntry.path, path)) continue;
485
- if (routeEntry.path.length !== path.length + 1) continue;
486
- const token = routeEntry.path[path.length];
487
- if (seen.has(token)) continue;
488
- seen.add(token);
489
- if (routeEntry.kind === "group") {
490
- const group2 = this.findGroupByPath(routeEntry.path);
491
- entries.push({
492
- label: `${token} <subcommand>`,
493
- description: group2?.description
494
- });
495
- continue;
496
- }
497
- const command2 = this.findCommandByPath(routeEntry.path);
498
- entries.push({
499
- label: token,
500
- description: command2?.description
501
- });
502
- }
503
- return entries;
504
- }
505
- getRootHelpEntries() {
506
- return this.getImmediateRouteEntries([]).filter((entry) => {
507
- const token = entry.label.replace(/\s+<subcommand>$/, "");
508
- const group2 = this.findGroupByPath([token]);
509
- if (!group2) {
510
- return true;
511
- }
512
- if (token === EXPERIMENTAL_COMMAND_PREFIX) {
513
- return this.groupHasExperimentalCommand(group2.path);
514
- }
515
- return this.groupHasVisibleNonExperimentalCommand(group2.path);
516
- });
517
- }
518
- groupHasVisibleNonExperimentalCommand(path) {
519
- for (const routeEntry of this.routeEntries) {
520
- if (routeEntry.kind !== "command") continue;
521
- if (!pathStartsWith(routeEntry.path, path)) continue;
522
- const command2 = this.findCommandByPath(routeEntry.path);
523
- if (command2 && !command2.experimental) {
524
- return true;
525
- }
526
- }
527
- return false;
528
- }
529
- groupHasExperimentalCommand(path) {
530
- for (const routeEntry of this.routeEntries) {
531
- if (routeEntry.kind !== "command") continue;
532
- if (!pathStartsWith(routeEntry.path, path)) continue;
533
- const command2 = this.findCommandByPath(routeEntry.path);
534
- if (command2?.experimental) {
535
- return true;
536
- }
537
- }
538
- return false;
539
- }
540
- findBestMatchingCommand(args) {
541
- let bestMatch = null;
542
- for (const command2 of this.resolvedCommands.values()) {
543
- if (command2.path.length > args.length) continue;
544
- if (!pathStartsWith(args, command2.path)) continue;
545
- if (!bestMatch || command2.path.length > bestMatch.path.length) {
546
- bestMatch = command2;
547
- }
548
- }
549
- return bestMatch;
550
- }
551
- findCommandByPath(path) {
552
- const routeKey = pathToRouteKey(path);
553
- return this.resolvedCommands.get(routeKey) ?? null;
554
- }
555
- findGroupByPath(path) {
556
- const routeKey = pathToRouteKey(path);
557
- return this.resolvedGroups.get(routeKey) ?? null;
558
- }
559
- }
560
- function splitNamedArg(arg) {
561
- const separatorIndex = arg.indexOf("=");
562
- if (separatorIndex < 0) return [arg, void 0];
563
- return [arg.slice(0, separatorIndex), arg.slice(separatorIndex + 1)];
564
- }
565
- function readNamedArgValue(args, index, rawName, displayName, spec, inlineValue, namedSpecs) {
566
- if (spec.kind === "flag") {
567
- return inlineValue === void 0 ? true : parseBooleanFlagValue(inlineValue, rawName);
568
- }
569
- if (inlineValue !== void 0) {
570
- return inlineValue;
571
- }
572
- const nextValue = args[index + 1];
573
- if (nextValue === void 0 || nextValue === "--" || isRecognizedNamedArgToken(nextValue, namedSpecs)) {
574
- throw new Error(`Missing value for ${displayName}.`);
575
- }
576
- return nextValue;
577
- }
578
- function isRecognizedNamedArgToken(token, namedSpecs) {
579
- if (token === "-" || !token.startsWith("-")) {
580
- return false;
581
- }
582
- const normalizedToken = token.startsWith("--") ? token.slice(2) : token.slice(1);
583
- const [rawName] = splitNamedArg(normalizedToken);
584
- return namedSpecs.has(rawName);
585
- }
586
- function buildNamedArgLookup(namedDefinition) {
587
- const lookup = /* @__PURE__ */ new Map();
588
- for (const [key, spec] of Object.entries(namedDefinition)) {
589
- if (spec.source === "--") continue;
590
- const flagName = buildNamedArgFlagName(key, spec);
591
- lookup.set(flagName, { key, spec });
592
- lookup.set(key, { key, spec });
593
- lookup.set(toCamelCase(flagName), { key, spec });
594
- for (const alias of spec.aliases ?? []) {
595
- const normalizedAlias = normalizeNamedArgToken(alias);
596
- lookup.set(normalizedAlias, { key, spec });
597
- lookup.set(toCamelCase(normalizedAlias), { key, spec });
598
- }
599
- }
600
- return lookup;
601
- }
602
- function validateParsedPositionals(command2, definitions, positionals) {
603
- const variadicDefinition = definitions.find(
604
- (definition) => definition.variadic
605
- );
606
- if (!variadicDefinition && positionals.length > definitions.length) {
607
- throw new Error(`Unexpected arguments for ${command2.path.join(" ")}.`);
608
- }
609
- definitions.forEach((definition, index) => {
610
- const value = definition.variadic ? positionals.slice(index) : positionals[index];
611
- if (value !== void 0 && (!Array.isArray(value) || value.length > 0))
612
- return;
613
- if (schemaAcceptsUndefined(definition.schema)) return;
614
- throw new Error(`Missing required argument <${definition.key}>.`);
615
- });
616
- }
617
- function validateInputDefinition(definition) {
618
- const variadicIndex = definition.positionals.findIndex(
619
- (positional2) => positional2.variadic
620
- );
621
- if (variadicIndex < 0) return;
622
- if (variadicIndex !== definition.positionals.length - 1) {
623
- throw new Error(
624
- "Variadic positional arguments must be the last positional."
625
- );
626
- }
627
- }
628
- function validateRequiredNamedArgs(definitions, named) {
629
- for (const [key, spec] of Object.entries(definitions)) {
630
- if (schemaAcceptsUndefined(spec.schema)) continue;
631
- const flagName = spec.source === "--" ? "--" : buildNamedArgFlagName(key, spec);
632
- if (Object.prototype.hasOwnProperty.call(named, flagName)) continue;
633
- if (spec.source === "--") {
634
- throw new Error(`Missing required passthrough arguments after --.`);
635
- }
636
- throw new Error(`Missing required option --${flagName}.`);
637
- }
638
- }
639
- function resolveRouteTree(routes) {
640
- const collected = collectRouteTree(routes);
641
- const { groups, routeEntries } = buildResolvedRouteEntries(
642
- collected.commands,
643
- collected.groupDescriptions
644
- );
645
- return {
646
- commands: collected.commands,
647
- groups,
648
- routeEntries
649
- };
650
- }
651
- function collectRouteTree(routes, parentPath = [], parentMiddlewares = []) {
652
- const resolved = {
653
- commands: [],
654
- groupDescriptions: /* @__PURE__ */ new Map()
655
- };
656
- for (const [token, routeValue] of Object.entries(routes)) {
657
- if (isGroup(routeValue)) {
658
- const groupPath = [...parentPath, token];
659
- resolved.groupDescriptions.set(
660
- pathToRouteKey(groupPath),
661
- routeValue.description
662
- );
663
- const nested = collectRouteTree(routeValue.routes, groupPath, [
664
- ...parentMiddlewares,
665
- ...routeValue.middlewares
666
- ]);
667
- resolved.commands.push(...nested.commands);
668
- for (const [routeKey, description] of nested.groupDescriptions) {
669
- resolved.groupDescriptions.set(routeKey, description);
670
- }
671
- continue;
672
- }
673
- const command2 = routeValue.getDefinition();
674
- if (!command2.handler) {
675
- throw new Error(
676
- `Command "${[...parentPath, token].join(" ")}" is missing a handler.`
677
- );
678
- }
679
- const rawPath = [...parentPath, token];
680
- const path = command2.config.experimental ? [EXPERIMENTAL_COMMAND_PREFIX, ...rawPath] : rawPath;
681
- resolved.commands.push({
682
- routeKey: pathToRouteKey(path),
683
- path,
684
- description: command2.config.description,
685
- experimental: command2.config.experimental,
686
- input: command2.input,
687
- middlewares: mergeInheritedMiddlewares(
688
- parentMiddlewares,
689
- command2.middlewares
690
- ),
691
- handler: command2.handler
692
- });
693
- }
694
- return resolved;
695
- }
696
- function buildResolvedRouteEntries(commands, groupDescriptions) {
697
- const groups = /* @__PURE__ */ new Map();
698
- const routeEntries = [];
699
- for (const command2 of commands) {
700
- for (let depth = 1; depth < command2.path.length; depth += 1) {
701
- const path = command2.path.slice(0, depth);
702
- const routeKey = pathToRouteKey(path);
703
- if (groups.has(routeKey)) continue;
704
- groups.set(routeKey, {
705
- routeKey,
706
- path,
707
- description: resolveGroupDescription(path, groupDescriptions)
708
- });
709
- routeEntries.push({
710
- kind: "group",
711
- path
712
- });
713
- }
714
- routeEntries.push({
715
- kind: "command",
716
- path: command2.path
717
- });
718
- }
719
- return {
720
- groups: [...groups.values()],
721
- routeEntries
722
- };
723
- }
724
- function resolveGroupDescription(path, groupDescriptions) {
725
- if (path.length === 1 && path[0] === EXPERIMENTAL_COMMAND_PREFIX) {
726
- return EXPERIMENTAL_GROUP_DESCRIPTION;
727
- }
728
- const originalPath = path[0] === EXPERIMENTAL_COMMAND_PREFIX ? path.slice(1) : path;
729
- return groupDescriptions.get(pathToRouteKey(originalPath));
730
- }
731
- function mergeInheritedMiddlewares(parentMiddlewares, commandMiddlewares) {
732
- if (parentMiddlewares.length === 0) {
733
- return [...commandMiddlewares];
734
- }
735
- if (commandMiddlewares.length >= parentMiddlewares.length && parentMiddlewares.every(
736
- (middleware, index) => commandMiddlewares[index] === middleware
737
- )) {
738
- return [...commandMiddlewares];
739
- }
740
- return [...parentMiddlewares, ...commandMiddlewares];
741
- }
742
- function isGroup(value) {
743
- return value.kind === "group";
744
- }
745
- function buildInputNormalizer(definition) {
746
- return (raw) => {
747
- const output = {};
748
- const positionals = raw.positionals ?? [];
749
- const named = raw.named ?? {};
750
- definition.positionals.forEach((positional2, index) => {
751
- output[positional2.key] = positional2.variadic ? positionals.slice(index) : positionals[index];
752
- });
753
- for (const [key, spec] of Object.entries(definition.named)) {
754
- const sourceKey = spec.source === "--" ? "--" : spec.name ?? key;
755
- const normalizedCandidates = [
756
- sourceKey,
757
- spec.name ? toCamelCase(spec.name) : "",
758
- ...(spec.aliases ?? []).flatMap((alias) => {
759
- const normalizedAlias = normalizeNamedArgToken(alias);
760
- return [normalizedAlias, toCamelCase(normalizedAlias)];
761
- }),
762
- toKebabCase(key),
763
- key
764
- ].filter((candidate) => candidate.length > 0);
765
- let found = false;
766
- for (const candidate of normalizedCandidates) {
767
- if (Object.prototype.hasOwnProperty.call(named, candidate)) {
768
- output[key] = named[candidate];
769
- found = true;
770
- break;
771
- }
772
- }
773
- if (!found && !Object.prototype.hasOwnProperty.call(output, key)) {
774
- output[key] = void 0;
775
- }
776
- }
777
- return output;
778
- };
779
- }
780
- function buildInputSchema(definition) {
781
- const shape = {};
782
- for (const positional2 of definition.positionals) {
783
- shape[positional2.key] = positional2.schema;
784
- }
785
- for (const [key, named] of Object.entries(definition.named)) {
786
- shape[key] = named.schema;
787
- }
788
- return zodObjectFromShape(shape);
789
- }
790
- function positional(key, schema, options) {
791
- return {
792
- kind: "positional",
793
- key,
794
- schema,
795
- help: options?.help,
796
- variadic: options?.variadic
797
- };
798
- }
799
- function option(schema, options) {
800
- return {
801
- kind: "option",
802
- schema,
803
- help: options?.help,
804
- name: options?.name,
805
- aliases: options?.aliases,
806
- source: options?.source
807
- };
808
- }
809
- function flag(options) {
810
- return {
811
- kind: "flag",
812
- schema: z.boolean().default(false),
813
- help: options?.help,
814
- name: options?.name,
815
- aliases: options?.aliases
816
- };
817
- }
818
- function input(definition) {
819
- validateInputDefinition(definition);
820
- return new SimpleCLIInput(
821
- buildInputNormalizer(definition),
822
- buildInputSchema(definition),
823
- definition
824
- );
825
- }
826
- function command(config) {
827
- return new SimpleCLICommandBuilder({
828
- config,
829
- middlewares: []
830
- });
831
- }
832
- function group(config) {
833
- return createScope([]).group(config);
834
- }
835
- function createScope(middlewares) {
836
- return {
837
- use(middleware) {
838
- return createScope([
839
- ...middlewares,
840
- middleware
841
- ]);
842
- },
843
- group(config) {
844
- return {
845
- kind: "group",
846
- description: config.description,
847
- routes: config.routes,
848
- middlewares: [...middlewares]
849
- };
850
- },
851
- command(config) {
852
- return new SimpleCLICommandBuilder({
853
- config,
854
- middlewares: [...middlewares]
855
- });
856
- }
857
- };
858
- }
859
- function use(middleware) {
860
- return createScope([middleware]);
861
- }
862
- function define(name, routes, config) {
863
- return new SimpleCLIApp(name, routes, config);
864
- }
865
- const SimpleCLI = {
866
- define,
867
- command,
868
- group,
869
- use,
870
- input,
871
- positional,
872
- option,
873
- flag
874
- };
875
- export {
876
- SimpleCLI,
877
- SimpleCLIApp,
878
- SimpleCLICommandBuilder,
879
- SimpleCLIInput
880
- };