@ricsam/r5dctl 0.0.103 → 0.0.104

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
@@ -55,6 +55,10 @@ r5dctl delete project <namespace/name|id>
55
55
 
56
56
  r5dctl -p <project> get branches
57
57
  r5dctl -p <project> describe branch <branch>
58
+ r5dctl -p <project> create branch <branch> --source worktree:<branch> [--working-tree carry|clean] [--worker <label>]
59
+ r5dctl -p <project> describe branch-operation <operation-id>
60
+ r5dctl -p <project> delete branch <branch> # preview the sessions that would be deleted, exit 1
61
+ r5dctl -p <project> delete branch <branch> --confirm # delete the branch and its sessions through the platform
58
62
  r5dctl -p <project> get envs
59
63
  r5dctl -p <project> get envs --show-values
60
64
  r5dctl -p <project> get env API_KEY
@@ -119,6 +123,12 @@ repository. Trust applies to that workflow path on every Git ref and grants
119
123
  cluster-admin, so protect workflow changes and do not invoke it for untrusted
120
124
  pull requests.
121
125
 
126
+ `create branch` creates a linked worktree branch through the platform from an existing branch's checkout, with the same `--source worktree:<branch>` and `--working-tree carry|clean` semantics as `session start --new-worktree` (carry is the default) and no session prompt. `--worker <label>` selects the connected worker that cuts the checkout; omitted, the platform chooses one, and an r5d worker shell's temporary credential is always bound to its own worker. The command returns as soon as the operation is accepted and prints its id; `describe branch-operation <operation-id>` reports `provisioning`, `ready`, or `failed` with the worker and any error. Neither prints a commit hash: read the branch with git in its checkout once it is ready.
127
+
128
+ `delete branch` removes a branch and every session on it through the platform, which is the only supported way to remove a linked worktree: the platform owns those checkouts, so never run `git worktree remove`, `git worktree prune`, or `git branch -D` inside a project checkout. Without `--confirm` it prints the branch and its sessions and exits non-zero without deleting anything. The primary branch `main` is refused, as is a branch with a running agent or active process (stop them first). On a server that predates the route, the command explains that the server does not support `delete branch` yet instead of reporting a missing branch.
129
+
130
+ `get branches` lists every branch the project knows about, including managed branches without sessions. A worker whose workspace sync found a configured branch's checkout directory missing, or present but not a linked worktree of the project, skips that branch and reports it; the listing then shows `checkout=missing on <worker>` or `checkout=not_a_worktree on <worker>`, and `workspace status` prints a `Missing checkouts on <worker>` line. Recover either by deleting the branch with `delete branch <branch> --confirm` or by reconnecting that worker, which recreates the checkout without snapshotting the project's other checkouts.
131
+
122
132
  `session start` creates an ordinary agent-mode chat on an explicitly selected existing worktree, or creates a named linked worktree from an explicit `worktree:<branch>` source. A new worktree is cut from the source branch's current commit; `--working-tree carry` (the default, `git switch -c` semantics) also copies the source checkout's uncommitted changes into it, while `--working-tree clean` checks out that commit only. The new session's first message states which mode applies, and the start output prints `Source` and `Working tree` lines. A clean request fails with a clear error when the selected worker predates the option, because such a worker would silently carry instead. It requires project, worker, model, and prompt and never infers them from the current shell. Start returns a durable `provisioning` handle immediately; workspace synchronization, linked-worktree creation, and prompt enqueue continue in the background and are visible through `session status`. Idempotent request retries return the same handle. Prompts return after durable enqueue. Task specialization belongs in the prompt; there are no predefined debug, explore, research, or test agents. `session stop` returns as soon as the agent run is stopped; the stopped worktree's workspace Git synchronization finishes asynchronously behind the response.
123
133
 
124
134
  `session status` reports the run lifecycle and the launch-time `Source` and `Working tree` facts only. It deliberately prints no commit hash for the worktree, not even the launch baseline: a hash would be stale by the time a caller acts on it, because commits can land between the read and the action. Find the base and the branch's commits with git in the worker checkout instead, for example `git merge-base <your-branch> <branch>`, `git log --oneline <your-branch>..<branch>`, and `git status` in the branch folder, as the `Branch state` line reminds you. While a new worktree is still provisioning, a `Provisioning` line reports where its creation stands in the worker's operation queue and since when (`Provisioning: queued behind 3 worker operations since 2026-09-03T11:41:30Z`); a worker that predates operation receipts leaves that line out.
package/dist/cjs/cli.cjs CHANGED
@@ -29,7 +29,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  var cli_exports = {};
30
30
  __export(cli_exports, {
31
31
  advanceTransientDevicePollBackoff: () => advanceTransientDevicePollBackoff,
32
+ branchDeletionConfirmationMessage: () => branchDeletionConfirmationMessage,
32
33
  collectK8sUsage: () => collectK8sUsage,
34
+ createBranchThroughPlatform: () => createBranchThroughPlatform,
35
+ deleteBranchThroughPlatform: () => deleteBranchThroughPlatform,
33
36
  dispatchSessionRunCommand: () => dispatchSessionRunCommand,
34
37
  followR5dctlSession: () => followR5dctlSession,
35
38
  formatK8sCpu: () => formatK8sCpu,
@@ -41,10 +44,13 @@ __export(cli_exports, {
41
44
  getR5dctlVersion: () => getR5dctlVersion,
42
45
  getTransientDevicePollDelay: () => getTransientDevicePollDelay,
43
46
  handleAuthLogin: () => handleAuthLogin,
47
+ isUnsupportedServerRoute: () => isUnsupportedServerRoute,
44
48
  main: () => main,
45
49
  parseAnswerEnvRequestCommandArgs: () => parseAnswerEnvRequestCommandArgs,
46
50
  parseAnswerFlags: () => parseAnswerFlags,
47
51
  parseAnswerQuestionsCommandArgs: () => parseAnswerQuestionsCommandArgs,
52
+ parseBranchCreateArgs: () => parseBranchCreateArgs,
53
+ parseBranchDeleteArgs: () => parseBranchDeleteArgs,
48
54
  parseConversationForkArgs: () => parseConversationForkArgs,
49
55
  parseConversationInspectWorkArgs: () => parseConversationInspectWorkArgs,
50
56
  parseConversationOverviewArgs: () => parseConversationOverviewArgs,
@@ -62,6 +68,10 @@ __export(cli_exports, {
62
68
  parseSetEnvArgs: () => parseSetEnvArgs,
63
69
  parseShellArgs: () => parseShellArgs,
64
70
  readDotenvFile: () => readDotenvFile,
71
+ renderBranchDeletion: () => renderBranchDeletion,
72
+ renderBranchDeletionPreview: () => renderBranchDeletionPreview,
73
+ renderBranchList: () => renderBranchList,
74
+ renderBranchOperation: () => renderBranchOperation,
65
75
  renderConversationForkResponse: () => renderConversationForkResponse,
66
76
  renderConversationHeadResponse: () => renderConversationHeadResponse,
67
77
  renderConversationNodeResponse: () => renderConversationNodeResponse,
@@ -199,6 +209,21 @@ const SHARED_HELP_ENTRIES = [
199
209
  { section: "projects", usage: "delete project <namespace/name|id>", description: "Delete a project." },
200
210
  { section: "branches", usage: "-p <project> get branches", description: "List branches for a project." },
201
211
  { section: "branches", usage: "-p <project> describe branch <branch>", description: "Show branch URLs and sessions." },
212
+ {
213
+ section: "branches",
214
+ usage: "-p <project> create branch <branch> --source worktree:<branch> [--working-tree <carry|clean>] [--worker <label>]",
215
+ description: "Create a linked worktree branch from an existing branch's checkout and print the operation id to poll."
216
+ },
217
+ {
218
+ section: "branches",
219
+ usage: "-p <project> describe branch-operation <operation-id>",
220
+ description: "Show whether a branch creation is provisioning, ready, or failed."
221
+ },
222
+ {
223
+ section: "branches",
224
+ usage: "-p <project> delete branch <branch> [--confirm]",
225
+ description: "Delete a branch and its sessions through the platform; without --confirm, only preview what would be deleted."
226
+ },
202
227
  {
203
228
  section: "envs",
204
229
  usage: "-p <project> get envs [--show-values]",
@@ -545,8 +570,8 @@ function parseCommandOperands(args, valueFlags, booleanFlags = /* @__PURE__ */ n
545
570
  function requireWorker(values) {
546
571
  return requireValue(values.get("--worker")?.trim(), "--worker <label> is required");
547
572
  }
548
- function parseWorktreeSource(rawValue) {
549
- const raw = requireValue(rawValue?.trim(), "--source worktree:<branch> is required with --new-worktree");
573
+ function parseWorktreeSource(rawValue, requiredMessage = "--source worktree:<branch> is required with --new-worktree") {
574
+ const raw = requireValue(rawValue?.trim(), requiredMessage);
550
575
  const separator = raw.indexOf(":");
551
576
  const type = raw.slice(0, separator);
552
577
  const branchName = raw.slice(separator + 1).trim();
@@ -573,6 +598,103 @@ function requireSessionMode(value) {
573
598
  }
574
599
  return mode;
575
600
  }
601
+ function parseBranchCreateArgs(args) {
602
+ const parsed = parseCommandOperands(args, /* @__PURE__ */ new Set(["--source", "--working-tree", "--worker"]), /* @__PURE__ */ new Map(), "create branch", 1);
603
+ if (parsed.positionals.length > 0) throw new Error(`Unexpected create branch argument: ${parsed.positionals[0]}`);
604
+ const worker = parsed.values.get("--worker")?.trim();
605
+ if (parsed.values.has("--worker") && !worker) throw new Error("Missing value for --worker");
606
+ return {
607
+ source: parseWorktreeSource(parsed.values.get("--source"), "--source worktree:<branch> is required for `create branch`"),
608
+ workingTree: parseWorkingTreeMode(parsed.values.get("--working-tree")),
609
+ ...worker ? { worker } : {}
610
+ };
611
+ }
612
+ function parseBranchDeleteArgs(args) {
613
+ let confirmed = false;
614
+ for (const arg of args) {
615
+ if (arg === "--confirm") {
616
+ confirmed = true;
617
+ continue;
618
+ }
619
+ throw new Error(arg.startsWith("-") ? `Unknown delete branch flag: ${arg}` : `Unexpected delete branch argument: ${arg}`);
620
+ }
621
+ return { confirmed };
622
+ }
623
+ const PRIMARY_BRANCH_NAME = "main";
624
+ function isUnsupportedServerRoute(error) {
625
+ return error instanceof import_r5d_api.R5dctlApiError && error.status === 404 && extractApiErrorMessage(error) === "Not Found";
626
+ }
627
+ async function requireServerRouteSupport(command, request) {
628
+ try {
629
+ return await request();
630
+ } catch (error) {
631
+ if (isUnsupportedServerRoute(error)) {
632
+ throw new Error(`This r5d server does not support \`${command}\` yet. Update the server, or manage the branch from the web sidebar.`);
633
+ }
634
+ throw error;
635
+ }
636
+ }
637
+ async function createBranchThroughPlatform(client, projectRef, input) {
638
+ return await requireServerRouteSupport(
639
+ "create branch",
640
+ () => client.projects.branches.create(projectRef, {
641
+ requestId: (0, import_node_crypto.randomUUID)(),
642
+ branchName: input.branchName,
643
+ source: input.source,
644
+ workingTree: input.workingTree,
645
+ ...input.worker ? { worker: input.worker } : {}
646
+ })
647
+ );
648
+ }
649
+ async function deleteBranchThroughPlatform(client, projectRef, branchName, options) {
650
+ if (branchName === PRIMARY_BRANCH_NAME) throw new Error(`The primary branch ${branchName} cannot be deleted`);
651
+ const branch = await client.projects.branches.describe(projectRef, branchName);
652
+ if (!options.confirmed) return { kind: "preview", branch };
653
+ const result = await requireServerRouteSupport("delete branch", () => client.projects.branches.delete(projectRef, branchName));
654
+ return { kind: "deleted", result };
655
+ }
656
+ function renderBranchDeletionPreview(branch) {
657
+ const count = branch.sessions.length;
658
+ const lines = [
659
+ `Project: ${branch.project.path}`,
660
+ `Branch: ${branch.branchName}`,
661
+ `Sessions: ${count}`,
662
+ ...branch.sessions.map((session) => ` ${session.id} ${session.name ?? "(unnamed)"}`),
663
+ `Deleting this branch removes its checkout from every connected worker and deletes the ${count} session(s) above.`
664
+ ];
665
+ return `${lines.join("\n")}
666
+ `;
667
+ }
668
+ function branchDeletionConfirmationMessage(branch) {
669
+ return `Nothing was deleted. Re-run with --confirm to delete branch ${branch.branchName} and its ${branch.sessions.length} session(s).`;
670
+ }
671
+ function renderBranchDeletion(result) {
672
+ const lines = [
673
+ `Deleted branch ${result.branchName} and ${result.deletedSessionIds.length} session(s).`,
674
+ ...result.deletedSessionIds.map((sessionId) => ` ${sessionId}`)
675
+ ];
676
+ return `${lines.join("\n")}
677
+ `;
678
+ }
679
+ function renderBranchOperation(operation, projectRef) {
680
+ const lines = [
681
+ `Operation: ${operation.id}`,
682
+ `Branch: ${operation.branchName}`,
683
+ `Source: worktree:${operation.source.branchName}`,
684
+ `Working tree: ${operation.workingTree}`,
685
+ `Worker: ${operation.worker ?? "(pending)"}`,
686
+ `Status: ${operation.status}`
687
+ ];
688
+ if (operation.error) lines.push(`Error: ${operation.error}`);
689
+ if (operation.cleanupPending) lines.push("Cleanup: pending");
690
+ if (operation.status === "provisioning") {
691
+ lines.push(`Check: r5dctl -p ${projectRef} describe branch-operation ${operation.id}`);
692
+ } else if (operation.status === "ready") {
693
+ lines.push(`Branch state: ${formatBranchStateHint(operation.branchName)}`);
694
+ }
695
+ return `${lines.join("\n")}
696
+ `;
697
+ }
576
698
  function rejectSessionRunGlobalScopes(options, allowProject) {
577
699
  if (!allowProject && options.project) throw new Error("--project/-p is only supported for `session start`");
578
700
  if (options.branch) throw new Error("--branch/-b is not supported for `session`; select the worktree explicitly");
@@ -1479,10 +1601,34 @@ function renderWorkspaceStatus(status) {
1479
1601
  `Workspace HEAD: ${status.head ?? "(not initialized)"}`,
1480
1602
  `Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
1481
1603
  `Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
1482
- ...status.workers.map(
1483
- (worker) => `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`
1484
- )
1604
+ ...status.workers.flatMap((worker) => [
1605
+ `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`,
1606
+ ...renderMissingCheckouts(worker.workerLabel, worker.missingCheckouts ?? [])
1607
+ ])
1485
1608
  ];
1609
+ if (status.latestSync?.missingCheckouts?.length && !status.workers.some((worker) => worker.missingCheckouts?.length)) {
1610
+ lines.push(...renderMissingCheckouts(status.latestSync.workerLabel, status.latestSync.missingCheckouts));
1611
+ }
1612
+ return `${lines.join("\n")}
1613
+ `;
1614
+ }
1615
+ const CHECKOUT_RECOVERY_HINT = "delete the branch with `r5dctl -p <project> delete branch <branch> --confirm`, or reconnect the worker to recreate the checkout";
1616
+ function renderMissingCheckouts(workerLabel, missingCheckouts) {
1617
+ if (missingCheckouts.length === 0) return [];
1618
+ return [
1619
+ `Missing checkouts on ${workerLabel}: ${missingCheckouts.map((checkout) => `${checkout.projectId}/${checkout.branchName} (${checkout.reason})`).join(", ")}`,
1620
+ ` Workspace sync skips these branches; ${CHECKOUT_RECOVERY_HINT}.`
1621
+ ];
1622
+ }
1623
+ function renderBranchList(branches) {
1624
+ if (branches.length === 0) return "No branches found.\n";
1625
+ const lines = branches.map((branch) => {
1626
+ const checkout = branch.checkoutState ? ` checkout=${branch.checkoutState}${branch.checkoutStateWorkerLabel ? ` on ${branch.checkoutStateWorkerLabel}` : ""}` : "";
1627
+ return `${branch.branchName} sessions=${branch.sessionCount}${checkout}`;
1628
+ });
1629
+ if (branches.some((branch) => branch.checkoutState)) {
1630
+ lines.push(`Workspace sync skips a branch whose checkout is missing or not a worktree; ${CHECKOUT_RECOVERY_HINT}.`);
1631
+ }
1486
1632
  return `${lines.join("\n")}
1487
1633
  `;
1488
1634
  }
@@ -2660,11 +2806,7 @@ async function executeR5dctlCommand(client, json, args) {
2660
2806
  if (first === "projects" && second === "branches" && third === "list" || first === "get" && second === "branches") {
2661
2807
  const projectRef = requireValue(first === "get" ? args[2] : args[3], "Missing project reference");
2662
2808
  const branches = await client.projects.branches.list(projectRef);
2663
- write(
2664
- branches,
2665
- branches.length === 0 ? "No branches found.\n" : `${branches.map((branch) => `${branch.branchName} sessions=${branch.sessionCount}`).join("\n")}
2666
- `
2667
- );
2809
+ write(branches, renderBranchList(branches));
2668
2810
  return;
2669
2811
  }
2670
2812
  if (first === "projects" && second === "branches" && third === "describe" || first === "describe" && second === "branch") {
@@ -2679,6 +2821,40 @@ Sessions: ${result.sessions.length}
2679
2821
  `);
2680
2822
  return;
2681
2823
  }
2824
+ if (first === "projects" && second === "branches" && third === "create" || first === "create" && second === "branch") {
2825
+ const offset = first === "create" ? 2 : 3;
2826
+ const projectRef = requireValue(args[offset], "Missing project reference");
2827
+ const branchName = requireValue(args[offset + 1], "Missing branch name");
2828
+ const operation = await createBranchThroughPlatform(client, projectRef, {
2829
+ branchName,
2830
+ ...parseBranchCreateArgs(args.slice(offset + 2))
2831
+ });
2832
+ write(operation, renderBranchOperation(operation, projectRef));
2833
+ return;
2834
+ }
2835
+ if (first === "projects" && second === "branches" && third === "operation" || first === "describe" && second === "branch-operation") {
2836
+ const offset = first === "describe" ? 2 : 3;
2837
+ const projectRef = requireValue(args[offset], "Missing project reference");
2838
+ const operationId = requireValue(args[offset + 1], "Missing operation id");
2839
+ const operation = await requireServerRouteSupport(
2840
+ "describe branch-operation",
2841
+ () => client.projects.branches.describeOperation(projectRef, operationId)
2842
+ );
2843
+ write(operation, renderBranchOperation(operation, projectRef));
2844
+ return;
2845
+ }
2846
+ if (first === "projects" && second === "branches" && third === "delete" || first === "delete" && second === "branch") {
2847
+ const offset = first === "delete" ? 2 : 3;
2848
+ const projectRef = requireValue(args[offset], "Missing project reference");
2849
+ const branchName = requireValue(args[offset + 1], "Missing branch name");
2850
+ const deletion = await deleteBranchThroughPlatform(client, projectRef, branchName, parseBranchDeleteArgs(args.slice(offset + 2)));
2851
+ if (deletion.kind === "preview") {
2852
+ write({ ...deletion.branch, confirmed: false }, renderBranchDeletionPreview(deletion.branch));
2853
+ throw new Error(branchDeletionConfirmationMessage(deletion.branch));
2854
+ }
2855
+ write(deletion.result, renderBranchDeletion(deletion.result));
2856
+ return;
2857
+ }
2682
2858
  if (first === "get" && second === "envs") {
2683
2859
  const projectRef = requireValue(args[2], "Missing project reference");
2684
2860
  const options = parseGetEnvsArgs(args.slice(3));
@@ -3095,6 +3271,19 @@ function resolveCommandExecution(options, rest) {
3095
3271
  pluginArgs: ["describe", "branch", options.project, branch]
3096
3272
  };
3097
3273
  }
3274
+ if (target === "branch-operation") {
3275
+ if (!options.project) {
3276
+ throw new Error("--project/-p is required for `describe branch-operation`");
3277
+ }
3278
+ const operationId = requireValue(commandArgs[1], "Operation id is required for `describe branch-operation`");
3279
+ if (commandArgs.length > 2) {
3280
+ throw new Error(`Unexpected describe branch-operation argument: ${commandArgs[2]}`);
3281
+ }
3282
+ return {
3283
+ kind: "plugin",
3284
+ pluginArgs: ["describe", "branch-operation", options.project, operationId]
3285
+ };
3286
+ }
3098
3287
  if (target === "session") {
3099
3288
  const sessionId = commandArgs[1] ?? options.session;
3100
3289
  if (!sessionId) {
@@ -3127,6 +3316,23 @@ function resolveCommandExecution(options, rest) {
3127
3316
  pluginArgs: ["create", "session", projectRef, branch, ...flagArgs]
3128
3317
  };
3129
3318
  }
3319
+ if (target === "branch") {
3320
+ if (!options.project) {
3321
+ throw new Error("--project/-p is required for `create branch`");
3322
+ }
3323
+ const rawArgs = commandArgs.slice(1);
3324
+ const positionalBranch = rawArgs[0] && !rawArgs[0].startsWith("-") ? rawArgs[0] : void 0;
3325
+ const branch = positionalBranch ?? options.branch;
3326
+ if (!branch) {
3327
+ throw new Error("Branch name is required for `create branch`");
3328
+ }
3329
+ const flagArgs = positionalBranch ? rawArgs.slice(1) : rawArgs;
3330
+ parseBranchCreateArgs(flagArgs);
3331
+ return {
3332
+ kind: "plugin",
3333
+ pluginArgs: ["create", "branch", options.project, branch, ...flagArgs]
3334
+ };
3335
+ }
3130
3336
  throw new Error("Unknown create command");
3131
3337
  }
3132
3338
  if (command === "update") {
@@ -3177,6 +3383,23 @@ function resolveCommandExecution(options, rest) {
3177
3383
  pluginArgs: ["delete", "session", sessionId]
3178
3384
  };
3179
3385
  }
3386
+ if (target === "branch") {
3387
+ if (!options.project) {
3388
+ throw new Error("--project/-p is required for `delete branch`");
3389
+ }
3390
+ const rawArgs = commandArgs.slice(1);
3391
+ const positionalBranch = rawArgs[0] && !rawArgs[0].startsWith("-") ? rawArgs[0] : void 0;
3392
+ const branch = positionalBranch ?? options.branch;
3393
+ if (!branch) {
3394
+ throw new Error("Branch name is required for `delete branch`");
3395
+ }
3396
+ const flagArgs = positionalBranch ? rawArgs.slice(1) : rawArgs;
3397
+ parseBranchDeleteArgs(flagArgs);
3398
+ return {
3399
+ kind: "plugin",
3400
+ pluginArgs: ["delete", "branch", options.project, branch, ...flagArgs]
3401
+ };
3402
+ }
3180
3403
  throw new Error("Unknown delete command");
3181
3404
  }
3182
3405
  if (command === "conversation") {
@@ -3404,7 +3627,10 @@ async function main(argv = process.argv.slice(2)) {
3404
3627
  // Annotate the CommonJS export names for ESM import in node:
3405
3628
  0 && (module.exports = {
3406
3629
  advanceTransientDevicePollBackoff,
3630
+ branchDeletionConfirmationMessage,
3407
3631
  collectK8sUsage,
3632
+ createBranchThroughPlatform,
3633
+ deleteBranchThroughPlatform,
3408
3634
  dispatchSessionRunCommand,
3409
3635
  followR5dctlSession,
3410
3636
  formatK8sCpu,
@@ -3416,10 +3642,13 @@ async function main(argv = process.argv.slice(2)) {
3416
3642
  getR5dctlVersion,
3417
3643
  getTransientDevicePollDelay,
3418
3644
  handleAuthLogin,
3645
+ isUnsupportedServerRoute,
3419
3646
  main,
3420
3647
  parseAnswerEnvRequestCommandArgs,
3421
3648
  parseAnswerFlags,
3422
3649
  parseAnswerQuestionsCommandArgs,
3650
+ parseBranchCreateArgs,
3651
+ parseBranchDeleteArgs,
3423
3652
  parseConversationForkArgs,
3424
3653
  parseConversationInspectWorkArgs,
3425
3654
  parseConversationOverviewArgs,
@@ -3437,6 +3666,10 @@ async function main(argv = process.argv.slice(2)) {
3437
3666
  parseSetEnvArgs,
3438
3667
  parseShellArgs,
3439
3668
  readDotenvFile,
3669
+ renderBranchDeletion,
3670
+ renderBranchDeletionPreview,
3671
+ renderBranchList,
3672
+ renderBranchOperation,
3440
3673
  renderConversationForkResponse,
3441
3674
  renderConversationHeadResponse,
3442
3675
  renderConversationNodeResponse,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.103",
3
+ "version": "0.0.104",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -114,6 +114,21 @@ const SHARED_HELP_ENTRIES = [
114
114
  { section: "projects", usage: "delete project <namespace/name|id>", description: "Delete a project." },
115
115
  { section: "branches", usage: "-p <project> get branches", description: "List branches for a project." },
116
116
  { section: "branches", usage: "-p <project> describe branch <branch>", description: "Show branch URLs and sessions." },
117
+ {
118
+ section: "branches",
119
+ usage: "-p <project> create branch <branch> --source worktree:<branch> [--working-tree <carry|clean>] [--worker <label>]",
120
+ description: "Create a linked worktree branch from an existing branch's checkout and print the operation id to poll."
121
+ },
122
+ {
123
+ section: "branches",
124
+ usage: "-p <project> describe branch-operation <operation-id>",
125
+ description: "Show whether a branch creation is provisioning, ready, or failed."
126
+ },
127
+ {
128
+ section: "branches",
129
+ usage: "-p <project> delete branch <branch> [--confirm]",
130
+ description: "Delete a branch and its sessions through the platform; without --confirm, only preview what would be deleted."
131
+ },
117
132
  {
118
133
  section: "envs",
119
134
  usage: "-p <project> get envs [--show-values]",
@@ -460,8 +475,8 @@ function parseCommandOperands(args, valueFlags, booleanFlags = /* @__PURE__ */ n
460
475
  function requireWorker(values) {
461
476
  return requireValue(values.get("--worker")?.trim(), "--worker <label> is required");
462
477
  }
463
- function parseWorktreeSource(rawValue) {
464
- const raw = requireValue(rawValue?.trim(), "--source worktree:<branch> is required with --new-worktree");
478
+ function parseWorktreeSource(rawValue, requiredMessage = "--source worktree:<branch> is required with --new-worktree") {
479
+ const raw = requireValue(rawValue?.trim(), requiredMessage);
465
480
  const separator = raw.indexOf(":");
466
481
  const type = raw.slice(0, separator);
467
482
  const branchName = raw.slice(separator + 1).trim();
@@ -488,6 +503,103 @@ function requireSessionMode(value) {
488
503
  }
489
504
  return mode;
490
505
  }
506
+ function parseBranchCreateArgs(args) {
507
+ const parsed = parseCommandOperands(args, /* @__PURE__ */ new Set(["--source", "--working-tree", "--worker"]), /* @__PURE__ */ new Map(), "create branch", 1);
508
+ if (parsed.positionals.length > 0) throw new Error(`Unexpected create branch argument: ${parsed.positionals[0]}`);
509
+ const worker = parsed.values.get("--worker")?.trim();
510
+ if (parsed.values.has("--worker") && !worker) throw new Error("Missing value for --worker");
511
+ return {
512
+ source: parseWorktreeSource(parsed.values.get("--source"), "--source worktree:<branch> is required for `create branch`"),
513
+ workingTree: parseWorkingTreeMode(parsed.values.get("--working-tree")),
514
+ ...worker ? { worker } : {}
515
+ };
516
+ }
517
+ function parseBranchDeleteArgs(args) {
518
+ let confirmed = false;
519
+ for (const arg of args) {
520
+ if (arg === "--confirm") {
521
+ confirmed = true;
522
+ continue;
523
+ }
524
+ throw new Error(arg.startsWith("-") ? `Unknown delete branch flag: ${arg}` : `Unexpected delete branch argument: ${arg}`);
525
+ }
526
+ return { confirmed };
527
+ }
528
+ const PRIMARY_BRANCH_NAME = "main";
529
+ function isUnsupportedServerRoute(error) {
530
+ return error instanceof R5dctlApiError && error.status === 404 && extractApiErrorMessage(error) === "Not Found";
531
+ }
532
+ async function requireServerRouteSupport(command, request) {
533
+ try {
534
+ return await request();
535
+ } catch (error) {
536
+ if (isUnsupportedServerRoute(error)) {
537
+ throw new Error(`This r5d server does not support \`${command}\` yet. Update the server, or manage the branch from the web sidebar.`);
538
+ }
539
+ throw error;
540
+ }
541
+ }
542
+ async function createBranchThroughPlatform(client, projectRef, input) {
543
+ return await requireServerRouteSupport(
544
+ "create branch",
545
+ () => client.projects.branches.create(projectRef, {
546
+ requestId: randomUUID(),
547
+ branchName: input.branchName,
548
+ source: input.source,
549
+ workingTree: input.workingTree,
550
+ ...input.worker ? { worker: input.worker } : {}
551
+ })
552
+ );
553
+ }
554
+ async function deleteBranchThroughPlatform(client, projectRef, branchName, options) {
555
+ if (branchName === PRIMARY_BRANCH_NAME) throw new Error(`The primary branch ${branchName} cannot be deleted`);
556
+ const branch = await client.projects.branches.describe(projectRef, branchName);
557
+ if (!options.confirmed) return { kind: "preview", branch };
558
+ const result = await requireServerRouteSupport("delete branch", () => client.projects.branches.delete(projectRef, branchName));
559
+ return { kind: "deleted", result };
560
+ }
561
+ function renderBranchDeletionPreview(branch) {
562
+ const count = branch.sessions.length;
563
+ const lines = [
564
+ `Project: ${branch.project.path}`,
565
+ `Branch: ${branch.branchName}`,
566
+ `Sessions: ${count}`,
567
+ ...branch.sessions.map((session) => ` ${session.id} ${session.name ?? "(unnamed)"}`),
568
+ `Deleting this branch removes its checkout from every connected worker and deletes the ${count} session(s) above.`
569
+ ];
570
+ return `${lines.join("\n")}
571
+ `;
572
+ }
573
+ function branchDeletionConfirmationMessage(branch) {
574
+ return `Nothing was deleted. Re-run with --confirm to delete branch ${branch.branchName} and its ${branch.sessions.length} session(s).`;
575
+ }
576
+ function renderBranchDeletion(result) {
577
+ const lines = [
578
+ `Deleted branch ${result.branchName} and ${result.deletedSessionIds.length} session(s).`,
579
+ ...result.deletedSessionIds.map((sessionId) => ` ${sessionId}`)
580
+ ];
581
+ return `${lines.join("\n")}
582
+ `;
583
+ }
584
+ function renderBranchOperation(operation, projectRef) {
585
+ const lines = [
586
+ `Operation: ${operation.id}`,
587
+ `Branch: ${operation.branchName}`,
588
+ `Source: worktree:${operation.source.branchName}`,
589
+ `Working tree: ${operation.workingTree}`,
590
+ `Worker: ${operation.worker ?? "(pending)"}`,
591
+ `Status: ${operation.status}`
592
+ ];
593
+ if (operation.error) lines.push(`Error: ${operation.error}`);
594
+ if (operation.cleanupPending) lines.push("Cleanup: pending");
595
+ if (operation.status === "provisioning") {
596
+ lines.push(`Check: r5dctl -p ${projectRef} describe branch-operation ${operation.id}`);
597
+ } else if (operation.status === "ready") {
598
+ lines.push(`Branch state: ${formatBranchStateHint(operation.branchName)}`);
599
+ }
600
+ return `${lines.join("\n")}
601
+ `;
602
+ }
491
603
  function rejectSessionRunGlobalScopes(options, allowProject) {
492
604
  if (!allowProject && options.project) throw new Error("--project/-p is only supported for `session start`");
493
605
  if (options.branch) throw new Error("--branch/-b is not supported for `session`; select the worktree explicitly");
@@ -1394,10 +1506,34 @@ function renderWorkspaceStatus(status) {
1394
1506
  `Workspace HEAD: ${status.head ?? "(not initialized)"}`,
1395
1507
  `Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
1396
1508
  `Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
1397
- ...status.workers.map(
1398
- (worker) => `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`
1399
- )
1509
+ ...status.workers.flatMap((worker) => [
1510
+ `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`,
1511
+ ...renderMissingCheckouts(worker.workerLabel, worker.missingCheckouts ?? [])
1512
+ ])
1400
1513
  ];
1514
+ if (status.latestSync?.missingCheckouts?.length && !status.workers.some((worker) => worker.missingCheckouts?.length)) {
1515
+ lines.push(...renderMissingCheckouts(status.latestSync.workerLabel, status.latestSync.missingCheckouts));
1516
+ }
1517
+ return `${lines.join("\n")}
1518
+ `;
1519
+ }
1520
+ const CHECKOUT_RECOVERY_HINT = "delete the branch with `r5dctl -p <project> delete branch <branch> --confirm`, or reconnect the worker to recreate the checkout";
1521
+ function renderMissingCheckouts(workerLabel, missingCheckouts) {
1522
+ if (missingCheckouts.length === 0) return [];
1523
+ return [
1524
+ `Missing checkouts on ${workerLabel}: ${missingCheckouts.map((checkout) => `${checkout.projectId}/${checkout.branchName} (${checkout.reason})`).join(", ")}`,
1525
+ ` Workspace sync skips these branches; ${CHECKOUT_RECOVERY_HINT}.`
1526
+ ];
1527
+ }
1528
+ function renderBranchList(branches) {
1529
+ if (branches.length === 0) return "No branches found.\n";
1530
+ const lines = branches.map((branch) => {
1531
+ const checkout = branch.checkoutState ? ` checkout=${branch.checkoutState}${branch.checkoutStateWorkerLabel ? ` on ${branch.checkoutStateWorkerLabel}` : ""}` : "";
1532
+ return `${branch.branchName} sessions=${branch.sessionCount}${checkout}`;
1533
+ });
1534
+ if (branches.some((branch) => branch.checkoutState)) {
1535
+ lines.push(`Workspace sync skips a branch whose checkout is missing or not a worktree; ${CHECKOUT_RECOVERY_HINT}.`);
1536
+ }
1401
1537
  return `${lines.join("\n")}
1402
1538
  `;
1403
1539
  }
@@ -2575,11 +2711,7 @@ async function executeR5dctlCommand(client, json, args) {
2575
2711
  if (first === "projects" && second === "branches" && third === "list" || first === "get" && second === "branches") {
2576
2712
  const projectRef = requireValue(first === "get" ? args[2] : args[3], "Missing project reference");
2577
2713
  const branches = await client.projects.branches.list(projectRef);
2578
- write(
2579
- branches,
2580
- branches.length === 0 ? "No branches found.\n" : `${branches.map((branch) => `${branch.branchName} sessions=${branch.sessionCount}`).join("\n")}
2581
- `
2582
- );
2714
+ write(branches, renderBranchList(branches));
2583
2715
  return;
2584
2716
  }
2585
2717
  if (first === "projects" && second === "branches" && third === "describe" || first === "describe" && second === "branch") {
@@ -2594,6 +2726,40 @@ Sessions: ${result.sessions.length}
2594
2726
  `);
2595
2727
  return;
2596
2728
  }
2729
+ if (first === "projects" && second === "branches" && third === "create" || first === "create" && second === "branch") {
2730
+ const offset = first === "create" ? 2 : 3;
2731
+ const projectRef = requireValue(args[offset], "Missing project reference");
2732
+ const branchName = requireValue(args[offset + 1], "Missing branch name");
2733
+ const operation = await createBranchThroughPlatform(client, projectRef, {
2734
+ branchName,
2735
+ ...parseBranchCreateArgs(args.slice(offset + 2))
2736
+ });
2737
+ write(operation, renderBranchOperation(operation, projectRef));
2738
+ return;
2739
+ }
2740
+ if (first === "projects" && second === "branches" && third === "operation" || first === "describe" && second === "branch-operation") {
2741
+ const offset = first === "describe" ? 2 : 3;
2742
+ const projectRef = requireValue(args[offset], "Missing project reference");
2743
+ const operationId = requireValue(args[offset + 1], "Missing operation id");
2744
+ const operation = await requireServerRouteSupport(
2745
+ "describe branch-operation",
2746
+ () => client.projects.branches.describeOperation(projectRef, operationId)
2747
+ );
2748
+ write(operation, renderBranchOperation(operation, projectRef));
2749
+ return;
2750
+ }
2751
+ if (first === "projects" && second === "branches" && third === "delete" || first === "delete" && second === "branch") {
2752
+ const offset = first === "delete" ? 2 : 3;
2753
+ const projectRef = requireValue(args[offset], "Missing project reference");
2754
+ const branchName = requireValue(args[offset + 1], "Missing branch name");
2755
+ const deletion = await deleteBranchThroughPlatform(client, projectRef, branchName, parseBranchDeleteArgs(args.slice(offset + 2)));
2756
+ if (deletion.kind === "preview") {
2757
+ write({ ...deletion.branch, confirmed: false }, renderBranchDeletionPreview(deletion.branch));
2758
+ throw new Error(branchDeletionConfirmationMessage(deletion.branch));
2759
+ }
2760
+ write(deletion.result, renderBranchDeletion(deletion.result));
2761
+ return;
2762
+ }
2597
2763
  if (first === "get" && second === "envs") {
2598
2764
  const projectRef = requireValue(args[2], "Missing project reference");
2599
2765
  const options = parseGetEnvsArgs(args.slice(3));
@@ -3010,6 +3176,19 @@ function resolveCommandExecution(options, rest) {
3010
3176
  pluginArgs: ["describe", "branch", options.project, branch]
3011
3177
  };
3012
3178
  }
3179
+ if (target === "branch-operation") {
3180
+ if (!options.project) {
3181
+ throw new Error("--project/-p is required for `describe branch-operation`");
3182
+ }
3183
+ const operationId = requireValue(commandArgs[1], "Operation id is required for `describe branch-operation`");
3184
+ if (commandArgs.length > 2) {
3185
+ throw new Error(`Unexpected describe branch-operation argument: ${commandArgs[2]}`);
3186
+ }
3187
+ return {
3188
+ kind: "plugin",
3189
+ pluginArgs: ["describe", "branch-operation", options.project, operationId]
3190
+ };
3191
+ }
3013
3192
  if (target === "session") {
3014
3193
  const sessionId = commandArgs[1] ?? options.session;
3015
3194
  if (!sessionId) {
@@ -3042,6 +3221,23 @@ function resolveCommandExecution(options, rest) {
3042
3221
  pluginArgs: ["create", "session", projectRef, branch, ...flagArgs]
3043
3222
  };
3044
3223
  }
3224
+ if (target === "branch") {
3225
+ if (!options.project) {
3226
+ throw new Error("--project/-p is required for `create branch`");
3227
+ }
3228
+ const rawArgs = commandArgs.slice(1);
3229
+ const positionalBranch = rawArgs[0] && !rawArgs[0].startsWith("-") ? rawArgs[0] : void 0;
3230
+ const branch = positionalBranch ?? options.branch;
3231
+ if (!branch) {
3232
+ throw new Error("Branch name is required for `create branch`");
3233
+ }
3234
+ const flagArgs = positionalBranch ? rawArgs.slice(1) : rawArgs;
3235
+ parseBranchCreateArgs(flagArgs);
3236
+ return {
3237
+ kind: "plugin",
3238
+ pluginArgs: ["create", "branch", options.project, branch, ...flagArgs]
3239
+ };
3240
+ }
3045
3241
  throw new Error("Unknown create command");
3046
3242
  }
3047
3243
  if (command === "update") {
@@ -3092,6 +3288,23 @@ function resolveCommandExecution(options, rest) {
3092
3288
  pluginArgs: ["delete", "session", sessionId]
3093
3289
  };
3094
3290
  }
3291
+ if (target === "branch") {
3292
+ if (!options.project) {
3293
+ throw new Error("--project/-p is required for `delete branch`");
3294
+ }
3295
+ const rawArgs = commandArgs.slice(1);
3296
+ const positionalBranch = rawArgs[0] && !rawArgs[0].startsWith("-") ? rawArgs[0] : void 0;
3297
+ const branch = positionalBranch ?? options.branch;
3298
+ if (!branch) {
3299
+ throw new Error("Branch name is required for `delete branch`");
3300
+ }
3301
+ const flagArgs = positionalBranch ? rawArgs.slice(1) : rawArgs;
3302
+ parseBranchDeleteArgs(flagArgs);
3303
+ return {
3304
+ kind: "plugin",
3305
+ pluginArgs: ["delete", "branch", options.project, branch, ...flagArgs]
3306
+ };
3307
+ }
3095
3308
  throw new Error("Unknown delete command");
3096
3309
  }
3097
3310
  if (command === "conversation") {
@@ -3318,7 +3531,10 @@ async function main(argv = process.argv.slice(2)) {
3318
3531
  }
3319
3532
  export {
3320
3533
  advanceTransientDevicePollBackoff,
3534
+ branchDeletionConfirmationMessage,
3321
3535
  collectK8sUsage,
3536
+ createBranchThroughPlatform,
3537
+ deleteBranchThroughPlatform,
3322
3538
  dispatchSessionRunCommand,
3323
3539
  followR5dctlSession,
3324
3540
  formatK8sCpu,
@@ -3330,10 +3546,13 @@ export {
3330
3546
  getR5dctlVersion,
3331
3547
  getTransientDevicePollDelay,
3332
3548
  handleAuthLogin,
3549
+ isUnsupportedServerRoute,
3333
3550
  main,
3334
3551
  parseAnswerEnvRequestCommandArgs,
3335
3552
  parseAnswerFlags,
3336
3553
  parseAnswerQuestionsCommandArgs,
3554
+ parseBranchCreateArgs,
3555
+ parseBranchDeleteArgs,
3337
3556
  parseConversationForkArgs,
3338
3557
  parseConversationInspectWorkArgs,
3339
3558
  parseConversationOverviewArgs,
@@ -3351,6 +3570,10 @@ export {
3351
3570
  parseSetEnvArgs,
3352
3571
  parseShellArgs,
3353
3572
  readDotenvFile,
3573
+ renderBranchDeletion,
3574
+ renderBranchDeletionPreview,
3575
+ renderBranchList,
3576
+ renderBranchOperation,
3354
3577
  renderConversationForkResponse,
3355
3578
  renderConversationHeadResponse,
3356
3579
  renderConversationNodeResponse,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.103",
3
+ "version": "0.0.104",
4
4
  "type": "module"
5
5
  }
@@ -1,4 +1,4 @@
1
- import { R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlK8sResourceHistory, type R5dctlK8sUsage, type R5dctlK8sWorkload, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
1
+ import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlK8sResourceHistory, type R5dctlK8sUsage, type R5dctlK8sWorkload, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlBranch, type R5dctlBranchDeleteResponse, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
2
2
  export type GlobalOptions = {
3
3
  baseUrl?: string;
4
4
  json: boolean;
@@ -79,6 +79,35 @@ export type R5dctlSessionRunCommand = R5dctlSessionStartCommand | {
79
79
  kind: "stop";
80
80
  sessionId: string;
81
81
  };
82
+ export type R5dctlBranchCreateArgs = {
83
+ source: R5dctlWorktreeSource;
84
+ workingTree: R5dctlWorkingTreeMode;
85
+ worker?: string;
86
+ };
87
+ export declare function parseBranchCreateArgs(args: string[]): R5dctlBranchCreateArgs;
88
+ export declare function parseBranchDeleteArgs(args: string[]): {
89
+ confirmed: boolean;
90
+ };
91
+ /** An older server strips unknown routes and answers with this generic 404; a route's own not-found carries a specific message. */
92
+ export declare function isUnsupportedServerRoute(error: unknown): error is R5dctlApiError;
93
+ export declare function createBranchThroughPlatform(client: R5dctlClient, projectRef: string, input: {
94
+ branchName: string;
95
+ } & R5dctlBranchCreateArgs): Promise<R5dctlBranchOperation>;
96
+ export type R5dctlBranchDeletionOutcome = {
97
+ kind: "preview";
98
+ branch: R5dctlBranchDescription;
99
+ } | {
100
+ kind: "deleted";
101
+ result: R5dctlBranchDeleteResponse;
102
+ };
103
+ export declare function deleteBranchThroughPlatform(client: R5dctlClient, projectRef: string, branchName: string, options: {
104
+ confirmed: boolean;
105
+ }): Promise<R5dctlBranchDeletionOutcome>;
106
+ export declare function renderBranchDeletionPreview(branch: R5dctlBranchDescription): string;
107
+ export declare function branchDeletionConfirmationMessage(branch: R5dctlBranchDescription): string;
108
+ export declare function renderBranchDeletion(result: R5dctlBranchDeleteResponse): string;
109
+ /** Operation facts only: no commit hash, because a hash is stale as soon as the branch commits. */
110
+ export declare function renderBranchOperation(operation: R5dctlBranchOperation, projectRef: string): string;
82
111
  export declare function parseSessionRunCommand(options: Pick<GlobalOptions, "project" | "branch" | "session">, args: string[]): R5dctlSessionRunCommand;
83
112
  export type R5dctlSessionProcessLogArgs = {
84
113
  runId: string;
@@ -146,6 +175,7 @@ export declare function advanceTransientDevicePollBackoff(intervalMs: number, tr
146
175
  };
147
176
  export declare function handleAuthLogin(client: R5dctlClient, options: GlobalOptions, commandArgs: string[], config: R5dctlConfig): Promise<void>;
148
177
  export declare function renderWorkspaceStatus(status: R5dctlWorkspaceStatus): string;
178
+ export declare function renderBranchList(branches: R5dctlBranch[]): string;
149
179
  export declare function renderWorkspaceSync(result: R5dctlWorkspaceSyncResult): string;
150
180
  export type K8sUsageSummary = {
151
181
  cpuMinNanoCores: number | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.103",
3
+ "version": "0.0.104",
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.103",
29
+ "@ricsam/r5d-api": "^0.0.104",
30
30
  "dotenv": "^17",
31
31
  "qrcode": "^1.5.4",
32
32
  "ws": "^8.18.3"