@ricsam/r5dctl 0.0.21 → 0.0.23

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
@@ -49,6 +49,9 @@ r5dctl delete project <namespace/name|id>
49
49
 
50
50
  r5dctl -p <project> get branches
51
51
  r5dctl -p <project> describe branch <branch>
52
+ r5dctl -p <project> -b <branch> get envs
53
+ r5dctl -p <project> -b <branch> set envs --from-env-file ~/.env --only-missing
54
+ r5dctl -p <project> -b <branch> set envs -e backend/API_KEY=value --description "API credential"
52
55
  r5dctl -p <project> get sessions --branch <branch>
53
56
  r5dctl -p <project> create session -b <branch> --name "Spec session"
54
57
  r5dctl describe session <session-id>
@@ -64,8 +67,14 @@ r5dctl -s <session-id> prompt --mode plan --model max "..."
64
67
  r5dctl -s <session-id> answer-questions -a1 "Recipe Collection" -a2 "Both Manual & AI"
65
68
  r5dctl -s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value
66
69
 
70
+ r5dctl -p <project> -b <branch> shell
71
+ r5dctl -p <project> -b <branch> shell -c "bun test"
67
72
  ```
68
73
 
74
+ `shell` connects to the same live worker PTY as the web Shell tab. Without `-c` it attaches the local terminal interactively. With `-c`/`--command`, it runs the supplied shell expression, streams combined PTY output, and exits with the remote command's status. Because command output uses a PTY, stdout and stderr are combined and programs may emit colors or other terminal control sequences.
75
+
76
+ `get envs` reports names and whether values are set, but never prints the values. Prefer `--from-env-file` for bulk imports so secret values do not appear in shell arguments or history. Dotenv imports target backend envs by default; use `--target frontend` when needed. Empty dotenv values are skipped unless `--include-empty` is passed, and `--only-missing` preserves values that are already set.
77
+
69
78
  Conversation output is human-readable by default and excludes both the system prompt and tool definitions. `--raw` keeps the transcript layout but renders structured message and tool parts as JSON, `--tools` includes the provider-shaped tool definitions, and `--system` includes system prompt messages. The global `--json` flag remains separate and prints the complete API response envelope.
70
79
 
71
80
  When available, conversation rendering uses the newest complete persisted model-request snapshot on the active conversation branch, so its mode, tier, model, thinking mode, tools, and messages reflect the request that actually ran. Legacy or empty sessions without snapshots fall back to reconstruction from the latest conversation context.
package/dist/cjs/cli.cjs CHANGED
@@ -36,9 +36,13 @@ __export(cli_exports, {
36
36
  parseEnvFlags: () => parseEnvFlags,
37
37
  parseGlobalArgs: () => parseGlobalArgs,
38
38
  parsePromptArgs: () => parsePromptArgs,
39
+ parseSetEnvArgs: () => parseSetEnvArgs,
40
+ parseShellArgs: () => parseShellArgs,
41
+ readDotenvFile: () => readDotenvFile,
39
42
  renderConversationResponse: () => renderConversationResponse,
40
43
  resolveCommandExecution: () => resolveCommandExecution,
41
- runR5dctlCli: () => runR5dctlCli
44
+ runR5dctlCli: () => runR5dctlCli,
45
+ summarizeEnvData: () => summarizeEnvData
42
46
  });
43
47
  module.exports = __toCommonJS(cli_exports);
44
48
  var import_node_fs = __toESM(require("node:fs"), 1);
@@ -46,7 +50,9 @@ var import_node_os = __toESM(require("node:os"), 1);
46
50
  var import_node_path = __toESM(require("node:path"), 1);
47
51
  var import_node_child_process = require("node:child_process");
48
52
  var import_promises = require("node:timers/promises");
53
+ var import_dotenv = require("dotenv");
49
54
  var import_r5d_api = require("@ricsam/r5d-api");
55
+ var import_shell = require("./shell.cjs");
50
56
  const CHAT_MODES = /* @__PURE__ */ new Set([
51
57
  "ask",
52
58
  "plan",
@@ -79,12 +85,21 @@ const CLI_ONLY_HELP_ENTRIES = [
79
85
  section: "auth",
80
86
  usage: "auth login [--no-open]",
81
87
  description: "Start the browser login flow."
88
+ },
89
+ {
90
+ section: "shell",
91
+ usage: "-p <project> -b <branch> shell [-c <command>]",
92
+ description: "Open an interactive worker shell or run one command through a PTY."
82
93
  }
83
94
  ];
84
95
  const CLI_ONLY_COMMAND_HELP = [
85
96
  {
86
97
  path: ["auth", "login"],
87
98
  usage: "auth login [--no-open]"
99
+ },
100
+ {
101
+ path: ["shell"],
102
+ usage: "-p <project> -b <branch> shell [-c <command>]"
88
103
  }
89
104
  ];
90
105
  const SHARED_HELP_ENTRIES = [
@@ -103,6 +118,21 @@ const SHARED_HELP_ENTRIES = [
103
118
  { section: "projects", usage: "delete project <namespace/name|id>", description: "Delete a project." },
104
119
  { section: "branches", usage: "-p <project> get branches", description: "List branches for a project." },
105
120
  { section: "branches", usage: "-p <project> describe branch <branch>", description: "Show branch URLs and sessions." },
121
+ {
122
+ section: "envs",
123
+ usage: "-p <project> -b <branch> get envs",
124
+ description: "List declared envs and whether each has a value, without revealing values."
125
+ },
126
+ {
127
+ section: "envs",
128
+ usage: "-p <project> -b <branch> set envs --from-env-file <path> [--target <backend|frontend>] [--only-missing]",
129
+ description: "Securely import branch envs from a dotenv file without putting values in command arguments."
130
+ },
131
+ {
132
+ section: "envs",
133
+ usage: "-p <project> -b <branch> set envs -e backend/KEY=value [--description <text>] [--only-missing]",
134
+ description: "Set one or more branch envs directly."
135
+ },
106
136
  {
107
137
  section: "sessions",
108
138
  usage: "-p <project> get sessions [--branch <branch>]",
@@ -151,12 +181,14 @@ const SHARED_HELP_ENTRIES = [
151
181
  { section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
152
182
  { section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
153
183
  ];
154
- const HELP_SECTION_ORDER = ["auth", "projects", "branches", "sessions", "agents", "merges"];
184
+ const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "shell", "agents", "merges"];
155
185
  const HELP_SECTION_TITLES = {
156
186
  auth: "Auth",
157
187
  projects: "Projects",
158
188
  branches: "Branches",
189
+ envs: "Environment variables",
159
190
  sessions: "Sessions",
191
+ shell: "Shell",
160
192
  agents: "Agents",
161
193
  merges: "Merges"
162
194
  };
@@ -501,10 +533,117 @@ function validateEnvAssignment(value) {
501
533
  }
502
534
  const target = assignment.slice(0, slashIndex);
503
535
  const key = assignment.slice(slashIndex + 1);
504
- if (target !== "backend" && target !== "frontend" || key.length === 0) {
536
+ if (target !== "backend" && target !== "frontend" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
505
537
  throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
506
538
  }
507
539
  }
540
+ function parseEnvAssignment(value) {
541
+ validateEnvAssignment(value);
542
+ const equalsIndex = value.indexOf("=");
543
+ const slashIndex = value.indexOf("/");
544
+ return {
545
+ target: value.slice(0, slashIndex),
546
+ name: value.slice(slashIndex + 1, equalsIndex),
547
+ value: value.slice(equalsIndex + 1)
548
+ };
549
+ }
550
+ function parseSetEnvArgs(args) {
551
+ const parsed = {
552
+ assignments: [],
553
+ target: "backend",
554
+ onlyMissing: false,
555
+ includeEmpty: false
556
+ };
557
+ for (let index = 0; index < args.length; index += 1) {
558
+ const arg = args[index];
559
+ if (!arg) continue;
560
+ if (arg === "-e" || arg === "--env") {
561
+ const value = requireValue(args[index + 1], `Missing value for ${arg}`);
562
+ validateEnvAssignment(value);
563
+ parsed.assignments.push(value);
564
+ index += 1;
565
+ continue;
566
+ }
567
+ const inlineEnv = parseLongOptionWithEquals(arg, "--env");
568
+ if (inlineEnv !== void 0) {
569
+ validateEnvAssignment(inlineEnv);
570
+ parsed.assignments.push(inlineEnv);
571
+ continue;
572
+ }
573
+ if (arg === "--from-env-file") {
574
+ parsed.fromEnvFile = requireValue(args[index + 1], "Missing value for --from-env-file");
575
+ index += 1;
576
+ continue;
577
+ }
578
+ const inlineFile = parseLongOptionWithEquals(arg, "--from-env-file");
579
+ if (inlineFile !== void 0) {
580
+ parsed.fromEnvFile = requireValue(inlineFile, "Missing value for --from-env-file");
581
+ continue;
582
+ }
583
+ if (arg === "--target") {
584
+ const target = requireValue(args[index + 1], "Missing value for --target");
585
+ if (target !== "backend" && target !== "frontend") {
586
+ throw new Error("Invalid --target. Expected backend or frontend");
587
+ }
588
+ parsed.target = target;
589
+ index += 1;
590
+ continue;
591
+ }
592
+ const inlineTarget = parseLongOptionWithEquals(arg, "--target");
593
+ if (inlineTarget !== void 0) {
594
+ if (inlineTarget !== "backend" && inlineTarget !== "frontend") {
595
+ throw new Error("Invalid --target. Expected backend or frontend");
596
+ }
597
+ parsed.target = inlineTarget;
598
+ continue;
599
+ }
600
+ if (arg === "--description") {
601
+ parsed.description = requireValue(args[index + 1], "Missing value for --description");
602
+ index += 1;
603
+ continue;
604
+ }
605
+ const inlineDescription = parseLongOptionWithEquals(arg, "--description");
606
+ if (inlineDescription !== void 0) {
607
+ parsed.description = requireValue(inlineDescription, "Missing value for --description");
608
+ continue;
609
+ }
610
+ if (arg === "--optional") {
611
+ if (parsed.optional === false) throw new Error("Use only one of --optional or --required");
612
+ parsed.optional = true;
613
+ continue;
614
+ }
615
+ if (arg === "--required") {
616
+ if (parsed.optional === true) throw new Error("Use only one of --optional or --required");
617
+ parsed.optional = false;
618
+ continue;
619
+ }
620
+ if (arg === "--only-missing") {
621
+ parsed.onlyMissing = true;
622
+ continue;
623
+ }
624
+ if (arg === "--include-empty") {
625
+ parsed.includeEmpty = true;
626
+ continue;
627
+ }
628
+ throw new Error(arg.startsWith("-") ? `Unknown set envs flag: ${arg}` : `Unexpected set envs value: ${arg}`);
629
+ }
630
+ if (parsed.assignments.length > 0 && parsed.fromEnvFile) {
631
+ throw new Error("Use either -e/--env or --from-env-file, not both");
632
+ }
633
+ if (parsed.assignments.length === 0 && !parsed.fromEnvFile) {
634
+ throw new Error("Provide at least one -e/--env assignment or --from-env-file <path>");
635
+ }
636
+ return parsed;
637
+ }
638
+ function resolveLocalPath(filePath) {
639
+ if (filePath === "~") return import_node_os.default.homedir();
640
+ if (filePath.startsWith("~/")) return import_node_path.default.join(import_node_os.default.homedir(), filePath.slice(2));
641
+ return import_node_path.default.resolve(filePath);
642
+ }
643
+ function readDotenvFile(filePath) {
644
+ const resolvedPath = resolveLocalPath(filePath);
645
+ return (0, import_dotenv.parse)(import_node_fs.default.readFileSync(resolvedPath));
646
+ }
508
647
  function parseEnvFlags(args) {
509
648
  const envs = [];
510
649
  for (let i = 0; i < args.length; i += 1) {
@@ -598,6 +737,33 @@ function parsePromptArgs(args) {
598
737
  message
599
738
  };
600
739
  }
740
+ function parseShellArgs(args) {
741
+ let command;
742
+ for (let index = 0; index < args.length; index += 1) {
743
+ const arg = args[index];
744
+ if (!arg) {
745
+ continue;
746
+ }
747
+ if (arg === "-c" || arg === "--command") {
748
+ if (command !== void 0) {
749
+ throw new Error("Use only one -c/--command value");
750
+ }
751
+ command = requireValue(args[index + 1], `Missing value for ${arg}`);
752
+ index += 1;
753
+ continue;
754
+ }
755
+ const inlineCommand = parseLongOptionWithEquals(arg, "--command");
756
+ if (inlineCommand !== void 0) {
757
+ if (command !== void 0) {
758
+ throw new Error("Use only one -c/--command value");
759
+ }
760
+ command = requireValue(inlineCommand, "Missing value for --command");
761
+ continue;
762
+ }
763
+ throw new Error(arg.startsWith("-") ? `Unknown shell flag: ${arg}` : `Unexpected shell value: ${arg}`);
764
+ }
765
+ return command === void 0 ? {} : { command };
766
+ }
601
767
  async function openInBrowser(url) {
602
768
  if (process.platform === "darwin") {
603
769
  (0, import_node_child_process.spawn)("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -739,6 +905,108 @@ function renderSessionList(sessions) {
739
905
  function renderSessionDescription(session) {
740
906
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
741
907
  }
908
+ function summarizeEnvData(data) {
909
+ const summary = {};
910
+ for (const target of ["backend", "frontend"]) {
911
+ const entries = data[target];
912
+ if (!entries) continue;
913
+ summary[target] = Object.fromEntries(
914
+ Object.entries(entries).sort(([left], [right]) => left.localeCompare(right)).map(([name, env]) => [
915
+ name,
916
+ {
917
+ optional: env.optional ?? false,
918
+ description: env.description,
919
+ hasValue: env.value !== null
920
+ }
921
+ ])
922
+ );
923
+ }
924
+ return summary;
925
+ }
926
+ function renderEnvSummary(data) {
927
+ const lines = [];
928
+ for (const target of ["backend", "frontend"]) {
929
+ const entries = Object.entries(data[target] ?? {});
930
+ if (entries.length === 0) continue;
931
+ if (lines.length > 0) lines.push("");
932
+ lines.push(`${target}:`);
933
+ for (const [name, env] of entries) {
934
+ lines.push(` ${name} ${env.hasValue ? "set" : "missing"} ${env.optional ? "optional" : "required"} ${env.description}`);
935
+ }
936
+ }
937
+ return lines.length > 0 ? `${lines.join("\n")}
938
+ ` : "No environment variables declared.\n";
939
+ }
940
+ function renderEnvSetResult(result) {
941
+ const lines = [
942
+ `Updated ${result.updated.length} environment variable${result.updated.length === 1 ? "" : "s"} for ${result.project}/${result.branch}.`
943
+ ];
944
+ if (result.skippedExisting.length > 0) lines.push(`Skipped ${result.skippedExisting.length} existing value(s).`);
945
+ if (result.skippedEmpty.length > 0) lines.push(`Skipped ${result.skippedEmpty.length} empty value(s).`);
946
+ if (result.updated.length > 0) {
947
+ lines.push(...result.updated.map(({ target, name }) => ` ${target}/${name}`));
948
+ }
949
+ return `${lines.join("\n")}
950
+ `;
951
+ }
952
+ async function setBranchEnvs(client, projectRef, branchName, args) {
953
+ const options = parseSetEnvArgs(args);
954
+ const project = await client.projects.describe(projectRef);
955
+ const current = await client.projects.env.get(project.id, branchName);
956
+ const assignments = options.fromEnvFile ? Object.entries(readDotenvFile(options.fromEnvFile)).map(([name, value]) => {
957
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
958
+ throw new Error(`Invalid environment variable name in dotenv file: ${name}`);
959
+ }
960
+ return { target: options.target, name, value };
961
+ }) : options.assignments.map(parseEnvAssignment);
962
+ const uniqueAssignments = /* @__PURE__ */ new Map();
963
+ for (const assignment of assignments) uniqueAssignments.set(`${assignment.target}/${assignment.name}`, assignment);
964
+ const update = {};
965
+ const result = {
966
+ success: true,
967
+ project: project.path,
968
+ branch: branchName,
969
+ updated: [],
970
+ skippedExisting: [],
971
+ skippedEmpty: []
972
+ };
973
+ const missingDescriptions = [];
974
+ const importedDescription = options.fromEnvFile ? `Imported from ${import_node_path.default.basename(options.fromEnvFile)}.` : void 0;
975
+ for (const assignment of uniqueAssignments.values()) {
976
+ const identity = { target: assignment.target, name: assignment.name };
977
+ if (!options.includeEmpty && assignment.value.length === 0) {
978
+ result.skippedEmpty.push(identity);
979
+ continue;
980
+ }
981
+ const existing = current[assignment.target]?.[assignment.name];
982
+ if (options.onlyMissing && existing?.value !== null && existing?.value !== void 0) {
983
+ result.skippedExisting.push(identity);
984
+ continue;
985
+ }
986
+ const description = options.description ?? existing?.description ?? importedDescription;
987
+ if (!description) {
988
+ missingDescriptions.push(identity);
989
+ continue;
990
+ }
991
+ const targetUpdate = update[assignment.target] ??= {};
992
+ const optional = options.optional ?? existing?.optional;
993
+ targetUpdate[assignment.name] = {
994
+ description,
995
+ value: assignment.value,
996
+ ...optional === void 0 ? {} : { optional }
997
+ };
998
+ result.updated.push(identity);
999
+ }
1000
+ if (missingDescriptions.length > 0) {
1001
+ throw new Error(
1002
+ `--description is required when declaring new envs with -e: ${missingDescriptions.map(({ target, name }) => `${target}/${name}`).join(", ")}`
1003
+ );
1004
+ }
1005
+ if (result.updated.length > 0) {
1006
+ await client.projects.env.update(project.id, branchName, update);
1007
+ }
1008
+ return result;
1009
+ }
742
1010
  function renderAgentStart(agent) {
743
1011
  return [
744
1012
  `Agent: ${agent.sessionId}`,
@@ -940,6 +1208,24 @@ Sessions: ${result.sessions.length}
940
1208
  `);
941
1209
  return;
942
1210
  }
1211
+ if (first === "get" && second === "envs") {
1212
+ const projectRef = requireValue(args[2], "Missing project reference");
1213
+ const branchName = requireValue(args[3], "Missing branch name");
1214
+ const project = await client.projects.describe(projectRef);
1215
+ const envs = summarizeEnvData(await client.projects.env.get(project.id, branchName));
1216
+ write({ project: project.path, branch: branchName, envs }, renderEnvSummary(envs));
1217
+ return;
1218
+ }
1219
+ if (first === "set" && second === "envs") {
1220
+ const result = await setBranchEnvs(
1221
+ client,
1222
+ requireValue(args[2], "Missing project reference"),
1223
+ requireValue(args[3], "Missing branch name"),
1224
+ args.slice(4)
1225
+ );
1226
+ write(result, renderEnvSetResult(result));
1227
+ return;
1228
+ }
943
1229
  if (first === "projects" && second === "sessions" && third === "list" || first === "get" && second === "sessions") {
944
1230
  const offset = first === "get" ? 2 : 3;
945
1231
  const projectRef = requireValue(args[offset], "Missing project reference");
@@ -1177,8 +1463,36 @@ function resolveCommandExecution(options, rest) {
1177
1463
  pluginArgs
1178
1464
  };
1179
1465
  }
1466
+ if (target === "envs") {
1467
+ if (!options.project) {
1468
+ throw new Error("--project/-p is required for `get envs`");
1469
+ }
1470
+ if (!options.branch) {
1471
+ throw new Error("--branch/-b is required for `get envs`");
1472
+ }
1473
+ return {
1474
+ kind: "plugin",
1475
+ pluginArgs: ["get", "envs", options.project, options.branch]
1476
+ };
1477
+ }
1180
1478
  throw new Error("Unknown get command");
1181
1479
  }
1480
+ if (command === "set") {
1481
+ const target = commandArgs[0];
1482
+ if (target !== "envs") {
1483
+ throw new Error("Unknown set command");
1484
+ }
1485
+ if (!options.project) {
1486
+ throw new Error("--project/-p is required for `set envs`");
1487
+ }
1488
+ if (!options.branch) {
1489
+ throw new Error("--branch/-b is required for `set envs`");
1490
+ }
1491
+ return {
1492
+ kind: "plugin",
1493
+ pluginArgs: ["set", "envs", options.project, options.branch, ...commandArgs.slice(1)]
1494
+ };
1495
+ }
1182
1496
  if (command === "describe") {
1183
1497
  const target = commandArgs[0];
1184
1498
  if (target === "project") {
@@ -1298,6 +1612,21 @@ function resolveCommandExecution(options, rest) {
1298
1612
  pluginArgs: ["conversation", sessionId, ...commandArgs]
1299
1613
  };
1300
1614
  }
1615
+ if (command === "shell") {
1616
+ if (!options.project) {
1617
+ throw new Error("--project/-p is required for `shell`");
1618
+ }
1619
+ if (!options.branch) {
1620
+ throw new Error("--branch/-b is required for `shell`");
1621
+ }
1622
+ if (options.json) {
1623
+ throw new Error("--json is not supported for `shell`");
1624
+ }
1625
+ return {
1626
+ kind: "shell",
1627
+ ...parseShellArgs(commandArgs)
1628
+ };
1629
+ }
1301
1630
  if (command === "prompt") {
1302
1631
  const sessionId = options.session;
1303
1632
  if (!sessionId) {
@@ -1421,31 +1750,43 @@ async function runCommand(argv) {
1421
1750
  const { options, rest } = parseGlobalArgs(argv);
1422
1751
  if (options.version) {
1423
1752
  printVersion();
1424
- return;
1753
+ return 0;
1425
1754
  }
1426
1755
  if (options.help || rest.length === 0) {
1427
1756
  printHelp();
1428
- return;
1757
+ return 0;
1429
1758
  }
1430
1759
  const config = parseConfig(options.configPath);
1431
- const client = new import_r5d_api.R5dctlClient(resolveClientOptions(options, config));
1760
+ const clientOptions = resolveClientOptions(options, config);
1761
+ const client = new import_r5d_api.R5dctlClient(clientOptions);
1432
1762
  const execution = resolveCommandExecution(options, rest);
1433
1763
  if (execution.kind === "auth-login") {
1434
1764
  await handleAuthLogin(client, options, execution.commandArgs, config);
1435
- return;
1765
+ return 0;
1436
1766
  }
1437
1767
  if (execution.kind === "auth-api-key-create") {
1438
1768
  await handleAuthApiKeyCreate(client, options, config, execution.commandArgs);
1439
- return;
1769
+ return 0;
1440
1770
  }
1441
1771
  if (execution.kind === "cli-help") {
1442
1772
  process.stdout.write(execution.text);
1443
- return;
1773
+ return 0;
1774
+ }
1775
+ if (execution.kind === "shell") {
1776
+ const project = await client.projects.describe(options.project);
1777
+ return await (0, import_shell.runR5dctlShell)({
1778
+ baseUrl: clientOptions.baseUrl ?? "https://r5d.dev",
1779
+ credential: clientOptions.token ?? clientOptions.apiKey ?? "",
1780
+ projectId: project.id,
1781
+ branchName: options.branch,
1782
+ command: execution.command
1783
+ });
1444
1784
  }
1445
1785
  await executeR5dctlCommand(client, options.json, execution.pluginArgs);
1446
1786
  if (execution.clearAuthOnSuccess) {
1447
1787
  writeConfig(options.configPath, clearAuthFromConfig(config, options));
1448
1788
  }
1789
+ return 0;
1449
1790
  }
1450
1791
  function extractApiErrorMessage(error) {
1451
1792
  if (typeof error.body === "object" && error.body !== null) {
@@ -1458,8 +1799,7 @@ function extractApiErrorMessage(error) {
1458
1799
  }
1459
1800
  async function runR5dctlCli(argv) {
1460
1801
  try {
1461
- await runCommand(argv);
1462
- return 0;
1802
+ return await runCommand(argv);
1463
1803
  } catch (error) {
1464
1804
  if (error instanceof import_r5d_api.R5dctlApiError) {
1465
1805
  process.stderr.write(`${extractApiErrorMessage(error)}
@@ -1474,7 +1814,7 @@ async function runR5dctlCli(argv) {
1474
1814
  async function main(argv = process.argv.slice(2)) {
1475
1815
  const exitCode = await runR5dctlCli(argv);
1476
1816
  if (exitCode !== 0) {
1477
- process.exit(exitCode);
1817
+ process.exitCode = exitCode;
1478
1818
  }
1479
1819
  }
1480
1820
  // Annotate the CommonJS export names for ESM import in node:
@@ -1487,7 +1827,11 @@ async function main(argv = process.argv.slice(2)) {
1487
1827
  parseEnvFlags,
1488
1828
  parseGlobalArgs,
1489
1829
  parsePromptArgs,
1830
+ parseSetEnvArgs,
1831
+ parseShellArgs,
1832
+ readDotenvFile,
1490
1833
  renderConversationResponse,
1491
1834
  resolveCommandExecution,
1492
- runR5dctlCli
1835
+ runR5dctlCli,
1836
+ summarizeEnvData
1493
1837
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "type": "commonjs"
5
5
  }