@reliverse/rempts 1.7.30 → 1.7.32
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/README.md +49 -0
- package/bin/libs/launcher/launcher-mod.d.ts +39 -9
- package/bin/libs/launcher/launcher-mod.js +250 -166
- package/bin/libs/launcher/launcher-types.d.ts +11 -0
- package/bin/mod.d.ts +1 -1
- package/bin/mod.js +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
- 🏞️ no more hacking together `inquirer`/`citty`/`commander`/`chalk`
|
|
29
29
|
- 🆕 automatic command creation (`bun dler rempts --init cmd1 cmd2`)
|
|
30
30
|
- 🐦🔥 automatic creation of `src/app/cmds.ts` file (`bun dler rempts`)
|
|
31
|
+
- 🔌 tRPC/ORPC router integration - automatically generate CLI commands from your RPC procedures
|
|
31
32
|
|
|
32
33
|
## Installation
|
|
33
34
|
|
|
@@ -364,6 +365,48 @@ See [example/launcher/app/nested](./example/launcher/app/nested/) and [example/l
|
|
|
364
365
|
|
|
365
366
|
When playing with the example, you can run e.g. `bun dev:modern nested foo bar baz` to see the result in action.
|
|
366
367
|
|
|
368
|
+
## RPC Integration
|
|
369
|
+
|
|
370
|
+
Rempts now supports seamless integration with tRPC and ORPC routers, allowing you to automatically generate CLI commands from your RPC procedures. This provides a powerful way to expose your API endpoints as command-line tools.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
import { z } from "zod";
|
|
374
|
+
import { initTRPC } from "@trpc/server";
|
|
375
|
+
import { createCli } from "@reliverse/rempts";
|
|
376
|
+
|
|
377
|
+
const t = initTRPC.create();
|
|
378
|
+
|
|
379
|
+
const appRouter = t.router({
|
|
380
|
+
hello: t.procedure
|
|
381
|
+
.input(z.object({ name: z.string().optional() }))
|
|
382
|
+
.query(({ input }) => `Hello ${input.name ?? "World"}!`),
|
|
383
|
+
|
|
384
|
+
add: t.procedure
|
|
385
|
+
.input(z.object({ a: z.number(), b: z.number() }))
|
|
386
|
+
.mutation(({ input }) => input.a + input.b)
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Automatically generates CLI commands from your tRPC procedures
|
|
390
|
+
await createCli({
|
|
391
|
+
name: "my-cli",
|
|
392
|
+
rpc: { router: appRouter }
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**Features:**
|
|
397
|
+
|
|
398
|
+
- 🚀 Automatic CLI generation from tRPC procedures
|
|
399
|
+
- 🔄 Support for both tRPC v10 and v11
|
|
400
|
+
- 🏗️ Nested command structures from sub-routers
|
|
401
|
+
- ✅ Input validation from Zod schemas
|
|
402
|
+
- 📖 Automatic help generation from procedure metadata
|
|
403
|
+
- 🎯 Full TypeScript support with type inference
|
|
404
|
+
- 🎨 Interactive prompts for missing arguments
|
|
405
|
+
- ⌨️ Shell completion support
|
|
406
|
+
- 🔧 Customizable logging and error handling
|
|
407
|
+
|
|
408
|
+
See [RPC Integration Guide](./docs/launcher-rpc.md) for detailed documentation and examples.
|
|
409
|
+
|
|
367
410
|
### Playground
|
|
368
411
|
|
|
369
412
|
```bash
|
|
@@ -377,6 +420,12 @@ bun dev
|
|
|
377
420
|
- `bun dev:modern`: This example will show you a modern CLI launcher usage with file-based commands.
|
|
378
421
|
- `bun dev:classic`: This example will show you a classic CLI launcher usage with programmatic commands.
|
|
379
422
|
|
|
423
|
+
### tRPC/oRPC Integration Example Commands
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
bun example/trpc-orpc/rempts/effect-primary.ts create-profile --name 'Jane Smith' --age 28 --bio 'Software Engineer' --tags 'developer,typescript'
|
|
427
|
+
```
|
|
428
|
+
|
|
380
429
|
### Launcher Usage Examples
|
|
381
430
|
|
|
382
431
|
#### Minimal Usage Example
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ReliArgParserOptions } from "@reliverse/reliarg";
|
|
2
2
|
import type { ArgDefinitions, Command, DefineCommandOptions, EmptyArgs, FileBasedOptions } from "./launcher-types.js";
|
|
3
|
+
import type { AnyRouter } from "./trpc-orpc-support/trpc-compat.js";
|
|
4
|
+
import type { Logger, OmeletteInstanceLike, Promptable } from "./trpc-orpc-support/types.js";
|
|
3
5
|
/**
|
|
4
6
|
* Defines a command with metadata, argument definitions,
|
|
5
7
|
* an execution function, and (optional) subCommands.
|
|
@@ -28,6 +30,7 @@ export declare function showUsage<A extends ArgDefinitions>(command: Command<A>,
|
|
|
28
30
|
* - File-based Commands: scanning for commands within a given commands root.
|
|
29
31
|
* - Commands defined within the command object.
|
|
30
32
|
* - Standard flags like --help, --version, and --debug.
|
|
33
|
+
* - RPC functionality: tRPC/oRPC router integration with automatic CLI generation.
|
|
31
34
|
*
|
|
32
35
|
* This function passes along remaining arguments to command runners to ensure
|
|
33
36
|
* consistent parsing.
|
|
@@ -50,21 +53,48 @@ export declare function createCli<A extends ArgDefinitions = EmptyArgs>(options:
|
|
|
50
53
|
onCmdExit?: Command<A>["onCmdExit"];
|
|
51
54
|
onLauncherInit?: Command<A>["onLauncherInit"];
|
|
52
55
|
onLauncherExit?: Command<A>["onLauncherExit"];
|
|
56
|
+
rpc?: {
|
|
57
|
+
/** A tRPC router. Procedures will become CLI commands. */
|
|
58
|
+
router: AnyRouter;
|
|
59
|
+
/** Context to be supplied when invoking the router. */
|
|
60
|
+
context?: any;
|
|
61
|
+
/** The `@trpc/server` module to use for calling procedures. Required when using trpc v10. */
|
|
62
|
+
trpcServer?: any | Promise<any>;
|
|
63
|
+
/** Usage code examples to display in `--help` output. */
|
|
64
|
+
usage?: string | string[];
|
|
65
|
+
/** Dependencies for schema validation libraries */
|
|
66
|
+
"@valibot/to-json-schema"?: {
|
|
67
|
+
toJsonSchema: (input: unknown, options?: {
|
|
68
|
+
errorMode?: "throw" | "ignore" | "warn";
|
|
69
|
+
}) => any;
|
|
70
|
+
};
|
|
71
|
+
effect?: {
|
|
72
|
+
Schema: {
|
|
73
|
+
isSchema: (input: unknown) => input is "JSONSchemaMakeable";
|
|
74
|
+
};
|
|
75
|
+
JSONSchema: {
|
|
76
|
+
make: (input: "JSONSchemaMakeable") => any;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
rpcRunParams?: {
|
|
81
|
+
argv?: string[];
|
|
82
|
+
logger?: Logger;
|
|
83
|
+
completion?: OmeletteInstanceLike | (() => Promise<OmeletteInstanceLike>);
|
|
84
|
+
prompts?: Promptable;
|
|
85
|
+
/** Format an error thrown by the root procedure before logging to `logger.error` */
|
|
86
|
+
formatError?: (error: unknown) => string;
|
|
87
|
+
process?: {
|
|
88
|
+
exit: (code: number) => never;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
53
91
|
}, legacyParserOptions?: ReliArgParserOptions & {
|
|
54
92
|
fileBased?: FileBasedOptions;
|
|
55
93
|
autoExit?: boolean;
|
|
56
94
|
metaSettings?: {
|
|
57
95
|
showDescriptionOnMain?: boolean;
|
|
58
96
|
};
|
|
59
|
-
}): {
|
|
60
|
-
/**
|
|
61
|
-
* @deprecated Use createCli() directly instead. This method will be removed in a future version.
|
|
62
|
-
* @example
|
|
63
|
-
* // Instead of:
|
|
64
|
-
* createCli({...}).run()
|
|
65
|
-
* // Use:
|
|
66
|
-
* await createCli({...})
|
|
67
|
-
*/
|
|
97
|
+
}): Promise<void> & {
|
|
68
98
|
run(_ctx?: any): Promise<void>;
|
|
69
99
|
};
|
|
70
100
|
/**
|
|
@@ -5,6 +5,7 @@ import fs from "@reliverse/relifso";
|
|
|
5
5
|
import { relinka, relinkaConfig, relinkaShutdown } from "@reliverse/relinka";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { readPackageJSON } from "pkg-types";
|
|
8
|
+
import { createRpcCli } from "./trpc-orpc-support/index.js";
|
|
8
9
|
function buildExampleArgs(args) {
|
|
9
10
|
const parts = [];
|
|
10
11
|
const positionalKeys = Object.keys(args || {}).filter(
|
|
@@ -71,6 +72,7 @@ export function defineCommand(options) {
|
|
|
71
72
|
onCmdExit,
|
|
72
73
|
onLauncherInit,
|
|
73
74
|
onLauncherExit,
|
|
75
|
+
router: options.router,
|
|
74
76
|
// Backward-compatible aliases
|
|
75
77
|
setup: onCmdInit,
|
|
76
78
|
cleanup: onCmdExit
|
|
@@ -379,95 +381,220 @@ export function createCli(options, legacyParserOptions) {
|
|
|
379
381
|
meta: mergedMeta
|
|
380
382
|
};
|
|
381
383
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
384
|
+
const execute = async (_ctx) => {
|
|
385
|
+
if (options && typeof options === "object" && "rpc" in options && options.rpc) {
|
|
386
|
+
const rpcOptions = options.rpc;
|
|
387
|
+
const rpcRunParams = options.rpcRunParams || {};
|
|
388
|
+
debugLog("RPC integration detected, creating RPC CLI...");
|
|
389
|
+
try {
|
|
390
|
+
require("tsx/cjs");
|
|
391
|
+
await import("tsx/esm");
|
|
392
|
+
debugLog("tsx loaded successfully for TypeScript support");
|
|
393
|
+
} catch {
|
|
394
|
+
debugLog("tsx not available, continuing without TypeScript support");
|
|
395
|
+
}
|
|
396
|
+
const getRouterMeta = (router) => {
|
|
397
|
+
if ("_def" in router && router._def && "meta" in router._def) {
|
|
398
|
+
return router._def.meta;
|
|
399
|
+
}
|
|
400
|
+
return void 0;
|
|
401
|
+
};
|
|
402
|
+
const routerMeta = getRouterMeta(rpcOptions.router);
|
|
403
|
+
const rpcCli = createRpcCli({
|
|
404
|
+
router: rpcOptions.router,
|
|
405
|
+
name: globalCliMeta.name || routerMeta?.name,
|
|
406
|
+
version: globalCliMeta.version || routerMeta?.version,
|
|
407
|
+
description: globalCliMeta.description || routerMeta?.description,
|
|
408
|
+
usage: rpcOptions.usage,
|
|
409
|
+
context: rpcOptions.context,
|
|
410
|
+
trpcServer: rpcOptions.trpcServer,
|
|
411
|
+
"@valibot/to-json-schema": rpcOptions["@valibot/to-json-schema"],
|
|
412
|
+
effect: rpcOptions.effect
|
|
413
|
+
});
|
|
414
|
+
debugLog(
|
|
415
|
+
"RPC CLI created, running with argv:",
|
|
416
|
+
rpcRunParams.argv || process.argv.slice(2)
|
|
395
417
|
);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
418
|
+
await rpcCli.run({
|
|
419
|
+
argv: rpcRunParams.argv || process.argv.slice(2),
|
|
420
|
+
logger: rpcRunParams.logger || {
|
|
421
|
+
info: console.log,
|
|
422
|
+
error: console.error
|
|
423
|
+
},
|
|
424
|
+
completion: rpcRunParams.completion,
|
|
425
|
+
prompts: rpcRunParams.prompts,
|
|
426
|
+
formatError: rpcRunParams.formatError,
|
|
427
|
+
process: rpcRunParams.process
|
|
428
|
+
});
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (command.router) {
|
|
432
|
+
debugLog(
|
|
433
|
+
"Router detected in command, automatically enabling RPC mode..."
|
|
434
|
+
);
|
|
435
|
+
try {
|
|
436
|
+
require("tsx/cjs");
|
|
437
|
+
await import("tsx/esm");
|
|
438
|
+
debugLog("tsx loaded successfully for TypeScript support");
|
|
439
|
+
} catch {
|
|
440
|
+
debugLog("tsx not available, continuing without TypeScript support");
|
|
441
|
+
}
|
|
442
|
+
const getRouterMeta = (router) => {
|
|
443
|
+
if ("_def" in router && router._def && "meta" in router._def) {
|
|
444
|
+
return router._def.meta;
|
|
445
|
+
}
|
|
446
|
+
return void 0;
|
|
447
|
+
};
|
|
448
|
+
const routerMeta = getRouterMeta(command.router);
|
|
449
|
+
const rpcCli = createRpcCli({
|
|
450
|
+
router: command.router,
|
|
451
|
+
name: globalCliMeta.name || command.meta?.name || routerMeta?.name,
|
|
452
|
+
version: globalCliMeta.version || command.meta?.version || routerMeta?.version,
|
|
453
|
+
description: globalCliMeta.description || command.meta?.description || routerMeta?.description
|
|
454
|
+
});
|
|
455
|
+
debugLog(
|
|
456
|
+
"RPC CLI created from command router, running with argv:",
|
|
457
|
+
process.argv.slice(2)
|
|
458
|
+
);
|
|
459
|
+
await rpcCli.run({
|
|
460
|
+
argv: process.argv.slice(2),
|
|
461
|
+
logger: {
|
|
462
|
+
info: console.log,
|
|
463
|
+
error: console.error
|
|
464
|
+
},
|
|
465
|
+
process: {
|
|
466
|
+
exit: process.exit
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (typeof command.onLauncherInit === "function") {
|
|
472
|
+
try {
|
|
473
|
+
await command.onLauncherInit();
|
|
474
|
+
} catch (err) {
|
|
475
|
+
relinka("error", "Error in onLauncherInit:", err);
|
|
476
|
+
if (parserOptions.autoExit !== false) process.exit(1);
|
|
477
|
+
throw err;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
if (!parserOptions.fileBased && !command.commands) {
|
|
482
|
+
const mainEntry = process.argv[1] ? path.dirname(path.resolve(process.argv[1])) : process.cwd();
|
|
483
|
+
const defaultCmdsRoot = path.join(mainEntry, "src", "app");
|
|
484
|
+
const exists = await fs.pathExists(defaultCmdsRoot);
|
|
485
|
+
const finalCmdsRoot = exists ? defaultCmdsRoot : path.join(mainEntry, "app");
|
|
486
|
+
parserOptions.fileBased = {
|
|
487
|
+
enable: true,
|
|
488
|
+
cmdsRootPath: finalCmdsRoot
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const rawArgv = process.argv.slice(2);
|
|
492
|
+
const autoExit = parserOptions.autoExit !== false;
|
|
493
|
+
if (!(parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0 || command.run || command.router)) {
|
|
494
|
+
relinka(
|
|
495
|
+
"error",
|
|
496
|
+
"Invalid CLI configuration: No file-based commands, subCommands, run() handler, or router are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand, a run() handler, or a router."
|
|
497
|
+
);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
if (parserOptions.fileBased?.enable && rawArgv.length > 0) {
|
|
501
|
+
const commandsDir = path.resolve(parserOptions.fileBased.cmdsRootPath);
|
|
502
|
+
const resolved = await resolveFileBasedCommandPath(
|
|
503
|
+
commandsDir,
|
|
504
|
+
rawArgv
|
|
505
|
+
);
|
|
506
|
+
if (resolved) {
|
|
507
|
+
const {
|
|
508
|
+
def: subCommand,
|
|
509
|
+
leftoverArgv,
|
|
510
|
+
path: pathSegments
|
|
511
|
+
} = resolved;
|
|
512
|
+
const helpIdx = leftoverArgv.findIndex(
|
|
513
|
+
(arg) => arg === "help" || arg === "--help" || arg === "-h"
|
|
514
|
+
);
|
|
515
|
+
if (helpIdx !== -1) {
|
|
516
|
+
await showUsage(
|
|
517
|
+
subCommand,
|
|
518
|
+
{
|
|
519
|
+
...parserOptions,
|
|
520
|
+
_fileBasedCurrentDir: pathSegments.length ? path.join(commandsDir, ...pathSegments) : commandsDir,
|
|
521
|
+
_fileBasedPathSegments: pathSegments
|
|
522
|
+
},
|
|
523
|
+
globalCliMeta
|
|
524
|
+
);
|
|
525
|
+
if (autoExit) process.exit(0);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const fileBasedEnabled = parserOptions.fileBased?.enable;
|
|
531
|
+
if (fileBasedEnabled && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
|
|
532
|
+
const [subName, ...subCmdArgv] = rawArgv;
|
|
399
533
|
try {
|
|
400
|
-
|
|
534
|
+
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
535
|
+
if (typeof command.onCmdInit === "function")
|
|
536
|
+
await command.onCmdInit(ctx);
|
|
537
|
+
await runFileBasedSubCmd(
|
|
538
|
+
subName,
|
|
539
|
+
subCmdArgv,
|
|
540
|
+
parserOptions.fileBased,
|
|
541
|
+
parserOptions,
|
|
542
|
+
command.onCmdExit ? async (_subCtx) => {
|
|
543
|
+
await command.onCmdExit?.(ctx);
|
|
544
|
+
} : void 0,
|
|
545
|
+
globalCliMeta
|
|
546
|
+
);
|
|
547
|
+
if (autoExit) process.exit(0);
|
|
548
|
+
return;
|
|
401
549
|
} catch (err) {
|
|
402
|
-
relinka("error", "Error
|
|
403
|
-
if (
|
|
550
|
+
relinka("error", "Error loading file-based subcommand:", err.message);
|
|
551
|
+
if (autoExit) process.exit(1);
|
|
404
552
|
throw err;
|
|
405
553
|
}
|
|
406
554
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
);
|
|
425
|
-
process.exit(1);
|
|
555
|
+
if (!fileBasedEnabled && command.commands && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
|
|
556
|
+
const [maybeSub, ...subCmdArgv] = rawArgv;
|
|
557
|
+
let subSpec;
|
|
558
|
+
for (const [key, spec] of Object.entries(command.commands)) {
|
|
559
|
+
if (key === maybeSub) {
|
|
560
|
+
subSpec = spec;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
const cmd = await loadSubCommand(spec);
|
|
565
|
+
if (cmd.meta?.aliases?.includes(maybeSub)) {
|
|
566
|
+
subSpec = spec;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
} catch (err) {
|
|
570
|
+
debugLog(`Error checking alias for command ${key}:`, err);
|
|
571
|
+
}
|
|
426
572
|
}
|
|
427
|
-
if (
|
|
428
|
-
const
|
|
429
|
-
|
|
573
|
+
if (subSpec) {
|
|
574
|
+
const helpIdx = subCmdArgv.findIndex(
|
|
575
|
+
(arg) => arg === "help" || arg === "--help" || arg === "-h"
|
|
430
576
|
);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} = resolved;
|
|
441
|
-
const helpIdx = leftoverArgv.findIndex(
|
|
442
|
-
(arg) => arg === "help" || arg === "--help" || arg === "-h"
|
|
577
|
+
if (helpIdx !== -1) {
|
|
578
|
+
const subCommandDef = await loadSubCommand(subSpec);
|
|
579
|
+
await showUsage(
|
|
580
|
+
subCommandDef,
|
|
581
|
+
{
|
|
582
|
+
...parserOptions,
|
|
583
|
+
_isSubcommand: true
|
|
584
|
+
},
|
|
585
|
+
globalCliMeta
|
|
443
586
|
);
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
subCommand,
|
|
447
|
-
{
|
|
448
|
-
...parserOptions,
|
|
449
|
-
_fileBasedCurrentDir: pathSegments.length ? path.join(commandsDir, ...pathSegments) : commandsDir,
|
|
450
|
-
_fileBasedPathSegments: pathSegments
|
|
451
|
-
},
|
|
452
|
-
globalCliMeta
|
|
453
|
-
);
|
|
454
|
-
if (autoExit) process.exit(0);
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
587
|
+
if (autoExit) process.exit(0);
|
|
588
|
+
return;
|
|
457
589
|
}
|
|
458
|
-
}
|
|
459
|
-
const fileBasedEnabled = parserOptions.fileBased?.enable;
|
|
460
|
-
if (fileBasedEnabled && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
|
|
461
|
-
const [subName, ...subCmdArgv] = rawArgv;
|
|
462
590
|
try {
|
|
463
591
|
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
464
592
|
if (typeof command.onCmdInit === "function")
|
|
465
593
|
await command.onCmdInit(ctx);
|
|
466
|
-
await
|
|
467
|
-
|
|
594
|
+
await runSubCommand(
|
|
595
|
+
subSpec,
|
|
468
596
|
subCmdArgv,
|
|
469
|
-
parserOptions
|
|
470
|
-
parserOptions,
|
|
597
|
+
{ ...parserOptions, _isSubcommand: true },
|
|
471
598
|
command.onCmdExit ? async (_subCtx) => {
|
|
472
599
|
await command.onCmdExit?.(ctx);
|
|
473
600
|
} : void 0,
|
|
@@ -476,107 +603,64 @@ export function createCli(options, legacyParserOptions) {
|
|
|
476
603
|
if (autoExit) process.exit(0);
|
|
477
604
|
return;
|
|
478
605
|
} catch (err) {
|
|
479
|
-
relinka(
|
|
480
|
-
"error",
|
|
481
|
-
"Error loading file-based subcommand:",
|
|
482
|
-
err.message
|
|
483
|
-
);
|
|
606
|
+
relinka("error", "Error running subcommand:", err.message);
|
|
484
607
|
if (autoExit) process.exit(1);
|
|
485
608
|
throw err;
|
|
486
609
|
}
|
|
487
610
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
break;
|
|
501
|
-
}
|
|
502
|
-
} catch (err) {
|
|
503
|
-
debugLog(`Error checking alias for command ${key}:`, err);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
if (subSpec) {
|
|
507
|
-
const helpIdx = subCmdArgv.findIndex(
|
|
508
|
-
(arg) => arg === "help" || arg === "--help" || arg === "-h"
|
|
509
|
-
);
|
|
510
|
-
if (helpIdx !== -1) {
|
|
511
|
-
const subCommandDef = await loadSubCommand(subSpec);
|
|
512
|
-
await showUsage(
|
|
513
|
-
subCommandDef,
|
|
514
|
-
{
|
|
515
|
-
...parserOptions,
|
|
516
|
-
_isSubcommand: true
|
|
517
|
-
},
|
|
518
|
-
globalCliMeta
|
|
519
|
-
);
|
|
520
|
-
if (autoExit) process.exit(0);
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
try {
|
|
524
|
-
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
525
|
-
if (typeof command.onCmdInit === "function")
|
|
526
|
-
await command.onCmdInit(ctx);
|
|
527
|
-
await runSubCommand(
|
|
528
|
-
subSpec,
|
|
529
|
-
subCmdArgv,
|
|
530
|
-
{ ...parserOptions, _isSubcommand: true },
|
|
531
|
-
command.onCmdExit ? async (_subCtx) => {
|
|
532
|
-
await command.onCmdExit?.(ctx);
|
|
533
|
-
} : void 0,
|
|
534
|
-
globalCliMeta
|
|
535
|
-
);
|
|
536
|
-
if (autoExit) process.exit(0);
|
|
537
|
-
return;
|
|
538
|
-
} catch (err) {
|
|
539
|
-
relinka("error", "Error running subcommand:", err.message);
|
|
540
|
-
if (autoExit) process.exit(1);
|
|
541
|
-
throw err;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
await relinkaConfig;
|
|
546
|
-
if (rawArgv[0] === "help" || checkHelp(rawArgv)) {
|
|
547
|
-
await showUsage(command, parserOptions, globalCliMeta);
|
|
548
|
-
if (autoExit) process.exit(0);
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
if (checkVersion(rawArgv)) {
|
|
552
|
-
if (command.meta?.name) {
|
|
553
|
-
relinka(
|
|
554
|
-
"info",
|
|
555
|
-
`${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
if (autoExit) process.exit(0);
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
try {
|
|
562
|
-
await runCommandWithArgs(
|
|
563
|
-
command,
|
|
564
|
-
rawArgv,
|
|
565
|
-
parserOptions,
|
|
566
|
-
globalCliMeta
|
|
611
|
+
}
|
|
612
|
+
await relinkaConfig;
|
|
613
|
+
if (rawArgv[0] === "help" || checkHelp(rawArgv)) {
|
|
614
|
+
await showUsage(command, parserOptions, globalCliMeta);
|
|
615
|
+
if (autoExit) process.exit(0);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (checkVersion(rawArgv)) {
|
|
619
|
+
if (command.meta?.name) {
|
|
620
|
+
relinka(
|
|
621
|
+
"info",
|
|
622
|
+
`${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
|
|
567
623
|
);
|
|
568
|
-
} finally {
|
|
569
624
|
}
|
|
570
|
-
|
|
625
|
+
if (autoExit) process.exit(0);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
try {
|
|
629
|
+
await runCommandWithArgs(
|
|
630
|
+
command,
|
|
631
|
+
rawArgv,
|
|
632
|
+
parserOptions,
|
|
633
|
+
globalCliMeta
|
|
634
|
+
);
|
|
571
635
|
} finally {
|
|
572
|
-
if (typeof command.onLauncherExit === "function")
|
|
573
|
-
await command.onLauncherExit();
|
|
574
636
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
)
|
|
637
|
+
await relinkaShutdown();
|
|
638
|
+
} finally {
|
|
639
|
+
if (typeof command.onLauncherExit === "function")
|
|
640
|
+
await command.onLauncherExit();
|
|
578
641
|
}
|
|
579
642
|
};
|
|
643
|
+
const promise = execute();
|
|
644
|
+
const cli = Object.assign(promise, {
|
|
645
|
+
/**
|
|
646
|
+
* @deprecated Use createCli() directly instead. This method will be removed in a future version.
|
|
647
|
+
* @example
|
|
648
|
+
* // Instead of:
|
|
649
|
+
* createCli({...}).run()
|
|
650
|
+
* // Use:
|
|
651
|
+
* await createCli({...})
|
|
652
|
+
*/
|
|
653
|
+
async run(_ctx) {
|
|
654
|
+
relinka(
|
|
655
|
+
"warn",
|
|
656
|
+
"\u26A0\uFE0F Deprecated: .run() method is deprecated. Use createCli() directly instead."
|
|
657
|
+
);
|
|
658
|
+
relinka("warn", " Instead of: createCli({...}).run()");
|
|
659
|
+
relinka("warn", " Use: await createCli({...})");
|
|
660
|
+
return execute(_ctx);
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
return cli;
|
|
580
664
|
}
|
|
581
665
|
function checkHelp(argv) {
|
|
582
666
|
return argv.includes("--help") || argv.includes("-h");
|
|
@@ -784,7 +868,7 @@ async function runCommandWithArgs(command, argv, parserOptions, globalCliMeta, r
|
|
|
784
868
|
if (command.run) {
|
|
785
869
|
await command.run(ctx);
|
|
786
870
|
} else {
|
|
787
|
-
const isDispatcher = parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0;
|
|
871
|
+
const isDispatcher = parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0 || command.router;
|
|
788
872
|
const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
|
|
789
873
|
if (isDispatcher && noSubcommandArgInCurrentCall) {
|
|
790
874
|
relinka("warn", "Please specify a command");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AnyRouter } from "./trpc-orpc-support/trpc-compat.js";
|
|
1
2
|
export type EmptyArgs = Record<string, never>;
|
|
2
3
|
export interface BaseArgProps {
|
|
3
4
|
description?: string;
|
|
@@ -99,6 +100,11 @@ export interface DefineCommandOptions<A extends ArgDefinitions = EmptyArgs> {
|
|
|
99
100
|
* Called once per CLI process, after all command/run() logic is finished
|
|
100
101
|
*/
|
|
101
102
|
onLauncherExit?: () => void | Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* tRPC/oRPC router for RPC mode. When provided, the command will automatically
|
|
105
|
+
* switch to RPC mode and use the router's procedures as CLI commands.
|
|
106
|
+
*/
|
|
107
|
+
router?: AnyRouter;
|
|
102
108
|
}
|
|
103
109
|
export interface Command<A extends ArgDefinitions = EmptyArgs> {
|
|
104
110
|
meta?: CommandMeta;
|
|
@@ -136,6 +142,11 @@ export interface Command<A extends ArgDefinitions = EmptyArgs> {
|
|
|
136
142
|
* Called once per CLI process, after all command/run() logic is finished
|
|
137
143
|
*/
|
|
138
144
|
onLauncherExit?: () => void | Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* tRPC/oRPC router for RPC mode. When provided, the command will automatically
|
|
147
|
+
* switch to RPC mode and use the router's procedures as CLI commands.
|
|
148
|
+
*/
|
|
149
|
+
router?: AnyRouter;
|
|
139
150
|
}
|
|
140
151
|
export type InferArgTypes<A extends ArgDefinitions> = {
|
|
141
152
|
[K in keyof A]: A[K] extends PositionalArgDefinition ? string : A[K] extends BooleanArgDefinition ? boolean : A[K] extends StringArgDefinition ? string : A[K] extends NumberArgDefinition ? number : A[K] extends {
|
package/bin/mod.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { animationMap, animateText } from "./libs/animate/animate-mod.js";
|
|
2
2
|
export { anykeyPrompt } from "./libs/anykey/anykey-mod.js";
|
|
3
|
-
export { createAsciiArt } from "./libs/visual/visual-mod.js";
|
|
4
3
|
export type { CancelValue } from "./libs/cancel/cancel.js";
|
|
5
4
|
export { CANCEL, isWindows, setRawMode, getColumns, block, isCancel, cancel, createCancel, } from "./libs/cancel/cancel.js";
|
|
6
5
|
export { confirm } from "./libs/confirm/confirm-alias.js";
|
|
@@ -63,4 +62,5 @@ export { completePrompt, renderEndLine, renderEndLineInput, } from "./libs/utils
|
|
|
63
62
|
export { streamText, streamTextBox, streamTextWithSpinner, } from "./libs/utils/stream-text.js";
|
|
64
63
|
export { pm, reliversePrompts } from "./libs/utils/system.js";
|
|
65
64
|
export { isTerminalInteractive, isValidName, normalizeName, } from "./libs/utils/validate.js";
|
|
65
|
+
export { createAsciiArt } from "./libs/visual/visual-mod.js";
|
|
66
66
|
export type { MsgType, TypographyName, BorderColorName, ColorName, VariantName, MsgConfig, PromptOptions, ChoiceOptions, SelectOption, StandardColor, OutputColor, EditorExitResult, MessageKind, AllKinds, MessageConfig, StreamOptions, ProgressBarOptions, ProgressBar, PromptType, ConfirmPromptOptions, StreamTextOptions, PreventWrongTerminalSizeOptions, InputPromptOptions, RenderParams, SymbolName, Symbols, FmtMsgOptions, TogglePromptParams, SeparatorOption, MultiselectPromptParams, DatePromptOptions, } from "./types.js";
|
package/bin/mod.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { animationMap, animateText } from "./libs/animate/animate-mod.js";
|
|
2
2
|
export { anykeyPrompt } from "./libs/anykey/anykey-mod.js";
|
|
3
|
-
export { createAsciiArt } from "./libs/visual/visual-mod.js";
|
|
4
3
|
export {
|
|
5
4
|
CANCEL,
|
|
6
5
|
isWindows,
|
|
@@ -148,3 +147,4 @@ export {
|
|
|
148
147
|
isValidName,
|
|
149
148
|
normalizeName
|
|
150
149
|
} from "./libs/utils/validate.js";
|
|
150
|
+
export { createAsciiArt } from "./libs/visual/visual-mod.js";
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"cli-spinners": "^3.2.0",
|
|
18
18
|
"commander": "^14.0.0",
|
|
19
19
|
"detect-package-manager": "^3.0.2",
|
|
20
|
+
"effect": "^3.16.8",
|
|
20
21
|
"enquirer": "^2.4.1",
|
|
21
22
|
"figlet": "^1.8.1",
|
|
22
23
|
"gradient-string": "^3.0.0",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"license": "MIT",
|
|
44
45
|
"name": "@reliverse/rempts",
|
|
45
46
|
"type": "module",
|
|
46
|
-
"version": "1.7.
|
|
47
|
+
"version": "1.7.32",
|
|
47
48
|
"author": "reliverse",
|
|
48
49
|
"bugs": {
|
|
49
50
|
"email": "blefnk@gmail.com",
|