@ricsam/r5dctl 0.0.101 → 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 +18 -1
- package/dist/cjs/cli.cjs +374 -25
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +362 -25
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +53 -1
- package/package.json +2 -2
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,
|
|
@@ -57,10 +63,15 @@ __export(cli_exports, {
|
|
|
57
63
|
parsePromptArgs: () => parsePromptArgs,
|
|
58
64
|
parsePsHistoryArgs: () => parsePsHistoryArgs,
|
|
59
65
|
parsePsListArgs: () => parsePsListArgs,
|
|
66
|
+
parseSessionProcessLogArgs: () => parseSessionProcessLogArgs,
|
|
60
67
|
parseSessionRunCommand: () => parseSessionRunCommand,
|
|
61
68
|
parseSetEnvArgs: () => parseSetEnvArgs,
|
|
62
69
|
parseShellArgs: () => parseShellArgs,
|
|
63
70
|
readDotenvFile: () => readDotenvFile,
|
|
71
|
+
renderBranchDeletion: () => renderBranchDeletion,
|
|
72
|
+
renderBranchDeletionPreview: () => renderBranchDeletionPreview,
|
|
73
|
+
renderBranchList: () => renderBranchList,
|
|
74
|
+
renderBranchOperation: () => renderBranchOperation,
|
|
64
75
|
renderConversationForkResponse: () => renderConversationForkResponse,
|
|
65
76
|
renderConversationHeadResponse: () => renderConversationHeadResponse,
|
|
66
77
|
renderConversationNodeResponse: () => renderConversationNodeResponse,
|
|
@@ -73,6 +84,7 @@ __export(cli_exports, {
|
|
|
73
84
|
renderProcessHistory: () => renderProcessHistory,
|
|
74
85
|
renderProcessInspection: () => renderProcessInspection,
|
|
75
86
|
renderProcessList: () => renderProcessList,
|
|
87
|
+
renderProcessLog: () => renderProcessLog,
|
|
76
88
|
renderSessionRunStart: () => renderSessionRunStart,
|
|
77
89
|
renderSessionRunStatus: () => renderSessionRunStatus,
|
|
78
90
|
renderWorkspaceStatus: () => renderWorkspaceStatus,
|
|
@@ -155,14 +167,19 @@ const K8S_HELP_TEXT = [
|
|
|
155
167
|
].join("\n");
|
|
156
168
|
const SESSION_RUN_HELP_TEXT = [
|
|
157
169
|
"Usage:",
|
|
158
|
-
' r5dctl -p <project> session start --worker <label> --worktree <branch> --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
159
|
-
' r5dctl -p <project> session start --worker <label> --new-worktree <branch> --source worktree:<branch> --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
170
|
+
' r5dctl -p <project> session start --worker <label> --worktree <branch> --model <tier> [-f|--follow|--watch] [--tools] [--parent-session <session-id>] "<prompt>"',
|
|
171
|
+
' r5dctl -p <project> session start --worker <label> --new-worktree <branch> --source worktree:<branch> [--working-tree carry|clean] --model <tier> [-f|--follow|--watch] [--tools] [--parent-session <session-id>] "<prompt>"',
|
|
160
172
|
" r5dctl session status <session-id>",
|
|
161
173
|
' r5dctl session prompt --worker <label> --mode <mode> --model <tier> <session-id> "<prompt>"',
|
|
162
174
|
" r5dctl session stop <session-id>",
|
|
175
|
+
" r5dctl session process-log <run-id> [--tail <n>] [--grep <pattern>]",
|
|
163
176
|
"",
|
|
164
177
|
"Create, inspect, resume, and stop ordinary agent sessions.",
|
|
178
|
+
"A new worktree starts with the source checkout's uncommitted changes (--working-tree carry, the default);",
|
|
179
|
+
"--working-tree clean checks out the source branch's current commit only. Start and status print the mode, never a commit hash.",
|
|
165
180
|
"Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
|
|
181
|
+
"process-log prints a shell run's stored stdout/stderr from the server: the whole log, its last n lines,",
|
|
182
|
+
"or the lines matching a JavaScript regular expression. The log is not a file on the worker.",
|
|
166
183
|
""
|
|
167
184
|
].join("\n");
|
|
168
185
|
const SHARED_HELP_ENTRIES = [
|
|
@@ -192,6 +209,21 @@ const SHARED_HELP_ENTRIES = [
|
|
|
192
209
|
{ section: "projects", usage: "delete project <namespace/name|id>", description: "Delete a project." },
|
|
193
210
|
{ section: "branches", usage: "-p <project> get branches", description: "List branches for a project." },
|
|
194
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
|
+
},
|
|
195
227
|
{
|
|
196
228
|
section: "envs",
|
|
197
229
|
usage: "-p <project> get envs [--show-values]",
|
|
@@ -262,8 +294,8 @@ const SHARED_HELP_ENTRIES = [
|
|
|
262
294
|
},
|
|
263
295
|
{
|
|
264
296
|
section: "sessions",
|
|
265
|
-
usage: '-p <project> session start --worker <label> (--worktree <branch>|--new-worktree <branch> --source worktree:<branch>) --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
266
|
-
description: "Start an ordinary agent session on an explicit existing or new worktree."
|
|
297
|
+
usage: '-p <project> session start --worker <label> (--worktree <branch>|--new-worktree <branch> --source worktree:<branch> [--working-tree carry|clean]) --model <tier> [-f|--follow|--watch] [--tools] [--parent-session <session-id>] "<prompt>"',
|
|
298
|
+
description: "Start an ordinary agent session on an explicit existing or new worktree. A new worktree carries the source checkout's uncommitted changes unless --working-tree clean is passed. --parent-session links it like start_subagent: the parent is notified when the run finishes."
|
|
267
299
|
},
|
|
268
300
|
{
|
|
269
301
|
section: "sessions",
|
|
@@ -280,6 +312,11 @@ const SHARED_HELP_ENTRIES = [
|
|
|
280
312
|
usage: "session stop <session-id>",
|
|
281
313
|
description: "Stop an active session generation."
|
|
282
314
|
},
|
|
315
|
+
{
|
|
316
|
+
section: "sessions",
|
|
317
|
+
usage: "session process-log <run-id> [--tail <n>] [--grep <pattern>]",
|
|
318
|
+
description: "Print a shell run's stored stdout/stderr from the server: the whole log, its last lines, or lines matching a regular expression."
|
|
319
|
+
},
|
|
283
320
|
{
|
|
284
321
|
section: "sessions",
|
|
285
322
|
usage: '-s <session-id> answer-questions --worker <label> --mode <mode> --model <tier> -a1 "<answer>" ...',
|
|
@@ -533,8 +570,8 @@ function parseCommandOperands(args, valueFlags, booleanFlags = /* @__PURE__ */ n
|
|
|
533
570
|
function requireWorker(values) {
|
|
534
571
|
return requireValue(values.get("--worker")?.trim(), "--worker <label> is required");
|
|
535
572
|
}
|
|
536
|
-
function parseWorktreeSource(rawValue) {
|
|
537
|
-
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);
|
|
538
575
|
const separator = raw.indexOf(":");
|
|
539
576
|
const type = raw.slice(0, separator);
|
|
540
577
|
const branchName = raw.slice(separator + 1).trim();
|
|
@@ -543,6 +580,12 @@ function parseWorktreeSource(rawValue) {
|
|
|
543
580
|
}
|
|
544
581
|
return { type, branchName };
|
|
545
582
|
}
|
|
583
|
+
function parseWorkingTreeMode(rawValue) {
|
|
584
|
+
if (rawValue === void 0) return "carry";
|
|
585
|
+
const mode = rawValue.trim();
|
|
586
|
+
if (mode !== "carry" && mode !== "clean") throw new Error("Invalid --working-tree. Expected carry or clean");
|
|
587
|
+
return mode;
|
|
588
|
+
}
|
|
546
589
|
function requireModel(value) {
|
|
547
590
|
const model = requireValue(value?.trim(), "--model <tier> is required");
|
|
548
591
|
if (!MODEL_TIERS.has(model)) throw new Error("Invalid --model. Expected one of: low, medium, high, max");
|
|
@@ -555,6 +598,103 @@ function requireSessionMode(value) {
|
|
|
555
598
|
}
|
|
556
599
|
return mode;
|
|
557
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
|
+
}
|
|
558
698
|
function rejectSessionRunGlobalScopes(options, allowProject) {
|
|
559
699
|
if (!allowProject && options.project) throw new Error("--project/-p is only supported for `session start`");
|
|
560
700
|
if (options.branch) throw new Error("--branch/-b is not supported for `session`; select the worktree explicitly");
|
|
@@ -568,7 +708,7 @@ function parseSessionRunCommand(options, args) {
|
|
|
568
708
|
const project = requireValue(options.project, "--project/-p is required for `session start`");
|
|
569
709
|
const parsed = parseCommandOperands(
|
|
570
710
|
commandArgs,
|
|
571
|
-
/* @__PURE__ */ new Set(["--worker", "--worktree", "--new-worktree", "--source", "--model"]),
|
|
711
|
+
/* @__PURE__ */ new Set(["--worker", "--worktree", "--new-worktree", "--source", "--working-tree", "--model", "--parent-session"]),
|
|
572
712
|
/* @__PURE__ */ new Map([
|
|
573
713
|
["-f", "--follow"],
|
|
574
714
|
["--follow", "--follow"],
|
|
@@ -587,6 +727,9 @@ function parseSessionRunCommand(options, args) {
|
|
|
587
727
|
}
|
|
588
728
|
const sourceValue = parsed.values.get("--source");
|
|
589
729
|
if (worktree && sourceValue) throw new Error("--source is only supported with --new-worktree");
|
|
730
|
+
const workingTreeValue = parsed.values.get("--working-tree");
|
|
731
|
+
if (worktree && workingTreeValue !== void 0) throw new Error("--working-tree is only supported with --new-worktree");
|
|
732
|
+
const parentSessionId = parsed.values.get("--parent-session")?.trim();
|
|
590
733
|
return {
|
|
591
734
|
kind: "start",
|
|
592
735
|
project,
|
|
@@ -595,7 +738,8 @@ function parseSessionRunCommand(options, args) {
|
|
|
595
738
|
prompt,
|
|
596
739
|
follow: parsed.booleans.has("--follow"),
|
|
597
740
|
includeTools: parsed.booleans.has("--tools"),
|
|
598
|
-
...
|
|
741
|
+
...parentSessionId ? { parentSessionId } : {},
|
|
742
|
+
...worktree ? { worktree } : { newWorktree, source: parseWorktreeSource(sourceValue), workingTree: parseWorkingTreeMode(workingTreeValue) }
|
|
599
743
|
};
|
|
600
744
|
}
|
|
601
745
|
rejectSessionRunGlobalScopes(options, false);
|
|
@@ -621,6 +765,16 @@ function parseSessionRunCommand(options, args) {
|
|
|
621
765
|
}
|
|
622
766
|
throw new Error(`Unknown session command: ${subcommand}`);
|
|
623
767
|
}
|
|
768
|
+
function parseSessionProcessLogArgs(args) {
|
|
769
|
+
const parsed = parseCommandOperands(args, /* @__PURE__ */ new Set(["--tail", "--grep"]), /* @__PURE__ */ new Map(), "session process-log");
|
|
770
|
+
const runId = requireValue(parsed.positionals[0], "Process run id is required for `session process-log`");
|
|
771
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected session process-log argument: ${parsed.positionals[1]}`);
|
|
772
|
+
const tailValue = parsed.values.get("--tail");
|
|
773
|
+
const tail = tailValue === void 0 ? void 0 : Number(tailValue);
|
|
774
|
+
if (tail !== void 0 && (!Number.isInteger(tail) || tail < 1)) throw new Error("--tail must be a positive integer");
|
|
775
|
+
const grep = parsed.values.get("--grep");
|
|
776
|
+
return { runId, ...tail !== void 0 ? { tail } : {}, ...grep !== void 0 ? { grep } : {} };
|
|
777
|
+
}
|
|
624
778
|
function assertAuthLoginArgs(args) {
|
|
625
779
|
const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
|
|
626
780
|
const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
|
|
@@ -1447,10 +1601,34 @@ function renderWorkspaceStatus(status) {
|
|
|
1447
1601
|
`Workspace HEAD: ${status.head ?? "(not initialized)"}`,
|
|
1448
1602
|
`Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
|
|
1449
1603
|
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1450
|
-
...status.workers.
|
|
1451
|
-
|
|
1452
|
-
|
|
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
|
+
])
|
|
1453
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
|
+
}
|
|
1454
1632
|
return `${lines.join("\n")}
|
|
1455
1633
|
`;
|
|
1456
1634
|
}
|
|
@@ -1678,6 +1856,32 @@ function renderProcessList(processes, nowMs = Date.now()) {
|
|
|
1678
1856
|
function renderProcessHistory(processes, nowMs = Date.now()) {
|
|
1679
1857
|
return processes.length === 0 ? "No process history.\n" : renderProcessList(processes, nowMs);
|
|
1680
1858
|
}
|
|
1859
|
+
function renderProcessLog(log) {
|
|
1860
|
+
const sections = [];
|
|
1861
|
+
for (const [name, stream] of [
|
|
1862
|
+
["stdout", log.stdout],
|
|
1863
|
+
["stderr", log.stderr]
|
|
1864
|
+
]) {
|
|
1865
|
+
if (stream.text.length === 0) continue;
|
|
1866
|
+
sections.push(`==> ${name} <==
|
|
1867
|
+
${stream.text.endsWith("\n") ? stream.text : `${stream.text}
|
|
1868
|
+
`}`);
|
|
1869
|
+
}
|
|
1870
|
+
const selection = [
|
|
1871
|
+
...log.grep !== void 0 ? [`lines matching /${log.grep}/`] : [],
|
|
1872
|
+
...log.tailLines !== void 0 ? [`last ${log.tailLines} lines`] : []
|
|
1873
|
+
];
|
|
1874
|
+
const cut = [log.stdout.truncated ? "stdout" : void 0, log.stderr.truncated ? "stderr" : void 0].filter(Boolean);
|
|
1875
|
+
const summary = [
|
|
1876
|
+
`Run ${log.runId} (${log.status}${log.exitCode !== void 0 ? `, exit ${log.exitCode}` : ""}): `,
|
|
1877
|
+
`stdout ${log.stdout.totalBytes} bytes, stderr ${log.stderr.totalBytes} bytes`,
|
|
1878
|
+
selection.length > 0 ? `; showing ${selection.join(", ")}` : "",
|
|
1879
|
+
cut.length > 0 ? `; ${cut.join(" and ")} cut to ${log.maxBytesPerStream} bytes, narrow with --tail or --grep` : "",
|
|
1880
|
+
sections.length === 0 ? "; no output to show" : ""
|
|
1881
|
+
].join("");
|
|
1882
|
+
return { text: sections.join(""), summary: `${summary}
|
|
1883
|
+
` };
|
|
1884
|
+
}
|
|
1681
1885
|
function renderProcessInspection(process2) {
|
|
1682
1886
|
return [
|
|
1683
1887
|
`Run: ${process2.runId}`,
|
|
@@ -1831,13 +2035,22 @@ function formatStatusValue(value) {
|
|
|
1831
2035
|
}
|
|
1832
2036
|
return value === void 0 || value === null ? void 0 : String(value);
|
|
1833
2037
|
}
|
|
2038
|
+
function pushWorktreeOriginLines(lines, session) {
|
|
2039
|
+
const source = responseString(session, "sourceBranch");
|
|
2040
|
+
const mode = responseString(session, "workingTree");
|
|
2041
|
+
if (!source || !mode) return;
|
|
2042
|
+
lines.push(`Source: ${source}`);
|
|
2043
|
+
const detail = mode === "carry" ? `carried ${source}'s uncommitted changes at launch` : mode === "clean" ? `clean checkout of ${source}'s commit at launch` : void 0;
|
|
2044
|
+
lines.push(`Working tree: ${mode}${detail ? ` (${detail})` : ""}`);
|
|
2045
|
+
}
|
|
1834
2046
|
function renderSessionRunStart(session) {
|
|
1835
2047
|
const lines = [
|
|
1836
2048
|
`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`,
|
|
1837
2049
|
`Status: ${formatStatusValue(session.status) ?? "queued"}`,
|
|
1838
|
-
`Worktree: ${responseString(session, "branchName") ?? "(provisioning)"}
|
|
1839
|
-
`Worker: ${responseString(session, "worker") ?? "(unassigned)"}`
|
|
2050
|
+
`Worktree: ${responseString(session, "branchName") ?? "(provisioning)"}`
|
|
1840
2051
|
];
|
|
2052
|
+
pushWorktreeOriginLines(lines, session);
|
|
2053
|
+
lines.push(`Worker: ${responseString(session, "worker") ?? "(unassigned)"}`);
|
|
1841
2054
|
const sessionUrl = responseString(session, "sessionUrl");
|
|
1842
2055
|
if (sessionUrl) lines.push(`URL: ${sessionUrl}`);
|
|
1843
2056
|
const sessionId = responseString(session, "sessionId");
|
|
@@ -1845,29 +2058,50 @@ function renderSessionRunStart(session) {
|
|
|
1845
2058
|
return `${lines.join("\n")}
|
|
1846
2059
|
`;
|
|
1847
2060
|
}
|
|
2061
|
+
function formatBranchStateHint(branch) {
|
|
2062
|
+
const target = branch ?? "<branch>";
|
|
2063
|
+
const commands = [
|
|
2064
|
+
`git merge-base <your-branch> ${target}`,
|
|
2065
|
+
`git log --oneline <your-branch>..${target}`,
|
|
2066
|
+
"git status in the branch folder"
|
|
2067
|
+
];
|
|
2068
|
+
return `inspect the worktree with git (${commands.join("; ")})`;
|
|
2069
|
+
}
|
|
2070
|
+
function formatQueuedAt(value) {
|
|
2071
|
+
const time = Date.parse(value);
|
|
2072
|
+
return Number.isNaN(time) ? value : new Date(time).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
2073
|
+
}
|
|
2074
|
+
function formatProvisioningQueueLine(session) {
|
|
2075
|
+
const position = session.provisioningQueuePosition;
|
|
2076
|
+
const queuedAt = responseString(session, "provisioningQueuedAt");
|
|
2077
|
+
if (typeof position !== "number" || !Number.isInteger(position) || position < 0 || !queuedAt) return void 0;
|
|
2078
|
+
const since = formatQueuedAt(queuedAt);
|
|
2079
|
+
if (position === 0) return `Provisioning: worker started the branch operation at ${since}`;
|
|
2080
|
+
return `Provisioning: queued behind ${position} worker operation${position === 1 ? "" : "s"} since ${since}`;
|
|
2081
|
+
}
|
|
1848
2082
|
function renderSessionRunStatus(session) {
|
|
1849
2083
|
const status = formatStatusValue(session.status) ?? "unknown";
|
|
1850
2084
|
const lines = [`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`, `Status: ${status}`];
|
|
1851
2085
|
const runStatus = formatStatusValue(session.runStatus);
|
|
1852
2086
|
if (runStatus) lines.push(`Run: ${runStatus}`);
|
|
2087
|
+
const provisioningQueue = formatProvisioningQueueLine(session);
|
|
2088
|
+
if (provisioningQueue) lines.push(provisioningQueue);
|
|
1853
2089
|
const promptDisposition = responseString(session, "promptDisposition");
|
|
1854
2090
|
if (promptDisposition) lines.push(`Prompt: ${promptDisposition}`);
|
|
1855
2091
|
const branch = responseString(session, "branchName");
|
|
1856
2092
|
if (branch) lines.push(`Worktree: ${branch}`);
|
|
2093
|
+
pushWorktreeOriginLines(lines, session);
|
|
1857
2094
|
for (const [label, key] of [
|
|
1858
|
-
["Workspace head", "workspaceHead"],
|
|
1859
|
-
["Baseline", "baselineCommit"],
|
|
1860
|
-
["Head", "headCommit"],
|
|
1861
2095
|
["Worker", "worker"],
|
|
2096
|
+
["Parent session", "parentSessionId"],
|
|
1862
2097
|
["Session", "sessionUrl"]
|
|
1863
2098
|
]) {
|
|
1864
2099
|
const value = responseString(session, key);
|
|
1865
2100
|
if (value) lines.push(`${label}: ${value}`);
|
|
1866
2101
|
}
|
|
2102
|
+
lines.push(`Branch state: ${formatBranchStateHint(branch)}`);
|
|
1867
2103
|
const error = responseString(session, "error");
|
|
1868
2104
|
if (error) lines.push(`Error: ${error}`);
|
|
1869
|
-
const headCommitError = responseString(session, "headCommitError");
|
|
1870
|
-
if (headCommitError) lines.push(`Head error: ${headCommitError}`);
|
|
1871
2105
|
const diffSummary = responseString(session, "diffSummary");
|
|
1872
2106
|
if (diffSummary) lines.push("", diffSummary);
|
|
1873
2107
|
const summary = responseString(session, "summary");
|
|
@@ -1885,13 +2119,14 @@ async function dispatchSessionRunCommand(client, command) {
|
|
|
1885
2119
|
prompt: command.prompt,
|
|
1886
2120
|
worker: command.worker,
|
|
1887
2121
|
model: command.model,
|
|
1888
|
-
requestId: (0, import_node_crypto.randomUUID)()
|
|
2122
|
+
requestId: (0, import_node_crypto.randomUUID)(),
|
|
2123
|
+
...command.parentSessionId ? { parentSessionId: command.parentSessionId } : {}
|
|
1889
2124
|
};
|
|
1890
2125
|
let input;
|
|
1891
2126
|
if (command.worktree !== void 0) {
|
|
1892
2127
|
input = { ...commonInput, worktree: command.worktree };
|
|
1893
2128
|
} else if (command.newWorktree !== void 0 && command.source !== void 0) {
|
|
1894
|
-
input = { ...commonInput, newWorktree: command.newWorktree, source: command.source };
|
|
2129
|
+
input = { ...commonInput, newWorktree: command.newWorktree, source: command.source, workingTree: command.workingTree };
|
|
1895
2130
|
} else {
|
|
1896
2131
|
throw new Error("Session start requires an existing or new worktree");
|
|
1897
2132
|
}
|
|
@@ -2531,6 +2766,17 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
2531
2766
|
`);
|
|
2532
2767
|
return;
|
|
2533
2768
|
}
|
|
2769
|
+
if (first === "session" && second === "process-log") {
|
|
2770
|
+
const selection = parseSessionProcessLogArgs(args.slice(2));
|
|
2771
|
+
const log = await client.processes.log(selection.runId, {
|
|
2772
|
+
...selection.tail !== void 0 ? { tail: selection.tail } : {},
|
|
2773
|
+
...selection.grep !== void 0 ? { grep: selection.grep } : {}
|
|
2774
|
+
});
|
|
2775
|
+
const rendered = renderProcessLog(log);
|
|
2776
|
+
write(log, rendered.text);
|
|
2777
|
+
if (!json) process.stderr.write(rendered.summary);
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2534
2780
|
if (first === "sessions" && second === "recent") {
|
|
2535
2781
|
const sessions = await client.sessions.recent(parseRecentSessionArgs(args.slice(2)));
|
|
2536
2782
|
write(sessions, renderRecentSessionList(sessions));
|
|
@@ -2560,11 +2806,7 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
2560
2806
|
if (first === "projects" && second === "branches" && third === "list" || first === "get" && second === "branches") {
|
|
2561
2807
|
const projectRef = requireValue(first === "get" ? args[2] : args[3], "Missing project reference");
|
|
2562
2808
|
const branches = await client.projects.branches.list(projectRef);
|
|
2563
|
-
write(
|
|
2564
|
-
branches,
|
|
2565
|
-
branches.length === 0 ? "No branches found.\n" : `${branches.map((branch) => `${branch.branchName} sessions=${branch.sessionCount}`).join("\n")}
|
|
2566
|
-
`
|
|
2567
|
-
);
|
|
2809
|
+
write(branches, renderBranchList(branches));
|
|
2568
2810
|
return;
|
|
2569
2811
|
}
|
|
2570
2812
|
if (first === "projects" && second === "branches" && third === "describe" || first === "describe" && second === "branch") {
|
|
@@ -2579,6 +2821,40 @@ Sessions: ${result.sessions.length}
|
|
|
2579
2821
|
`);
|
|
2580
2822
|
return;
|
|
2581
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
|
+
}
|
|
2582
2858
|
if (first === "get" && second === "envs") {
|
|
2583
2859
|
const projectRef = requireValue(args[2], "Missing project reference");
|
|
2584
2860
|
const options = parseGetEnvsArgs(args.slice(3));
|
|
@@ -2995,6 +3271,19 @@ function resolveCommandExecution(options, rest) {
|
|
|
2995
3271
|
pluginArgs: ["describe", "branch", options.project, branch]
|
|
2996
3272
|
};
|
|
2997
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
|
+
}
|
|
2998
3287
|
if (target === "session") {
|
|
2999
3288
|
const sessionId = commandArgs[1] ?? options.session;
|
|
3000
3289
|
if (!sessionId) {
|
|
@@ -3027,6 +3316,23 @@ function resolveCommandExecution(options, rest) {
|
|
|
3027
3316
|
pluginArgs: ["create", "session", projectRef, branch, ...flagArgs]
|
|
3028
3317
|
};
|
|
3029
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
|
+
}
|
|
3030
3336
|
throw new Error("Unknown create command");
|
|
3031
3337
|
}
|
|
3032
3338
|
if (command === "update") {
|
|
@@ -3077,6 +3383,23 @@ function resolveCommandExecution(options, rest) {
|
|
|
3077
3383
|
pluginArgs: ["delete", "session", sessionId]
|
|
3078
3384
|
};
|
|
3079
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
|
+
}
|
|
3080
3403
|
throw new Error("Unknown delete command");
|
|
3081
3404
|
}
|
|
3082
3405
|
if (command === "conversation") {
|
|
@@ -3138,6 +3461,20 @@ function resolveCommandExecution(options, rest) {
|
|
|
3138
3461
|
};
|
|
3139
3462
|
}
|
|
3140
3463
|
if (command === "session") {
|
|
3464
|
+
if (commandArgs[0] === "process-log") {
|
|
3465
|
+
rejectSessionRunGlobalScopes(options, false);
|
|
3466
|
+
const selection = parseSessionProcessLogArgs(commandArgs.slice(1));
|
|
3467
|
+
return {
|
|
3468
|
+
kind: "plugin",
|
|
3469
|
+
pluginArgs: [
|
|
3470
|
+
"session",
|
|
3471
|
+
"process-log",
|
|
3472
|
+
selection.runId,
|
|
3473
|
+
...selection.tail !== void 0 ? [`--tail=${selection.tail}`] : [],
|
|
3474
|
+
...selection.grep !== void 0 ? [`--grep=${selection.grep}`] : []
|
|
3475
|
+
]
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3141
3478
|
return { kind: "session-run", command: parseSessionRunCommand(options, commandArgs) };
|
|
3142
3479
|
}
|
|
3143
3480
|
if (command === "answer-questions") {
|
|
@@ -3290,7 +3627,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3290
3627
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3291
3628
|
0 && (module.exports = {
|
|
3292
3629
|
advanceTransientDevicePollBackoff,
|
|
3630
|
+
branchDeletionConfirmationMessage,
|
|
3293
3631
|
collectK8sUsage,
|
|
3632
|
+
createBranchThroughPlatform,
|
|
3633
|
+
deleteBranchThroughPlatform,
|
|
3294
3634
|
dispatchSessionRunCommand,
|
|
3295
3635
|
followR5dctlSession,
|
|
3296
3636
|
formatK8sCpu,
|
|
@@ -3302,10 +3642,13 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3302
3642
|
getR5dctlVersion,
|
|
3303
3643
|
getTransientDevicePollDelay,
|
|
3304
3644
|
handleAuthLogin,
|
|
3645
|
+
isUnsupportedServerRoute,
|
|
3305
3646
|
main,
|
|
3306
3647
|
parseAnswerEnvRequestCommandArgs,
|
|
3307
3648
|
parseAnswerFlags,
|
|
3308
3649
|
parseAnswerQuestionsCommandArgs,
|
|
3650
|
+
parseBranchCreateArgs,
|
|
3651
|
+
parseBranchDeleteArgs,
|
|
3309
3652
|
parseConversationForkArgs,
|
|
3310
3653
|
parseConversationInspectWorkArgs,
|
|
3311
3654
|
parseConversationOverviewArgs,
|
|
@@ -3318,10 +3661,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3318
3661
|
parsePromptArgs,
|
|
3319
3662
|
parsePsHistoryArgs,
|
|
3320
3663
|
parsePsListArgs,
|
|
3664
|
+
parseSessionProcessLogArgs,
|
|
3321
3665
|
parseSessionRunCommand,
|
|
3322
3666
|
parseSetEnvArgs,
|
|
3323
3667
|
parseShellArgs,
|
|
3324
3668
|
readDotenvFile,
|
|
3669
|
+
renderBranchDeletion,
|
|
3670
|
+
renderBranchDeletionPreview,
|
|
3671
|
+
renderBranchList,
|
|
3672
|
+
renderBranchOperation,
|
|
3325
3673
|
renderConversationForkResponse,
|
|
3326
3674
|
renderConversationHeadResponse,
|
|
3327
3675
|
renderConversationNodeResponse,
|
|
@@ -3334,6 +3682,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3334
3682
|
renderProcessHistory,
|
|
3335
3683
|
renderProcessInspection,
|
|
3336
3684
|
renderProcessList,
|
|
3685
|
+
renderProcessLog,
|
|
3337
3686
|
renderSessionRunStart,
|
|
3338
3687
|
renderSessionRunStatus,
|
|
3339
3688
|
renderWorkspaceStatus,
|
package/dist/cjs/package.json
CHANGED