@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 +10 -0
- package/dist/cjs/cli.cjs +243 -10
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +233 -10
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +31 -1
- package/package.json +2 -2
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(),
|
|
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.
|
|
1483
|
-
|
|
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,
|
package/dist/cjs/package.json
CHANGED
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(),
|
|
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.
|
|
1398
|
-
|
|
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,
|
package/dist/mjs/package.json
CHANGED
package/dist/types/cli.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
29
|
+
"@ricsam/r5d-api": "^0.0.104",
|
|
30
30
|
"dotenv": "^17",
|
|
31
31
|
"qrcode": "^1.5.4",
|
|
32
32
|
"ws": "^8.18.3"
|