@ricsam/r5dctl 0.0.23 → 0.0.24

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
@@ -51,7 +51,7 @@ r5dctl -p <project> get branches
51
51
  r5dctl -p <project> describe branch <branch>
52
52
  r5dctl -p <project> -b <branch> get envs
53
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"
54
+ r5dctl -p <project> -b <branch> set envs -e API_KEY=value --description "API credential"
55
55
  r5dctl -p <project> get sessions --branch <branch>
56
56
  r5dctl -p <project> create session -b <branch> --name "Spec session"
57
57
  r5dctl describe session <session-id>
@@ -65,15 +65,22 @@ r5dctl -s <session-id> conversation --system # include system prompt m
65
65
  r5dctl -s <session-id> conversation --raw --tools --system # exact raw agent request transcript
66
66
  r5dctl -s <session-id> prompt --mode plan --model max "..."
67
67
  r5dctl -s <session-id> answer-questions -a1 "Recipe Collection" -a2 "Both Manual & AI"
68
- r5dctl -s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value
68
+ r5dctl -s <session-id> apply-required-envs -e KEY=value
69
69
 
70
70
  r5dctl -p <project> -b <branch> shell
71
71
  r5dctl -p <project> -b <branch> shell -c "bun test"
72
+
73
+ r5dctl ps list
74
+ r5dctl -p <project> -b <branch> ps list --worker <label>
75
+ r5dctl -s <session-id> ps list --json
76
+ r5dctl ps stop <run-id>
72
77
  ```
73
78
 
74
79
  `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
80
 
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.
81
+ `ps list` shows active long-running commands managed by r5d. It does not expose the worker host's general OS process table or interactive PTYs. Global project, branch, and session options filter the list, while `--worker` selects a worker label. `ps stop` sends `SIGTERM` to the selected run. Inside an r5d-provided worker shell, the temporary credential can only list and stop runs on that shell's worker.
82
+
83
+ `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. Empty dotenv values are skipped unless `--include-empty` is passed, and `--only-missing` preserves values that are already set.
77
84
 
78
85
  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.
79
86
 
package/dist/cjs/cli.cjs CHANGED
@@ -28,6 +28,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var cli_exports = {};
30
30
  __export(cli_exports, {
31
+ formatProcessAge: () => formatProcessAge,
31
32
  getCliHelpText: () => getCliHelpText,
32
33
  getR5dctlVersion: () => getR5dctlVersion,
33
34
  main: () => main,
@@ -36,10 +37,12 @@ __export(cli_exports, {
36
37
  parseEnvFlags: () => parseEnvFlags,
37
38
  parseGlobalArgs: () => parseGlobalArgs,
38
39
  parsePromptArgs: () => parsePromptArgs,
40
+ parsePsListArgs: () => parsePsListArgs,
39
41
  parseSetEnvArgs: () => parseSetEnvArgs,
40
42
  parseShellArgs: () => parseShellArgs,
41
43
  readDotenvFile: () => readDotenvFile,
42
44
  renderConversationResponse: () => renderConversationResponse,
45
+ renderProcessList: () => renderProcessList,
43
46
  resolveCommandExecution: () => resolveCommandExecution,
44
47
  runR5dctlCli: () => runR5dctlCli,
45
48
  summarizeEnvData: () => summarizeEnvData
@@ -102,6 +105,15 @@ const CLI_ONLY_COMMAND_HELP = [
102
105
  usage: "-p <project> -b <branch> shell [-c <command>]"
103
106
  }
104
107
  ];
108
+ const PS_HELP_TEXT = [
109
+ "Usage:",
110
+ " r5dctl ps list [--worker <label>]",
111
+ " r5dctl ps stop <run-id>",
112
+ "",
113
+ "List or gracefully stop active r5d-managed worker processes.",
114
+ "Use -p/--project, -b/--branch, or -s/--session to filter ps list.",
115
+ ""
116
+ ].join("\n");
105
117
  const SHARED_HELP_ENTRIES = [
106
118
  { section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
107
119
  { section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
@@ -125,12 +137,12 @@ const SHARED_HELP_ENTRIES = [
125
137
  },
126
138
  {
127
139
  section: "envs",
128
- usage: "-p <project> -b <branch> set envs --from-env-file <path> [--target <backend|frontend>] [--only-missing]",
140
+ usage: "-p <project> -b <branch> set envs --from-env-file <path> [--only-missing]",
129
141
  description: "Securely import branch envs from a dotenv file without putting values in command arguments."
130
142
  },
131
143
  {
132
144
  section: "envs",
133
- usage: "-p <project> -b <branch> set envs -e backend/KEY=value [--description <text>] [--only-missing]",
145
+ usage: "-p <project> -b <branch> set envs -e KEY=value [--description <text>] [--only-missing]",
134
146
  description: "Set one or more branch envs directly."
135
147
  },
136
148
  {
@@ -163,9 +175,15 @@ const SHARED_HELP_ENTRIES = [
163
175
  },
164
176
  {
165
177
  section: "sessions",
166
- usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value",
167
- description: "Apply required backend or frontend env values."
178
+ usage: "-s <session-id> apply-required-envs -e KEY=value",
179
+ description: "Apply required environment variable values."
168
180
  },
181
+ {
182
+ section: "processes",
183
+ usage: "ps list [--worker <label>]",
184
+ description: "List active r5d-managed worker processes; global project, branch, and session filters apply."
185
+ },
186
+ { section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." },
169
187
  {
170
188
  section: "agents",
171
189
  usage: '-p <project> start-agent <src-branch> <agent-type> "<prompt>"',
@@ -181,13 +199,24 @@ const SHARED_HELP_ENTRIES = [
181
199
  { section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
182
200
  { section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
183
201
  ];
184
- const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "shell", "agents", "merges"];
202
+ const HELP_SECTION_ORDER = [
203
+ "auth",
204
+ "projects",
205
+ "branches",
206
+ "envs",
207
+ "sessions",
208
+ "processes",
209
+ "shell",
210
+ "agents",
211
+ "merges"
212
+ ];
185
213
  const HELP_SECTION_TITLES = {
186
214
  auth: "Auth",
187
215
  projects: "Projects",
188
216
  branches: "Branches",
189
217
  envs: "Environment variables",
190
218
  sessions: "Sessions",
219
+ processes: "Processes",
191
220
  shell: "Shell",
192
221
  agents: "Agents",
193
222
  merges: "Merges"
@@ -288,7 +317,7 @@ function renderCliOnlyCommandHelp(entry) {
288
317
  }
289
318
  function renderSharedCommandHelp(pathSegments) {
290
319
  const entry = SHARED_HELP_ENTRIES.find((candidate) => {
291
- const usageSegments = candidate.usage.split(" ").filter((segment) => !segment.startsWith("-") && !segment.startsWith("<") && !segment.startsWith("["));
320
+ const usageSegments = candidate.usage.split(" ").map((segment) => segment.replace(/^\[/, "").replace(/\]$/, "")).filter((segment) => !segment.startsWith("-") && !segment.startsWith("<") && !segment.startsWith("["));
292
321
  return usageSegments.length <= pathSegments.length && usageSegments.every((segment, index) => segment === pathSegments[index]);
293
322
  });
294
323
  if (!entry) {
@@ -524,33 +553,24 @@ function parseAnswerFlags(args) {
524
553
  function validateEnvAssignment(value) {
525
554
  const equalsIndex = value.indexOf("=");
526
555
  if (equalsIndex <= 0) {
527
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
528
- }
529
- const assignment = value.slice(0, equalsIndex);
530
- const slashIndex = assignment.indexOf("/");
531
- if (slashIndex <= 0) {
532
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
556
+ throw new Error(`Invalid env assignment '${value}'. Expected KEY=value`);
533
557
  }
534
- const target = assignment.slice(0, slashIndex);
535
- const key = assignment.slice(slashIndex + 1);
536
- if (target !== "backend" && target !== "frontend" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
537
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
558
+ const key = value.slice(0, equalsIndex);
559
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
560
+ throw new Error(`Invalid env assignment '${value}'. Expected KEY=value`);
538
561
  }
539
562
  }
540
563
  function parseEnvAssignment(value) {
541
564
  validateEnvAssignment(value);
542
565
  const equalsIndex = value.indexOf("=");
543
- const slashIndex = value.indexOf("/");
544
566
  return {
545
- target: value.slice(0, slashIndex),
546
- name: value.slice(slashIndex + 1, equalsIndex),
567
+ name: value.slice(0, equalsIndex),
547
568
  value: value.slice(equalsIndex + 1)
548
569
  };
549
570
  }
550
571
  function parseSetEnvArgs(args) {
551
572
  const parsed = {
552
573
  assignments: [],
553
- target: "backend",
554
574
  onlyMissing: false,
555
575
  includeEmpty: false
556
576
  };
@@ -580,23 +600,6 @@ function parseSetEnvArgs(args) {
580
600
  parsed.fromEnvFile = requireValue(inlineFile, "Missing value for --from-env-file");
581
601
  continue;
582
602
  }
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
603
  if (arg === "--description") {
601
604
  parsed.description = requireValue(args[index + 1], "Missing value for --description");
602
605
  index += 1;
@@ -670,7 +673,7 @@ function parseEnvFlags(args) {
670
673
  if (arg.startsWith("-")) {
671
674
  throw new Error(`Unknown env flag: ${arg}`);
672
675
  }
673
- throw new Error(`Unexpected value '${arg}'. Use -e backend/KEY=value.`);
676
+ throw new Error(`Unexpected value '${arg}'. Use -e KEY=value.`);
674
677
  }
675
678
  if (envs.length === 0) {
676
679
  throw new Error("At least one -e/--env value is required.");
@@ -764,6 +767,40 @@ function parseShellArgs(args) {
764
767
  }
765
768
  return command === void 0 ? {} : { command };
766
769
  }
770
+ function parsePsListArgs(args) {
771
+ const input = {};
772
+ const definitions = [
773
+ { flag: "--project", key: "project" },
774
+ { flag: "--branch", key: "branch" },
775
+ { flag: "--session", key: "session" },
776
+ { flag: "--worker", key: "worker" }
777
+ ];
778
+ const assign = (key, value, flag) => {
779
+ if (input[key] !== void 0) {
780
+ throw new Error(`Use only one ${flag} value`);
781
+ }
782
+ input[key] = requireValue(value, `Missing value for ${flag}`);
783
+ };
784
+ for (let index = 0; index < args.length; index += 1) {
785
+ const arg = args[index];
786
+ if (!arg) continue;
787
+ const definition = definitions.find(({ flag }) => arg === flag || arg.startsWith(`${flag}=`));
788
+ if (!definition) {
789
+ throw new Error(arg.startsWith("-") ? `Unknown ps list flag: ${arg}` : `Unexpected ps list value: ${arg}`);
790
+ }
791
+ if (arg === definition.flag) {
792
+ assign(definition.key, requireValue(args[index + 1], `Missing value for ${definition.flag}`), definition.flag);
793
+ index += 1;
794
+ continue;
795
+ }
796
+ assign(
797
+ definition.key,
798
+ requireValue(parseLongOptionWithEquals(arg, definition.flag), `Missing value for ${definition.flag}`),
799
+ definition.flag
800
+ );
801
+ }
802
+ return input;
803
+ }
767
804
  async function openInBrowser(url) {
768
805
  if (process.platform === "darwin") {
769
806
  (0, import_node_child_process.spawn)("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -905,34 +942,66 @@ function renderSessionList(sessions) {
905
942
  function renderSessionDescription(session) {
906
943
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
907
944
  }
945
+ function formatProcessAge(startedAt, nowMs = Date.now()) {
946
+ const startedAtMs = Date.parse(startedAt);
947
+ if (!Number.isFinite(startedAtMs)) {
948
+ return "unknown";
949
+ }
950
+ const totalSeconds = Math.max(0, Math.floor((nowMs - startedAtMs) / 1e3));
951
+ if (totalSeconds < 60) {
952
+ return `${totalSeconds}s`;
953
+ }
954
+ if (totalSeconds < 60 * 60) {
955
+ const minutes = Math.floor(totalSeconds / 60);
956
+ const seconds = totalSeconds % 60;
957
+ return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
958
+ }
959
+ if (totalSeconds < 24 * 60 * 60) {
960
+ const hours2 = Math.floor(totalSeconds / (60 * 60));
961
+ const minutes = Math.floor(totalSeconds % (60 * 60) / 60);
962
+ return `${hours2}h ${String(minutes).padStart(2, "0")}m`;
963
+ }
964
+ const days = Math.floor(totalSeconds / (24 * 60 * 60));
965
+ const hours = Math.floor(totalSeconds % (24 * 60 * 60) / (60 * 60));
966
+ return `${days}d ${hours}h`;
967
+ }
968
+ function renderProcessList(processes, nowMs = Date.now()) {
969
+ if (processes.length === 0) {
970
+ return "No active processes.\n";
971
+ }
972
+ const headers = ["RUN ID", "STATUS", "AGE", "WORKER", "PROJECT", "BRANCH", "SESSION", "COMMAND"];
973
+ const rows = processes.map((process2) => [
974
+ process2.runId,
975
+ process2.status,
976
+ formatProcessAge(process2.startedAt, nowMs),
977
+ process2.workerLabel,
978
+ process2.projectPath,
979
+ process2.branchName,
980
+ process2.sessionId,
981
+ process2.command
982
+ ]);
983
+ const widths = headers.map((header, column) => Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0)));
984
+ const formatRow = (row) => row.map((value, column) => column === row.length - 1 ? value : value.padEnd(widths[column] ?? value.length)).join(" ");
985
+ const separator = widths.map((width) => "-".repeat(width)).join(" ");
986
+ return `${[formatRow(headers), separator, ...rows.map(formatRow)].join("\n")}
987
+ `;
988
+ }
908
989
  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;
990
+ return Object.fromEntries(
991
+ Object.entries(data).sort(([left], [right]) => left.localeCompare(right)).map(([name, env]) => [
992
+ name,
993
+ {
994
+ optional: env.optional ?? false,
995
+ description: env.description,
996
+ hasValue: env.value !== null
997
+ }
998
+ ])
999
+ );
925
1000
  }
926
1001
  function renderEnvSummary(data) {
927
1002
  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
- }
1003
+ for (const [name, env] of Object.entries(data)) {
1004
+ lines.push(`${name} ${env.hasValue ? "set" : "missing"} ${env.optional ? "optional" : "required"} ${env.description}`);
936
1005
  }
937
1006
  return lines.length > 0 ? `${lines.join("\n")}
938
1007
  ` : "No environment variables declared.\n";
@@ -944,7 +1013,7 @@ function renderEnvSetResult(result) {
944
1013
  if (result.skippedExisting.length > 0) lines.push(`Skipped ${result.skippedExisting.length} existing value(s).`);
945
1014
  if (result.skippedEmpty.length > 0) lines.push(`Skipped ${result.skippedEmpty.length} empty value(s).`);
946
1015
  if (result.updated.length > 0) {
947
- lines.push(...result.updated.map(({ target, name }) => ` ${target}/${name}`));
1016
+ lines.push(...result.updated.map(({ name }) => ` ${name}`));
948
1017
  }
949
1018
  return `${lines.join("\n")}
950
1019
  `;
@@ -957,10 +1026,10 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
957
1026
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
958
1027
  throw new Error(`Invalid environment variable name in dotenv file: ${name}`);
959
1028
  }
960
- return { target: options.target, name, value };
1029
+ return { name, value };
961
1030
  }) : options.assignments.map(parseEnvAssignment);
962
1031
  const uniqueAssignments = /* @__PURE__ */ new Map();
963
- for (const assignment of assignments) uniqueAssignments.set(`${assignment.target}/${assignment.name}`, assignment);
1032
+ for (const assignment of assignments) uniqueAssignments.set(assignment.name, assignment);
964
1033
  const update = {};
965
1034
  const result = {
966
1035
  success: true,
@@ -973,12 +1042,12 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
973
1042
  const missingDescriptions = [];
974
1043
  const importedDescription = options.fromEnvFile ? `Imported from ${import_node_path.default.basename(options.fromEnvFile)}.` : void 0;
975
1044
  for (const assignment of uniqueAssignments.values()) {
976
- const identity = { target: assignment.target, name: assignment.name };
1045
+ const identity = { name: assignment.name };
977
1046
  if (!options.includeEmpty && assignment.value.length === 0) {
978
1047
  result.skippedEmpty.push(identity);
979
1048
  continue;
980
1049
  }
981
- const existing = current[assignment.target]?.[assignment.name];
1050
+ const existing = current[assignment.name];
982
1051
  if (options.onlyMissing && existing?.value !== null && existing?.value !== void 0) {
983
1052
  result.skippedExisting.push(identity);
984
1053
  continue;
@@ -988,9 +1057,8 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
988
1057
  missingDescriptions.push(identity);
989
1058
  continue;
990
1059
  }
991
- const targetUpdate = update[assignment.target] ??= {};
992
1060
  const optional = options.optional ?? existing?.optional;
993
- targetUpdate[assignment.name] = {
1061
+ update[assignment.name] = {
994
1062
  description,
995
1063
  value: assignment.value,
996
1064
  ...optional === void 0 ? {} : { optional }
@@ -998,9 +1066,7 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
998
1066
  result.updated.push(identity);
999
1067
  }
1000
1068
  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
- );
1069
+ throw new Error(`--description is required when declaring new envs with -e: ${missingDescriptions.map(({ name }) => name).join(", ")}`);
1004
1070
  }
1005
1071
  if (result.updated.length > 0) {
1006
1072
  await client.projects.env.update(project.id, branchName, update);
@@ -1165,6 +1231,17 @@ async function executeR5dctlCommand(client, json, args) {
1165
1231
  write(result, "API key revoked.\n");
1166
1232
  return;
1167
1233
  }
1234
+ if (first === "ps" && second === "list") {
1235
+ const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
1236
+ write(processes, renderProcessList(processes));
1237
+ return;
1238
+ }
1239
+ if (first === "ps" && second === "stop") {
1240
+ const process2 = await client.processes.stop(requireValue(args[2], "Missing process run id"));
1241
+ write(process2, `Stopped process ${process2.runId}: ${process2.command}
1242
+ `);
1243
+ return;
1244
+ }
1168
1245
  if (first === "projects" && second === "list" || first === "get" && second === "projects") {
1169
1246
  const projects = await client.projects.list();
1170
1247
  write(projects, renderProjectList(projects));
@@ -1360,6 +1437,12 @@ function resolveCommandExecution(options, rest) {
1360
1437
  throw new Error("Missing command");
1361
1438
  }
1362
1439
  const commandArgs = normalizedRest.slice(1);
1440
+ if (command === "ps" && commandArgs.length === 0) {
1441
+ return {
1442
+ kind: "cli-help",
1443
+ text: PS_HELP_TEXT
1444
+ };
1445
+ }
1363
1446
  if (trailingHelp) {
1364
1447
  const cliOnlyHelp = findCliOnlyCommandHelp(normalizedRest);
1365
1448
  if (cliOnlyHelp) {
@@ -1433,6 +1516,36 @@ function resolveCommandExecution(options, rest) {
1433
1516
  }
1434
1517
  throw new Error("Unknown auth command");
1435
1518
  }
1519
+ if (command === "ps") {
1520
+ const subcommand = commandArgs[0];
1521
+ if (subcommand === "list") {
1522
+ const commandFilters = parsePsListArgs(commandArgs.slice(1));
1523
+ const filters = {
1524
+ ...commandFilters,
1525
+ ...options.project ? { project: options.project } : {},
1526
+ ...options.branch ? { branch: options.branch } : {},
1527
+ ...options.session ? { session: options.session } : {}
1528
+ };
1529
+ const pluginArgs = ["ps", "list"];
1530
+ for (const [flag, value] of [
1531
+ ["--project", filters.project],
1532
+ ["--branch", filters.branch],
1533
+ ["--session", filters.session],
1534
+ ["--worker", filters.worker]
1535
+ ]) {
1536
+ if (value) pluginArgs.push(flag, value);
1537
+ }
1538
+ return { kind: "plugin", pluginArgs };
1539
+ }
1540
+ if (subcommand === "stop") {
1541
+ const runId = requireValue(commandArgs[1], "Missing process run id");
1542
+ if (commandArgs.length > 2) {
1543
+ throw new Error(`Unexpected ps stop value: ${commandArgs[2]}`);
1544
+ }
1545
+ return { kind: "plugin", pluginArgs: ["ps", "stop", runId] };
1546
+ }
1547
+ throw new Error(`Unknown ps command: ${subcommand ?? ""}`.trim());
1548
+ }
1436
1549
  if (command === "get") {
1437
1550
  const target = commandArgs[0];
1438
1551
  if (target === "projects") {
@@ -1819,6 +1932,7 @@ async function main(argv = process.argv.slice(2)) {
1819
1932
  }
1820
1933
  // Annotate the CommonJS export names for ESM import in node:
1821
1934
  0 && (module.exports = {
1935
+ formatProcessAge,
1822
1936
  getCliHelpText,
1823
1937
  getR5dctlVersion,
1824
1938
  main,
@@ -1827,10 +1941,12 @@ async function main(argv = process.argv.slice(2)) {
1827
1941
  parseEnvFlags,
1828
1942
  parseGlobalArgs,
1829
1943
  parsePromptArgs,
1944
+ parsePsListArgs,
1830
1945
  parseSetEnvArgs,
1831
1946
  parseShellArgs,
1832
1947
  readDotenvFile,
1833
1948
  renderConversationResponse,
1949
+ renderProcessList,
1834
1950
  resolveCommandExecution,
1835
1951
  runR5dctlCli,
1836
1952
  summarizeEnvData
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -58,6 +58,15 @@ const CLI_ONLY_COMMAND_HELP = [
58
58
  usage: "-p <project> -b <branch> shell [-c <command>]"
59
59
  }
60
60
  ];
61
+ const PS_HELP_TEXT = [
62
+ "Usage:",
63
+ " r5dctl ps list [--worker <label>]",
64
+ " r5dctl ps stop <run-id>",
65
+ "",
66
+ "List or gracefully stop active r5d-managed worker processes.",
67
+ "Use -p/--project, -b/--branch, or -s/--session to filter ps list.",
68
+ ""
69
+ ].join("\n");
61
70
  const SHARED_HELP_ENTRIES = [
62
71
  { section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
63
72
  { section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
@@ -81,12 +90,12 @@ const SHARED_HELP_ENTRIES = [
81
90
  },
82
91
  {
83
92
  section: "envs",
84
- usage: "-p <project> -b <branch> set envs --from-env-file <path> [--target <backend|frontend>] [--only-missing]",
93
+ usage: "-p <project> -b <branch> set envs --from-env-file <path> [--only-missing]",
85
94
  description: "Securely import branch envs from a dotenv file without putting values in command arguments."
86
95
  },
87
96
  {
88
97
  section: "envs",
89
- usage: "-p <project> -b <branch> set envs -e backend/KEY=value [--description <text>] [--only-missing]",
98
+ usage: "-p <project> -b <branch> set envs -e KEY=value [--description <text>] [--only-missing]",
90
99
  description: "Set one or more branch envs directly."
91
100
  },
92
101
  {
@@ -119,9 +128,15 @@ const SHARED_HELP_ENTRIES = [
119
128
  },
120
129
  {
121
130
  section: "sessions",
122
- usage: "-s <session-id> apply-required-envs -e backend/KEY=value -e frontend/KEY=value",
123
- description: "Apply required backend or frontend env values."
131
+ usage: "-s <session-id> apply-required-envs -e KEY=value",
132
+ description: "Apply required environment variable values."
124
133
  },
134
+ {
135
+ section: "processes",
136
+ usage: "ps list [--worker <label>]",
137
+ description: "List active r5d-managed worker processes; global project, branch, and session filters apply."
138
+ },
139
+ { section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." },
125
140
  {
126
141
  section: "agents",
127
142
  usage: '-p <project> start-agent <src-branch> <agent-type> "<prompt>"',
@@ -137,13 +152,24 @@ const SHARED_HELP_ENTRIES = [
137
152
  { section: "merges", usage: "-p <project> continue-merge <target-branch>", description: "Commit a resolved merge." },
138
153
  { section: "merges", usage: "-p <project> abort-merge <target-branch>", description: "Abort an in-progress merge." }
139
154
  ];
140
- const HELP_SECTION_ORDER = ["auth", "projects", "branches", "envs", "sessions", "shell", "agents", "merges"];
155
+ const HELP_SECTION_ORDER = [
156
+ "auth",
157
+ "projects",
158
+ "branches",
159
+ "envs",
160
+ "sessions",
161
+ "processes",
162
+ "shell",
163
+ "agents",
164
+ "merges"
165
+ ];
141
166
  const HELP_SECTION_TITLES = {
142
167
  auth: "Auth",
143
168
  projects: "Projects",
144
169
  branches: "Branches",
145
170
  envs: "Environment variables",
146
171
  sessions: "Sessions",
172
+ processes: "Processes",
147
173
  shell: "Shell",
148
174
  agents: "Agents",
149
175
  merges: "Merges"
@@ -244,7 +270,7 @@ function renderCliOnlyCommandHelp(entry) {
244
270
  }
245
271
  function renderSharedCommandHelp(pathSegments) {
246
272
  const entry = SHARED_HELP_ENTRIES.find((candidate) => {
247
- const usageSegments = candidate.usage.split(" ").filter((segment) => !segment.startsWith("-") && !segment.startsWith("<") && !segment.startsWith("["));
273
+ const usageSegments = candidate.usage.split(" ").map((segment) => segment.replace(/^\[/, "").replace(/\]$/, "")).filter((segment) => !segment.startsWith("-") && !segment.startsWith("<") && !segment.startsWith("["));
248
274
  return usageSegments.length <= pathSegments.length && usageSegments.every((segment, index) => segment === pathSegments[index]);
249
275
  });
250
276
  if (!entry) {
@@ -480,33 +506,24 @@ function parseAnswerFlags(args) {
480
506
  function validateEnvAssignment(value) {
481
507
  const equalsIndex = value.indexOf("=");
482
508
  if (equalsIndex <= 0) {
483
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
484
- }
485
- const assignment = value.slice(0, equalsIndex);
486
- const slashIndex = assignment.indexOf("/");
487
- if (slashIndex <= 0) {
488
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
509
+ throw new Error(`Invalid env assignment '${value}'. Expected KEY=value`);
489
510
  }
490
- const target = assignment.slice(0, slashIndex);
491
- const key = assignment.slice(slashIndex + 1);
492
- if (target !== "backend" && target !== "frontend" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
493
- throw new Error(`Invalid env assignment '${value}'. Expected backend/KEY=value or frontend/KEY=value`);
511
+ const key = value.slice(0, equalsIndex);
512
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
513
+ throw new Error(`Invalid env assignment '${value}'. Expected KEY=value`);
494
514
  }
495
515
  }
496
516
  function parseEnvAssignment(value) {
497
517
  validateEnvAssignment(value);
498
518
  const equalsIndex = value.indexOf("=");
499
- const slashIndex = value.indexOf("/");
500
519
  return {
501
- target: value.slice(0, slashIndex),
502
- name: value.slice(slashIndex + 1, equalsIndex),
520
+ name: value.slice(0, equalsIndex),
503
521
  value: value.slice(equalsIndex + 1)
504
522
  };
505
523
  }
506
524
  function parseSetEnvArgs(args) {
507
525
  const parsed = {
508
526
  assignments: [],
509
- target: "backend",
510
527
  onlyMissing: false,
511
528
  includeEmpty: false
512
529
  };
@@ -536,23 +553,6 @@ function parseSetEnvArgs(args) {
536
553
  parsed.fromEnvFile = requireValue(inlineFile, "Missing value for --from-env-file");
537
554
  continue;
538
555
  }
539
- if (arg === "--target") {
540
- const target = requireValue(args[index + 1], "Missing value for --target");
541
- if (target !== "backend" && target !== "frontend") {
542
- throw new Error("Invalid --target. Expected backend or frontend");
543
- }
544
- parsed.target = target;
545
- index += 1;
546
- continue;
547
- }
548
- const inlineTarget = parseLongOptionWithEquals(arg, "--target");
549
- if (inlineTarget !== void 0) {
550
- if (inlineTarget !== "backend" && inlineTarget !== "frontend") {
551
- throw new Error("Invalid --target. Expected backend or frontend");
552
- }
553
- parsed.target = inlineTarget;
554
- continue;
555
- }
556
556
  if (arg === "--description") {
557
557
  parsed.description = requireValue(args[index + 1], "Missing value for --description");
558
558
  index += 1;
@@ -626,7 +626,7 @@ function parseEnvFlags(args) {
626
626
  if (arg.startsWith("-")) {
627
627
  throw new Error(`Unknown env flag: ${arg}`);
628
628
  }
629
- throw new Error(`Unexpected value '${arg}'. Use -e backend/KEY=value.`);
629
+ throw new Error(`Unexpected value '${arg}'. Use -e KEY=value.`);
630
630
  }
631
631
  if (envs.length === 0) {
632
632
  throw new Error("At least one -e/--env value is required.");
@@ -720,6 +720,40 @@ function parseShellArgs(args) {
720
720
  }
721
721
  return command === void 0 ? {} : { command };
722
722
  }
723
+ function parsePsListArgs(args) {
724
+ const input = {};
725
+ const definitions = [
726
+ { flag: "--project", key: "project" },
727
+ { flag: "--branch", key: "branch" },
728
+ { flag: "--session", key: "session" },
729
+ { flag: "--worker", key: "worker" }
730
+ ];
731
+ const assign = (key, value, flag) => {
732
+ if (input[key] !== void 0) {
733
+ throw new Error(`Use only one ${flag} value`);
734
+ }
735
+ input[key] = requireValue(value, `Missing value for ${flag}`);
736
+ };
737
+ for (let index = 0; index < args.length; index += 1) {
738
+ const arg = args[index];
739
+ if (!arg) continue;
740
+ const definition = definitions.find(({ flag }) => arg === flag || arg.startsWith(`${flag}=`));
741
+ if (!definition) {
742
+ throw new Error(arg.startsWith("-") ? `Unknown ps list flag: ${arg}` : `Unexpected ps list value: ${arg}`);
743
+ }
744
+ if (arg === definition.flag) {
745
+ assign(definition.key, requireValue(args[index + 1], `Missing value for ${definition.flag}`), definition.flag);
746
+ index += 1;
747
+ continue;
748
+ }
749
+ assign(
750
+ definition.key,
751
+ requireValue(parseLongOptionWithEquals(arg, definition.flag), `Missing value for ${definition.flag}`),
752
+ definition.flag
753
+ );
754
+ }
755
+ return input;
756
+ }
723
757
  async function openInBrowser(url) {
724
758
  if (process.platform === "darwin") {
725
759
  spawn("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -861,34 +895,66 @@ function renderSessionList(sessions) {
861
895
  function renderSessionDescription(session) {
862
896
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
863
897
  }
898
+ function formatProcessAge(startedAt, nowMs = Date.now()) {
899
+ const startedAtMs = Date.parse(startedAt);
900
+ if (!Number.isFinite(startedAtMs)) {
901
+ return "unknown";
902
+ }
903
+ const totalSeconds = Math.max(0, Math.floor((nowMs - startedAtMs) / 1e3));
904
+ if (totalSeconds < 60) {
905
+ return `${totalSeconds}s`;
906
+ }
907
+ if (totalSeconds < 60 * 60) {
908
+ const minutes = Math.floor(totalSeconds / 60);
909
+ const seconds = totalSeconds % 60;
910
+ return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
911
+ }
912
+ if (totalSeconds < 24 * 60 * 60) {
913
+ const hours2 = Math.floor(totalSeconds / (60 * 60));
914
+ const minutes = Math.floor(totalSeconds % (60 * 60) / 60);
915
+ return `${hours2}h ${String(minutes).padStart(2, "0")}m`;
916
+ }
917
+ const days = Math.floor(totalSeconds / (24 * 60 * 60));
918
+ const hours = Math.floor(totalSeconds % (24 * 60 * 60) / (60 * 60));
919
+ return `${days}d ${hours}h`;
920
+ }
921
+ function renderProcessList(processes, nowMs = Date.now()) {
922
+ if (processes.length === 0) {
923
+ return "No active processes.\n";
924
+ }
925
+ const headers = ["RUN ID", "STATUS", "AGE", "WORKER", "PROJECT", "BRANCH", "SESSION", "COMMAND"];
926
+ const rows = processes.map((process2) => [
927
+ process2.runId,
928
+ process2.status,
929
+ formatProcessAge(process2.startedAt, nowMs),
930
+ process2.workerLabel,
931
+ process2.projectPath,
932
+ process2.branchName,
933
+ process2.sessionId,
934
+ process2.command
935
+ ]);
936
+ const widths = headers.map((header, column) => Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0)));
937
+ const formatRow = (row) => row.map((value, column) => column === row.length - 1 ? value : value.padEnd(widths[column] ?? value.length)).join(" ");
938
+ const separator = widths.map((width) => "-".repeat(width)).join(" ");
939
+ return `${[formatRow(headers), separator, ...rows.map(formatRow)].join("\n")}
940
+ `;
941
+ }
864
942
  function summarizeEnvData(data) {
865
- const summary = {};
866
- for (const target of ["backend", "frontend"]) {
867
- const entries = data[target];
868
- if (!entries) continue;
869
- summary[target] = Object.fromEntries(
870
- Object.entries(entries).sort(([left], [right]) => left.localeCompare(right)).map(([name, env]) => [
871
- name,
872
- {
873
- optional: env.optional ?? false,
874
- description: env.description,
875
- hasValue: env.value !== null
876
- }
877
- ])
878
- );
879
- }
880
- return summary;
943
+ return Object.fromEntries(
944
+ Object.entries(data).sort(([left], [right]) => left.localeCompare(right)).map(([name, env]) => [
945
+ name,
946
+ {
947
+ optional: env.optional ?? false,
948
+ description: env.description,
949
+ hasValue: env.value !== null
950
+ }
951
+ ])
952
+ );
881
953
  }
882
954
  function renderEnvSummary(data) {
883
955
  const lines = [];
884
- for (const target of ["backend", "frontend"]) {
885
- const entries = Object.entries(data[target] ?? {});
886
- if (entries.length === 0) continue;
887
- if (lines.length > 0) lines.push("");
888
- lines.push(`${target}:`);
889
- for (const [name, env] of entries) {
890
- lines.push(` ${name} ${env.hasValue ? "set" : "missing"} ${env.optional ? "optional" : "required"} ${env.description}`);
891
- }
956
+ for (const [name, env] of Object.entries(data)) {
957
+ lines.push(`${name} ${env.hasValue ? "set" : "missing"} ${env.optional ? "optional" : "required"} ${env.description}`);
892
958
  }
893
959
  return lines.length > 0 ? `${lines.join("\n")}
894
960
  ` : "No environment variables declared.\n";
@@ -900,7 +966,7 @@ function renderEnvSetResult(result) {
900
966
  if (result.skippedExisting.length > 0) lines.push(`Skipped ${result.skippedExisting.length} existing value(s).`);
901
967
  if (result.skippedEmpty.length > 0) lines.push(`Skipped ${result.skippedEmpty.length} empty value(s).`);
902
968
  if (result.updated.length > 0) {
903
- lines.push(...result.updated.map(({ target, name }) => ` ${target}/${name}`));
969
+ lines.push(...result.updated.map(({ name }) => ` ${name}`));
904
970
  }
905
971
  return `${lines.join("\n")}
906
972
  `;
@@ -913,10 +979,10 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
913
979
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
914
980
  throw new Error(`Invalid environment variable name in dotenv file: ${name}`);
915
981
  }
916
- return { target: options.target, name, value };
982
+ return { name, value };
917
983
  }) : options.assignments.map(parseEnvAssignment);
918
984
  const uniqueAssignments = /* @__PURE__ */ new Map();
919
- for (const assignment of assignments) uniqueAssignments.set(`${assignment.target}/${assignment.name}`, assignment);
985
+ for (const assignment of assignments) uniqueAssignments.set(assignment.name, assignment);
920
986
  const update = {};
921
987
  const result = {
922
988
  success: true,
@@ -929,12 +995,12 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
929
995
  const missingDescriptions = [];
930
996
  const importedDescription = options.fromEnvFile ? `Imported from ${path.basename(options.fromEnvFile)}.` : void 0;
931
997
  for (const assignment of uniqueAssignments.values()) {
932
- const identity = { target: assignment.target, name: assignment.name };
998
+ const identity = { name: assignment.name };
933
999
  if (!options.includeEmpty && assignment.value.length === 0) {
934
1000
  result.skippedEmpty.push(identity);
935
1001
  continue;
936
1002
  }
937
- const existing = current[assignment.target]?.[assignment.name];
1003
+ const existing = current[assignment.name];
938
1004
  if (options.onlyMissing && existing?.value !== null && existing?.value !== void 0) {
939
1005
  result.skippedExisting.push(identity);
940
1006
  continue;
@@ -944,9 +1010,8 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
944
1010
  missingDescriptions.push(identity);
945
1011
  continue;
946
1012
  }
947
- const targetUpdate = update[assignment.target] ??= {};
948
1013
  const optional = options.optional ?? existing?.optional;
949
- targetUpdate[assignment.name] = {
1014
+ update[assignment.name] = {
950
1015
  description,
951
1016
  value: assignment.value,
952
1017
  ...optional === void 0 ? {} : { optional }
@@ -954,9 +1019,7 @@ async function setBranchEnvs(client, projectRef, branchName, args) {
954
1019
  result.updated.push(identity);
955
1020
  }
956
1021
  if (missingDescriptions.length > 0) {
957
- throw new Error(
958
- `--description is required when declaring new envs with -e: ${missingDescriptions.map(({ target, name }) => `${target}/${name}`).join(", ")}`
959
- );
1022
+ throw new Error(`--description is required when declaring new envs with -e: ${missingDescriptions.map(({ name }) => name).join(", ")}`);
960
1023
  }
961
1024
  if (result.updated.length > 0) {
962
1025
  await client.projects.env.update(project.id, branchName, update);
@@ -1121,6 +1184,17 @@ async function executeR5dctlCommand(client, json, args) {
1121
1184
  write(result, "API key revoked.\n");
1122
1185
  return;
1123
1186
  }
1187
+ if (first === "ps" && second === "list") {
1188
+ const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
1189
+ write(processes, renderProcessList(processes));
1190
+ return;
1191
+ }
1192
+ if (first === "ps" && second === "stop") {
1193
+ const process2 = await client.processes.stop(requireValue(args[2], "Missing process run id"));
1194
+ write(process2, `Stopped process ${process2.runId}: ${process2.command}
1195
+ `);
1196
+ return;
1197
+ }
1124
1198
  if (first === "projects" && second === "list" || first === "get" && second === "projects") {
1125
1199
  const projects = await client.projects.list();
1126
1200
  write(projects, renderProjectList(projects));
@@ -1316,6 +1390,12 @@ function resolveCommandExecution(options, rest) {
1316
1390
  throw new Error("Missing command");
1317
1391
  }
1318
1392
  const commandArgs = normalizedRest.slice(1);
1393
+ if (command === "ps" && commandArgs.length === 0) {
1394
+ return {
1395
+ kind: "cli-help",
1396
+ text: PS_HELP_TEXT
1397
+ };
1398
+ }
1319
1399
  if (trailingHelp) {
1320
1400
  const cliOnlyHelp = findCliOnlyCommandHelp(normalizedRest);
1321
1401
  if (cliOnlyHelp) {
@@ -1389,6 +1469,36 @@ function resolveCommandExecution(options, rest) {
1389
1469
  }
1390
1470
  throw new Error("Unknown auth command");
1391
1471
  }
1472
+ if (command === "ps") {
1473
+ const subcommand = commandArgs[0];
1474
+ if (subcommand === "list") {
1475
+ const commandFilters = parsePsListArgs(commandArgs.slice(1));
1476
+ const filters = {
1477
+ ...commandFilters,
1478
+ ...options.project ? { project: options.project } : {},
1479
+ ...options.branch ? { branch: options.branch } : {},
1480
+ ...options.session ? { session: options.session } : {}
1481
+ };
1482
+ const pluginArgs = ["ps", "list"];
1483
+ for (const [flag, value] of [
1484
+ ["--project", filters.project],
1485
+ ["--branch", filters.branch],
1486
+ ["--session", filters.session],
1487
+ ["--worker", filters.worker]
1488
+ ]) {
1489
+ if (value) pluginArgs.push(flag, value);
1490
+ }
1491
+ return { kind: "plugin", pluginArgs };
1492
+ }
1493
+ if (subcommand === "stop") {
1494
+ const runId = requireValue(commandArgs[1], "Missing process run id");
1495
+ if (commandArgs.length > 2) {
1496
+ throw new Error(`Unexpected ps stop value: ${commandArgs[2]}`);
1497
+ }
1498
+ return { kind: "plugin", pluginArgs: ["ps", "stop", runId] };
1499
+ }
1500
+ throw new Error(`Unknown ps command: ${subcommand ?? ""}`.trim());
1501
+ }
1392
1502
  if (command === "get") {
1393
1503
  const target = commandArgs[0];
1394
1504
  if (target === "projects") {
@@ -1774,6 +1884,7 @@ async function main(argv = process.argv.slice(2)) {
1774
1884
  }
1775
1885
  }
1776
1886
  export {
1887
+ formatProcessAge,
1777
1888
  getCliHelpText,
1778
1889
  getR5dctlVersion,
1779
1890
  main,
@@ -1782,10 +1893,12 @@ export {
1782
1893
  parseEnvFlags,
1783
1894
  parseGlobalArgs,
1784
1895
  parsePromptArgs,
1896
+ parsePsListArgs,
1785
1897
  parseSetEnvArgs,
1786
1898
  parseShellArgs,
1787
1899
  readDotenvFile,
1788
1900
  renderConversationResponse,
1901
+ renderProcessList,
1789
1902
  resolveCommandExecution,
1790
1903
  runR5dctlCli,
1791
1904
  summarizeEnvData
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "type": "module"
5
5
  }
@@ -1,4 +1,4 @@
1
- import { type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlEnvData, type R5dctlEnvTarget, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
1
+ import { type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlEnvData, type R5dctlProcessListInput, type R5dctlProcessRun, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
2
2
  export type GlobalOptions = {
3
3
  baseUrl?: string;
4
4
  json: boolean;
@@ -44,7 +44,6 @@ export declare function parseAnswerFlags(args: string[]): string[];
44
44
  export type SetEnvOptions = {
45
45
  assignments: string[];
46
46
  fromEnvFile?: string;
47
- target: R5dctlEnvTarget;
48
47
  description?: string;
49
48
  optional?: boolean;
50
49
  onlyMissing: boolean;
@@ -61,11 +60,14 @@ export declare function parsePromptArgs(args: string[]): {
61
60
  export declare function parseShellArgs(args: string[]): {
62
61
  command?: string;
63
62
  };
64
- export type EnvSummaryData = Partial<Record<R5dctlEnvTarget, Record<string, {
63
+ export declare function parsePsListArgs(args: string[]): R5dctlProcessListInput;
64
+ export declare function formatProcessAge(startedAt: string, nowMs?: number): string;
65
+ export declare function renderProcessList(processes: R5dctlProcessRun[], nowMs?: number): string;
66
+ export type EnvSummaryData = Record<string, {
65
67
  optional: boolean;
66
68
  description: string;
67
69
  hasValue: boolean;
68
- }>>>;
70
+ }>;
69
71
  export declare function summarizeEnvData(data: R5dctlEnvData): EnvSummaryData;
70
72
  export declare function parseConversationRenderArgs(args: string[]): ConversationRenderOptions;
71
73
  export declare function renderConversationResponse(read: R5dctlConversationResponse): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -26,7 +26,7 @@
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.23",
29
+ "@ricsam/r5d-api": "^0.0.24",
30
30
  "dotenv": "^17",
31
31
  "ws": "^8.18.3"
32
32
  },