@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 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
- return {
383
- /**
384
- * @deprecated Use createCli() directly instead. This method will be removed in a future version.
385
- * @example
386
- * // Instead of:
387
- * createCli({...}).run()
388
- * // Use:
389
- * await createCli({...})
390
- */
391
- async run(_ctx) {
392
- relinka(
393
- "warn",
394
- "\u26A0\uFE0F Deprecated: .run() method is deprecated. Use createCli() directly instead."
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
- relinka("warn", " Instead of: createCli({...}).run()");
397
- relinka("warn", " Use: await createCli({...})");
398
- if (typeof command.onLauncherInit === "function") {
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
- await command.onLauncherInit();
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 in onLauncherInit:", err);
403
- if (parserOptions.autoExit !== false) process.exit(1);
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
- try {
408
- if (!parserOptions.fileBased && !command.commands) {
409
- const mainEntry = process.argv[1] ? path.dirname(path.resolve(process.argv[1])) : process.cwd();
410
- const defaultCmdsRoot = path.join(mainEntry, "src", "app");
411
- const exists = await fs.pathExists(defaultCmdsRoot);
412
- const finalCmdsRoot = exists ? defaultCmdsRoot : path.join(mainEntry, "app");
413
- parserOptions.fileBased = {
414
- enable: true,
415
- cmdsRootPath: finalCmdsRoot
416
- };
417
- }
418
- const rawArgv = process.argv.slice(2);
419
- const autoExit = parserOptions.autoExit !== false;
420
- if (!(parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0 || command.run)) {
421
- relinka(
422
- "error",
423
- "Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand or a run() handler."
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 (parserOptions.fileBased?.enable && rawArgv.length > 0) {
428
- const commandsDir = path.resolve(
429
- parserOptions.fileBased.cmdsRootPath
573
+ if (subSpec) {
574
+ const helpIdx = subCmdArgv.findIndex(
575
+ (arg) => arg === "help" || arg === "--help" || arg === "-h"
430
576
  );
431
- const resolved = await resolveFileBasedCommandPath(
432
- commandsDir,
433
- rawArgv
434
- );
435
- if (resolved) {
436
- const {
437
- def: subCommand,
438
- leftoverArgv,
439
- path: pathSegments
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 (helpIdx !== -1) {
445
- await showUsage(
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 runFileBasedSubCmd(
467
- subName,
594
+ await runSubCommand(
595
+ subSpec,
468
596
  subCmdArgv,
469
- parserOptions.fileBased,
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
- if (!fileBasedEnabled && command.commands && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
489
- const [maybeSub, ...subCmdArgv] = rawArgv;
490
- let subSpec;
491
- for (const [key, spec] of Object.entries(command.commands)) {
492
- if (key === maybeSub) {
493
- subSpec = spec;
494
- break;
495
- }
496
- try {
497
- const cmd = await loadSubCommand(spec);
498
- if (cmd.meta?.aliases?.includes(maybeSub)) {
499
- subSpec = spec;
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
- await relinkaShutdown();
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
- throw new Error(
576
- "\u26A0\uFE0F Deprecated: .run() method is deprecated. Use createCli() directly instead.\n Instead of: createCli({...}).run()\n Use: await createCli({...})"
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.30",
47
+ "version": "1.7.32",
47
48
  "author": "reliverse",
48
49
  "bugs": {
49
50
  "email": "blefnk@gmail.com",