@ricsam/r5dctl 0.0.60 → 0.0.62
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 +20 -11
- package/dist/cjs/cli.cjs +691 -235
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +681 -230
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +114 -48
- package/package.json +2 -2
package/dist/cjs/cli.cjs
CHANGED
|
@@ -30,7 +30,9 @@ var cli_exports = {};
|
|
|
30
30
|
__export(cli_exports, {
|
|
31
31
|
advanceTransientDevicePollBackoff: () => advanceTransientDevicePollBackoff,
|
|
32
32
|
collectK8sUsage: () => collectK8sUsage,
|
|
33
|
-
|
|
33
|
+
dispatchMergeCommand: () => dispatchMergeCommand,
|
|
34
|
+
dispatchSessionRunCommand: () => dispatchSessionRunCommand,
|
|
35
|
+
followR5dctlSession: () => followR5dctlSession,
|
|
34
36
|
formatK8sCpu: () => formatK8sCpu,
|
|
35
37
|
formatK8sMemory: () => formatK8sMemory,
|
|
36
38
|
formatProcessAge: () => formatProcessAge,
|
|
@@ -41,8 +43,9 @@ __export(cli_exports, {
|
|
|
41
43
|
getTransientDevicePollDelay: () => getTransientDevicePollDelay,
|
|
42
44
|
handleAuthLogin: () => handleAuthLogin,
|
|
43
45
|
main: () => main,
|
|
46
|
+
parseAnswerEnvRequestCommandArgs: () => parseAnswerEnvRequestCommandArgs,
|
|
44
47
|
parseAnswerFlags: () => parseAnswerFlags,
|
|
45
|
-
|
|
48
|
+
parseAnswerQuestionsCommandArgs: () => parseAnswerQuestionsCommandArgs,
|
|
46
49
|
parseConversationRenderArgs: () => parseConversationRenderArgs,
|
|
47
50
|
parseConversationWorkDetailArgs: () => parseConversationWorkDetailArgs,
|
|
48
51
|
parseEnvFlags: () => parseEnvFlags,
|
|
@@ -50,15 +53,14 @@ __export(cli_exports, {
|
|
|
50
53
|
parseGetEnvsArgs: () => parseGetEnvsArgs,
|
|
51
54
|
parseGlobalArgs: () => parseGlobalArgs,
|
|
52
55
|
parseK8sUsageArgs: () => parseK8sUsageArgs,
|
|
56
|
+
parseMergeCommand: () => parseMergeCommand,
|
|
53
57
|
parsePromptArgs: () => parsePromptArgs,
|
|
54
58
|
parsePsHistoryArgs: () => parsePsHistoryArgs,
|
|
55
59
|
parsePsListArgs: () => parsePsListArgs,
|
|
60
|
+
parseSessionRunCommand: () => parseSessionRunCommand,
|
|
56
61
|
parseSetEnvArgs: () => parseSetEnvArgs,
|
|
57
62
|
parseShellArgs: () => parseShellArgs,
|
|
58
63
|
readDotenvFile: () => readDotenvFile,
|
|
59
|
-
renderBranchAgentMerge: () => renderBranchAgentMerge,
|
|
60
|
-
renderBranchAgentStart: () => renderBranchAgentStart,
|
|
61
|
-
renderBranchAgentStatus: () => renderBranchAgentStatus,
|
|
62
64
|
renderConversationNodeResponse: () => renderConversationNodeResponse,
|
|
63
65
|
renderConversationOverviewResponse: () => renderConversationOverviewResponse,
|
|
64
66
|
renderConversationResponse: () => renderConversationResponse,
|
|
@@ -69,8 +71,11 @@ __export(cli_exports, {
|
|
|
69
71
|
renderProcessHistory: () => renderProcessHistory,
|
|
70
72
|
renderProcessInspection: () => renderProcessInspection,
|
|
71
73
|
renderProcessList: () => renderProcessList,
|
|
74
|
+
renderSessionRunStart: () => renderSessionRunStart,
|
|
75
|
+
renderSessionRunStatus: () => renderSessionRunStatus,
|
|
72
76
|
renderWorkspaceStatus: () => renderWorkspaceStatus,
|
|
73
77
|
renderWorkspaceSync: () => renderWorkspaceSync,
|
|
78
|
+
renderWorktreeMerge: () => renderWorktreeMerge,
|
|
74
79
|
resolveCommandExecution: () => resolveCommandExecution,
|
|
75
80
|
runR5dctlCli: () => runR5dctlCli,
|
|
76
81
|
summarizeEnvData: () => summarizeEnvData,
|
|
@@ -92,15 +97,12 @@ const CHAT_MODES = /* @__PURE__ */ new Set([
|
|
|
92
97
|
"plan",
|
|
93
98
|
"build",
|
|
94
99
|
"agent",
|
|
95
|
-
"explore",
|
|
96
|
-
"test",
|
|
97
|
-
"research",
|
|
98
100
|
"security_review",
|
|
99
101
|
"large_diff_remediation",
|
|
100
102
|
"merge_conflict_resolution"
|
|
101
103
|
]);
|
|
104
|
+
const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["ask", "plan", "build", "agent"]);
|
|
102
105
|
const MODEL_TIERS = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
|
|
103
|
-
const AGENT_TYPES = /* @__PURE__ */ new Set(["research", "debug", "test"]);
|
|
104
106
|
const R5DCTL_PACKAGE_NAME = "@ricsam/r5dctl";
|
|
105
107
|
const CLI_GLOBAL_OPTION_HELP = [
|
|
106
108
|
"--base-url <url>",
|
|
@@ -154,17 +156,24 @@ const K8S_HELP_TEXT = [
|
|
|
154
156
|
"Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
|
|
155
157
|
""
|
|
156
158
|
].join("\n");
|
|
157
|
-
const
|
|
159
|
+
const SESSION_RUN_HELP_TEXT = [
|
|
158
160
|
"Usage:",
|
|
159
|
-
' r5dctl -p <project>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
" r5dctl
|
|
164
|
-
' r5dctl agent finish-merge --worker <label> <resolver-session-id> --summary "<summary>"',
|
|
161
|
+
' r5dctl -p <project> session start --worker <label> --worktree <branch> --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
162
|
+
' r5dctl -p <project> session start --worker <label> --new-worktree <branch> --source worktree:<branch> --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
163
|
+
" r5dctl session status <session-id>",
|
|
164
|
+
' r5dctl session prompt --worker <label> --mode <mode> --model <tier> <session-id> "<prompt>"',
|
|
165
|
+
" r5dctl session stop <session-id>",
|
|
165
166
|
"",
|
|
166
|
-
"Create, inspect, resume,
|
|
167
|
-
"
|
|
167
|
+
"Create, inspect, resume, and stop ordinary agent sessions.",
|
|
168
|
+
"Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
|
|
169
|
+
""
|
|
170
|
+
].join("\n");
|
|
171
|
+
const MERGE_HELP_TEXT = [
|
|
172
|
+
"Usage:",
|
|
173
|
+
" r5dctl -p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] <source-worktree>",
|
|
174
|
+
' r5dctl -p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
175
|
+
"",
|
|
176
|
+
"Merge a managed worktree into its recorded parent worktree.",
|
|
168
177
|
""
|
|
169
178
|
].join("\n");
|
|
170
179
|
const SHARED_HELP_ENTRIES = [
|
|
@@ -234,7 +243,7 @@ const SHARED_HELP_ENTRIES = [
|
|
|
234
243
|
{ section: "sessions", usage: "-s <session-id> delete session", description: "Delete a session." },
|
|
235
244
|
{
|
|
236
245
|
section: "sessions",
|
|
237
|
-
usage: "-s <session-id> conversation [--raw] [--tools] [--system]",
|
|
246
|
+
usage: "-s <session-id> conversation [--raw] [--tools] [--system] [-f|--follow|--watch]",
|
|
238
247
|
description: "Read the agent request transcript in human-readable form."
|
|
239
248
|
},
|
|
240
249
|
{
|
|
@@ -254,17 +263,32 @@ const SHARED_HELP_ENTRIES = [
|
|
|
254
263
|
},
|
|
255
264
|
{
|
|
256
265
|
section: "sessions",
|
|
257
|
-
usage: '-
|
|
258
|
-
description: "
|
|
266
|
+
usage: '-p <project> session start --worker <label> (--worktree <branch>|--new-worktree <branch> --source worktree:<branch>) --model <tier> [-f|--follow|--watch] [--tools] "<prompt>"',
|
|
267
|
+
description: "Start an ordinary agent session on an explicit existing or new worktree."
|
|
259
268
|
},
|
|
260
269
|
{
|
|
261
270
|
section: "sessions",
|
|
262
|
-
usage:
|
|
271
|
+
usage: "session status <session-id>",
|
|
272
|
+
description: "Show asynchronous session execution status."
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
section: "sessions",
|
|
276
|
+
usage: 'session prompt --worker <label> --mode <mode> --model <tier> <session-id> "<prompt>"',
|
|
277
|
+
description: "Queue a prompt or start a new session generation."
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
section: "sessions",
|
|
281
|
+
usage: "session stop <session-id>",
|
|
282
|
+
description: "Stop an active session generation."
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
section: "sessions",
|
|
286
|
+
usage: '-s <session-id> answer-questions --worker <label> --mode <mode> --model <tier> -a1 "<answer>" ...',
|
|
263
287
|
description: "Answer pending questions in order."
|
|
264
288
|
},
|
|
265
289
|
{
|
|
266
290
|
section: "sessions",
|
|
267
|
-
usage: "-s <session-id> answer-env-request [-e KEY=value] [--context <text>]",
|
|
291
|
+
usage: "-s <session-id> answer-env-request --worker <label> --mode <mode> --model <tier> [-e KEY=value] [--context <text>]",
|
|
268
292
|
description: "Answer a pending environment variable request with values, context, or both."
|
|
269
293
|
},
|
|
270
294
|
{
|
|
@@ -289,34 +313,14 @@ const SHARED_HELP_ENTRIES = [
|
|
|
289
313
|
},
|
|
290
314
|
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." },
|
|
291
315
|
{
|
|
292
|
-
section: "
|
|
293
|
-
usage:
|
|
294
|
-
description: "
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
section: "agents",
|
|
298
|
-
usage: "agent status <session-id>",
|
|
299
|
-
description: "Show branch-agent execution, publication, and merge status."
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
section: "agents",
|
|
303
|
-
usage: 'agent prompt --worker <label> <session-id> "<prompt>"',
|
|
304
|
-
description: "Queue a prompt or resume an idle branch agent on the selected worker."
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
section: "agents",
|
|
308
|
-
usage: "agent merge --worker <label> <session-id>",
|
|
309
|
-
description: "Merge a terminal branch agent into its recorded parent worktree."
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
section: "agents",
|
|
313
|
-
usage: "agent stop <session-id>",
|
|
314
|
-
description: "Stop a branch agent or merge resolver."
|
|
316
|
+
section: "merge",
|
|
317
|
+
usage: "-p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] <source-worktree>",
|
|
318
|
+
description: "Merge a worktree into its recorded parent, with an explicit conflict strategy."
|
|
315
319
|
},
|
|
316
320
|
{
|
|
317
|
-
section: "
|
|
318
|
-
usage: '
|
|
319
|
-
description: "Publish and
|
|
321
|
+
section: "merge",
|
|
322
|
+
usage: '-p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
323
|
+
description: "Publish and finish a manually resolved conflict worktree."
|
|
320
324
|
}
|
|
321
325
|
];
|
|
322
326
|
const HELP_SECTION_ORDER = [
|
|
@@ -329,7 +333,7 @@ const HELP_SECTION_ORDER = [
|
|
|
329
333
|
"kubernetes",
|
|
330
334
|
"processes",
|
|
331
335
|
"shell",
|
|
332
|
-
"
|
|
336
|
+
"merge"
|
|
333
337
|
];
|
|
334
338
|
const HELP_SECTION_TITLES = {
|
|
335
339
|
auth: "Auth",
|
|
@@ -341,7 +345,7 @@ const HELP_SECTION_TITLES = {
|
|
|
341
345
|
kubernetes: "Kubernetes",
|
|
342
346
|
processes: "Processes",
|
|
343
347
|
shell: "Shell",
|
|
344
|
-
|
|
348
|
+
merge: "Worktree merging"
|
|
345
349
|
};
|
|
346
350
|
function getCliHelpText() {
|
|
347
351
|
const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
|
|
@@ -488,9 +492,10 @@ function parseOptionalFlagValue(args, flag, shortFlag) {
|
|
|
488
492
|
function hasBooleanFlag(args, flag) {
|
|
489
493
|
return args.includes(flag);
|
|
490
494
|
}
|
|
491
|
-
function
|
|
495
|
+
function parseCommandOperands(args, valueFlags, booleanFlags = /* @__PURE__ */ new Map(), family = "command", allowFlagLikeValuesAfter = Number.POSITIVE_INFINITY) {
|
|
492
496
|
const positionals = [];
|
|
493
497
|
const values = /* @__PURE__ */ new Map();
|
|
498
|
+
const booleans = /* @__PURE__ */ new Set();
|
|
494
499
|
let parseOptions = true;
|
|
495
500
|
for (let index = 0; index < args.length; index += 1) {
|
|
496
501
|
const arg = args[index];
|
|
@@ -498,6 +503,12 @@ function parseAgentOperands(args, valueFlags, options = {}) {
|
|
|
498
503
|
parseOptions = false;
|
|
499
504
|
continue;
|
|
500
505
|
}
|
|
506
|
+
const booleanFlag = parseOptions ? booleanFlags.get(arg) : void 0;
|
|
507
|
+
if (booleanFlag) {
|
|
508
|
+
if (booleans.has(booleanFlag)) throw new Error(`${booleanFlag} may only be provided once`);
|
|
509
|
+
booleans.add(booleanFlag);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
501
512
|
const inlineFlag = parseOptions ? [...valueFlags].find((flag) => arg.startsWith(`${flag}=`)) : void 0;
|
|
502
513
|
if (inlineFlag) {
|
|
503
514
|
if (values.has(inlineFlag)) throw new Error(`${inlineFlag} may only be provided once`);
|
|
@@ -514,18 +525,18 @@ function parseAgentOperands(args, valueFlags, options = {}) {
|
|
|
514
525
|
index += 1;
|
|
515
526
|
continue;
|
|
516
527
|
}
|
|
517
|
-
if (parseOptions && arg.startsWith("-") && positionals.length <
|
|
518
|
-
throw new Error(`Unknown
|
|
528
|
+
if (parseOptions && arg.startsWith("-") && positionals.length < allowFlagLikeValuesAfter) {
|
|
529
|
+
throw new Error(`Unknown ${family} flag: ${arg}`);
|
|
519
530
|
}
|
|
520
531
|
positionals.push(arg);
|
|
521
532
|
}
|
|
522
|
-
return { positionals, values };
|
|
533
|
+
return { positionals, values, booleans };
|
|
523
534
|
}
|
|
524
|
-
function
|
|
535
|
+
function requireWorker(values) {
|
|
525
536
|
return requireValue(values.get("--worker")?.trim(), "--worker <label> is required");
|
|
526
537
|
}
|
|
527
|
-
function
|
|
528
|
-
const raw = requireValue(
|
|
538
|
+
function parseWorktreeSource(rawValue) {
|
|
539
|
+
const raw = requireValue(rawValue?.trim(), "--source worktree:<branch> is required with --new-worktree");
|
|
529
540
|
const separator = raw.indexOf(":");
|
|
530
541
|
const type = raw.slice(0, separator);
|
|
531
542
|
const branchName = raw.slice(separator + 1).trim();
|
|
@@ -534,61 +545,119 @@ function requireAgentSource(values) {
|
|
|
534
545
|
}
|
|
535
546
|
return { type, branchName };
|
|
536
547
|
}
|
|
537
|
-
function
|
|
538
|
-
|
|
539
|
-
if (
|
|
540
|
-
|
|
548
|
+
function requireModel(value) {
|
|
549
|
+
const model = requireValue(value?.trim(), "--model <tier> is required");
|
|
550
|
+
if (!MODEL_TIERS.has(model)) throw new Error("Invalid --model. Expected one of: low, medium, high, max");
|
|
551
|
+
return model;
|
|
552
|
+
}
|
|
553
|
+
function requireSessionMode(value) {
|
|
554
|
+
const mode = requireValue(value?.trim(), "--mode <mode> is required");
|
|
555
|
+
if (!R5DCTL_SESSION_MODES.has(mode)) {
|
|
556
|
+
throw new Error("Invalid --mode. Expected one of: ask, plan, build, agent");
|
|
557
|
+
}
|
|
558
|
+
return mode;
|
|
559
|
+
}
|
|
560
|
+
function rejectSessionRunGlobalScopes(options, allowProject) {
|
|
561
|
+
if (!allowProject && options.project) throw new Error("--project/-p is only supported for `session start`");
|
|
562
|
+
if (options.branch) throw new Error("--branch/-b is not supported for `session`; select the worktree explicitly");
|
|
563
|
+
if (options.session) throw new Error("--session/-s is not supported for `session`; pass the session id explicitly");
|
|
541
564
|
}
|
|
542
|
-
function
|
|
543
|
-
const subcommand = requireValue(args[0], "Missing
|
|
565
|
+
function parseSessionRunCommand(options, args) {
|
|
566
|
+
const subcommand = requireValue(args[0], "Missing session command");
|
|
544
567
|
const commandArgs = args.slice(1);
|
|
545
568
|
if (subcommand === "start") {
|
|
546
|
-
|
|
547
|
-
const project = requireValue(options.project, "--project/-p is required for `
|
|
548
|
-
const parsed =
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
569
|
+
rejectSessionRunGlobalScopes(options, true);
|
|
570
|
+
const project = requireValue(options.project, "--project/-p is required for `session start`");
|
|
571
|
+
const parsed = parseCommandOperands(
|
|
572
|
+
commandArgs,
|
|
573
|
+
/* @__PURE__ */ new Set(["--worker", "--worktree", "--new-worktree", "--source", "--model"]),
|
|
574
|
+
/* @__PURE__ */ new Map([
|
|
575
|
+
["-f", "--follow"],
|
|
576
|
+
["--follow", "--follow"],
|
|
577
|
+
["--watch", "--follow"],
|
|
578
|
+
["--tools", "--tools"]
|
|
579
|
+
]),
|
|
580
|
+
"session start",
|
|
581
|
+
1
|
|
582
|
+
);
|
|
583
|
+
const prompt = parsed.positionals.join(" ").trim();
|
|
584
|
+
if (!prompt) throw new Error("Prompt is required for `session start`");
|
|
585
|
+
const worktree = parsed.values.get("--worktree")?.trim();
|
|
586
|
+
const newWorktree = parsed.values.get("--new-worktree")?.trim();
|
|
587
|
+
if (Boolean(worktree) === Boolean(newWorktree)) {
|
|
588
|
+
throw new Error("Use exactly one of --worktree <branch> or --new-worktree <branch>");
|
|
589
|
+
}
|
|
590
|
+
const sourceValue = parsed.values.get("--source");
|
|
591
|
+
if (worktree && sourceValue) throw new Error("--source is only supported with --new-worktree");
|
|
555
592
|
return {
|
|
556
593
|
kind: "start",
|
|
557
594
|
project,
|
|
558
|
-
worker:
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
595
|
+
worker: requireWorker(parsed.values),
|
|
596
|
+
model: requireModel(parsed.values.get("--model")),
|
|
597
|
+
prompt,
|
|
598
|
+
follow: parsed.booleans.has("--follow"),
|
|
599
|
+
includeTools: parsed.booleans.has("--tools"),
|
|
600
|
+
...worktree ? { worktree } : { newWorktree, source: parseWorktreeSource(sourceValue) }
|
|
562
601
|
};
|
|
563
602
|
}
|
|
564
|
-
|
|
603
|
+
rejectSessionRunGlobalScopes(options, false);
|
|
565
604
|
if (subcommand === "status" || subcommand === "stop") {
|
|
566
|
-
const parsed =
|
|
567
|
-
const sessionId = requireValue(parsed.positionals[0], `Session id is required for \`
|
|
568
|
-
if (parsed.positionals.length > 1) throw new Error(`Unexpected
|
|
605
|
+
const parsed = parseCommandOperands(commandArgs, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), `session ${subcommand}`);
|
|
606
|
+
const sessionId = requireValue(parsed.positionals[0], `Session id is required for \`session ${subcommand}\``);
|
|
607
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected session ${subcommand} argument: ${parsed.positionals[1]}`);
|
|
569
608
|
return { kind: subcommand, sessionId };
|
|
570
609
|
}
|
|
571
610
|
if (subcommand === "prompt") {
|
|
572
|
-
const parsed =
|
|
573
|
-
const sessionId = requireValue(parsed.positionals[0], "Session id is required for `
|
|
611
|
+
const parsed = parseCommandOperands(commandArgs, /* @__PURE__ */ new Set(["--worker", "--mode", "--model"]), /* @__PURE__ */ new Map(), "session prompt", 1);
|
|
612
|
+
const sessionId = requireValue(parsed.positionals[0], "Session id is required for `session prompt`");
|
|
574
613
|
const prompt = parsed.positionals.slice(1).join(" ").trim();
|
|
575
|
-
if (!prompt) throw new Error("Prompt is required for `
|
|
576
|
-
return {
|
|
614
|
+
if (!prompt) throw new Error("Prompt is required for `session prompt`");
|
|
615
|
+
return {
|
|
616
|
+
kind: "prompt",
|
|
617
|
+
worker: requireWorker(parsed.values),
|
|
618
|
+
sessionId,
|
|
619
|
+
prompt,
|
|
620
|
+
mode: requireSessionMode(parsed.values.get("--mode")),
|
|
621
|
+
model: requireModel(parsed.values.get("--model"))
|
|
622
|
+
};
|
|
577
623
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
624
|
+
throw new Error(`Unknown session command: ${subcommand}`);
|
|
625
|
+
}
|
|
626
|
+
function parseMergeCommand(options, args) {
|
|
627
|
+
const project = requireValue(options.project, "--project/-p is required for `merge`");
|
|
628
|
+
if (options.branch) throw new Error("--branch/-b is not supported for `merge`; pass the source worktree explicitly");
|
|
629
|
+
if (options.session) throw new Error("--session/-s is not supported for `merge`");
|
|
630
|
+
if (args[0] === "finish") {
|
|
631
|
+
const parsed2 = parseCommandOperands(args.slice(1), /* @__PURE__ */ new Set(["--worker", "--summary"]), /* @__PURE__ */ new Map(), "merge finish");
|
|
632
|
+
const conflictWorktree = requireValue(parsed2.positionals[0], "Conflict worktree is required for `merge finish`");
|
|
633
|
+
if (parsed2.positionals.length > 1) throw new Error(`Unexpected merge finish argument: ${parsed2.positionals[1]}`);
|
|
634
|
+
return {
|
|
635
|
+
kind: "finish",
|
|
636
|
+
project,
|
|
637
|
+
conflictWorktree,
|
|
638
|
+
worker: requireWorker(parsed2.values),
|
|
639
|
+
...parsed2.values.get("--summary")?.trim() ? { summary: parsed2.values.get("--summary").trim() } : {}
|
|
640
|
+
};
|
|
583
641
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
642
|
+
const parsed = parseCommandOperands(
|
|
643
|
+
args,
|
|
644
|
+
/* @__PURE__ */ new Set(["--worker"]),
|
|
645
|
+
/* @__PURE__ */ new Map([
|
|
646
|
+
["--create-conflict-worktree", "--create-conflict-worktree"],
|
|
647
|
+
["--launch-conflict-resolution-agent", "--launch-conflict-resolution-agent"]
|
|
648
|
+
]),
|
|
649
|
+
"merge"
|
|
650
|
+
);
|
|
651
|
+
const sourceWorktree = requireValue(parsed.positionals[0], "Source worktree is required for `merge`");
|
|
652
|
+
if (parsed.positionals.length > 1) throw new Error(`Unexpected merge argument: ${parsed.positionals[1]}`);
|
|
653
|
+
const createWorktree = parsed.booleans.has("--create-conflict-worktree");
|
|
654
|
+
const launchAgent = parsed.booleans.has("--launch-conflict-resolution-agent");
|
|
655
|
+
if (createWorktree && launchAgent) {
|
|
656
|
+
throw new Error("Use only one of --create-conflict-worktree or --launch-conflict-resolution-agent");
|
|
590
657
|
}
|
|
591
|
-
|
|
658
|
+
const conflictMode = createWorktree ? "worktree" : launchAgent ? "agent" : "none";
|
|
659
|
+
const worker = parsed.values.get("--worker")?.trim();
|
|
660
|
+
return { kind: "merge", project, sourceWorktree, conflictMode, ...worker ? { worker } : {} };
|
|
592
661
|
}
|
|
593
662
|
function assertAuthLoginArgs(args) {
|
|
594
663
|
const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
|
|
@@ -806,6 +875,55 @@ function parseAnswerFlags(args) {
|
|
|
806
875
|
}
|
|
807
876
|
return ordered.map(([, value]) => value);
|
|
808
877
|
}
|
|
878
|
+
function requireChatMode(value) {
|
|
879
|
+
const mode = requireValue(value?.trim(), "--mode <mode> is required");
|
|
880
|
+
if (!CHAT_MODES.has(mode)) throw new Error(`Invalid --mode: ${mode}`);
|
|
881
|
+
return mode;
|
|
882
|
+
}
|
|
883
|
+
function partitionInteractiveExecutionArgs(args, consumesNextValue) {
|
|
884
|
+
const values = /* @__PURE__ */ new Map();
|
|
885
|
+
const payloadArgs = [];
|
|
886
|
+
const executionFlags = ["--worker", "--mode", "--model"];
|
|
887
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
888
|
+
const arg = args[index];
|
|
889
|
+
if (consumesNextValue(arg)) {
|
|
890
|
+
payloadArgs.push(arg);
|
|
891
|
+
const value = args[index + 1];
|
|
892
|
+
if (value === void 0) throw new Error(`Missing value for ${arg}`);
|
|
893
|
+
payloadArgs.push(value);
|
|
894
|
+
index += 1;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
const inlineFlag = executionFlags.find((flag) => arg.startsWith(`${flag}=`));
|
|
898
|
+
if (inlineFlag) {
|
|
899
|
+
if (values.has(inlineFlag)) throw new Error(`${inlineFlag} may only be provided once`);
|
|
900
|
+
const value = requireValue(arg.slice(inlineFlag.length + 1).trim(), `Missing value for ${inlineFlag}`);
|
|
901
|
+
values.set(inlineFlag, value);
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
if (executionFlags.includes(arg)) {
|
|
905
|
+
const flag = arg;
|
|
906
|
+
if (values.has(flag)) throw new Error(`${flag} may only be provided once`);
|
|
907
|
+
const value = requireValue(args[index + 1]?.trim(), `Missing value for ${flag}`);
|
|
908
|
+
values.set(flag, value);
|
|
909
|
+
index += 1;
|
|
910
|
+
continue;
|
|
911
|
+
}
|
|
912
|
+
payloadArgs.push(arg);
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
context: {
|
|
916
|
+
worker: requireValue(values.get("--worker")?.trim(), "--worker <label> is required"),
|
|
917
|
+
mode: requireChatMode(values.get("--mode")),
|
|
918
|
+
model: requireModel(values.get("--model"))
|
|
919
|
+
},
|
|
920
|
+
payloadArgs
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function parseAnswerQuestionsCommandArgs(args) {
|
|
924
|
+
const { context, payloadArgs } = partitionInteractiveExecutionArgs(args, (arg) => /^-a\d+$/.test(arg));
|
|
925
|
+
return { ...context, answers: parseAnswerFlags(payloadArgs) };
|
|
926
|
+
}
|
|
809
927
|
function validateEnvAssignment(value) {
|
|
810
928
|
const equalsIndex = value.indexOf("=");
|
|
811
929
|
if (equalsIndex <= 0) {
|
|
@@ -965,6 +1083,10 @@ function parseEnvRequestResponseArgs(args) {
|
|
|
965
1083
|
...additionalContext?.trim() ? { additionalContext: additionalContext.trim() } : {}
|
|
966
1084
|
};
|
|
967
1085
|
}
|
|
1086
|
+
function parseAnswerEnvRequestCommandArgs(args) {
|
|
1087
|
+
const { context, payloadArgs } = partitionInteractiveExecutionArgs(args, (arg) => arg === "-e" || arg === "--env" || arg === "--context");
|
|
1088
|
+
return { ...context, ...parseEnvRequestResponseArgs(payloadArgs) };
|
|
1089
|
+
}
|
|
968
1090
|
function parsePromptArgs(args) {
|
|
969
1091
|
let modeRaw;
|
|
970
1092
|
let modelRaw;
|
|
@@ -1739,11 +1861,6 @@ function responseString(response, key) {
|
|
|
1739
1861
|
const value = response[key];
|
|
1740
1862
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1741
1863
|
}
|
|
1742
|
-
function responseSource(response) {
|
|
1743
|
-
const source = response.source;
|
|
1744
|
-
if (!source || source.type !== "worktree" || !source.branchName) return void 0;
|
|
1745
|
-
return `${source.type}:${source.branchName}`;
|
|
1746
|
-
}
|
|
1747
1864
|
function formatStatusValue(value) {
|
|
1748
1865
|
if (typeof value === "string" && value.length > 0) return value;
|
|
1749
1866
|
if (typeof value === "object" && value !== null) {
|
|
@@ -1753,21 +1870,16 @@ function formatStatusValue(value) {
|
|
|
1753
1870
|
}
|
|
1754
1871
|
return value === void 0 || value === null ? void 0 : String(value);
|
|
1755
1872
|
}
|
|
1756
|
-
function
|
|
1757
|
-
return typeof value === "object" && value !== null ? value : void 0;
|
|
1758
|
-
}
|
|
1759
|
-
function appendMergeDetails(lines, response) {
|
|
1760
|
-
const merge = asResponseRecord(response.merge);
|
|
1761
|
-
if (!merge) return;
|
|
1873
|
+
function appendMergeDetails(lines, merge) {
|
|
1762
1874
|
const mergeStatus = formatStatusValue(merge.status);
|
|
1763
|
-
lines.push(
|
|
1875
|
+
lines.push(`Merge: ${mergeStatus ?? "pending"}`);
|
|
1764
1876
|
for (const [label, key] of [
|
|
1765
1877
|
["Attempt", "attemptId"],
|
|
1766
|
-
["Source", "
|
|
1767
|
-
["Target", "
|
|
1878
|
+
["Source worktree", "sourceWorktree"],
|
|
1879
|
+
["Target worktree", "targetWorktree"],
|
|
1768
1880
|
["Commit", "commitHash"],
|
|
1769
1881
|
["Resolver agent", "resolverSessionId"],
|
|
1770
|
-
["
|
|
1882
|
+
["Conflict worktree", "conflictWorktree"],
|
|
1771
1883
|
["Resolver worktree", "folderPath"]
|
|
1772
1884
|
]) {
|
|
1773
1885
|
const value = responseString(merge, key);
|
|
@@ -1781,45 +1893,30 @@ function appendMergeDetails(lines, response) {
|
|
|
1781
1893
|
if (message) lines.push("", message);
|
|
1782
1894
|
const failureReason = responseString(merge, "failureReason");
|
|
1783
1895
|
if (failureReason) lines.push(`Failure: ${failureReason}`);
|
|
1784
|
-
const resolverSessionId = responseString(merge, "resolverSessionId");
|
|
1785
|
-
const worker = responseString(merge, "worker") ?? responseString(response, "worker");
|
|
1786
|
-
if (resolverSessionId && worker) {
|
|
1787
|
-
lines.push(
|
|
1788
|
-
"",
|
|
1789
|
-
"Resolver takeover:",
|
|
1790
|
-
` r5dctl agent stop ${resolverSessionId}`,
|
|
1791
|
-
` r5dctl agent finish-merge --worker ${worker} ${resolverSessionId} --summary "<summary>"`
|
|
1792
|
-
);
|
|
1793
|
-
}
|
|
1794
1896
|
}
|
|
1795
|
-
function
|
|
1897
|
+
function renderSessionRunStart(session) {
|
|
1796
1898
|
const lines = [
|
|
1797
|
-
`
|
|
1798
|
-
`Status: ${formatStatusValue(
|
|
1799
|
-
`
|
|
1800
|
-
`
|
|
1801
|
-
`Workspace head: ${responseString(agent, "workspaceHead") ?? "(pending)"}`,
|
|
1802
|
-
`Baseline: ${responseString(agent, "baselineCommit") ?? "(pending)"}`,
|
|
1803
|
-
`Worker: ${responseString(agent, "worker") ?? "(unassigned)"}`
|
|
1899
|
+
`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`,
|
|
1900
|
+
`Status: ${formatStatusValue(session.status) ?? "queued"}`,
|
|
1901
|
+
`Worktree: ${responseString(session, "branchName") ?? "(provisioning)"}`,
|
|
1902
|
+
`Worker: ${responseString(session, "worker") ?? "(unassigned)"}`
|
|
1804
1903
|
];
|
|
1805
|
-
const sessionUrl = responseString(
|
|
1806
|
-
if (sessionUrl) lines.push(`
|
|
1807
|
-
const sessionId = responseString(
|
|
1808
|
-
if (sessionId) lines.push("", `Next: r5dctl
|
|
1904
|
+
const sessionUrl = responseString(session, "sessionUrl");
|
|
1905
|
+
if (sessionUrl) lines.push(`URL: ${sessionUrl}`);
|
|
1906
|
+
const sessionId = responseString(session, "sessionId");
|
|
1907
|
+
if (sessionId) lines.push("", `Next: r5dctl session status ${sessionId}`);
|
|
1809
1908
|
return `${lines.join("\n")}
|
|
1810
1909
|
`;
|
|
1811
1910
|
}
|
|
1812
|
-
function
|
|
1813
|
-
const status = formatStatusValue(
|
|
1814
|
-
const lines = [`
|
|
1815
|
-
const runStatus = formatStatusValue(
|
|
1911
|
+
function renderSessionRunStatus(session) {
|
|
1912
|
+
const status = formatStatusValue(session.status) ?? "unknown";
|
|
1913
|
+
const lines = [`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`, `Status: ${status}`];
|
|
1914
|
+
const runStatus = formatStatusValue(session.runStatus);
|
|
1816
1915
|
if (runStatus) lines.push(`Run: ${runStatus}`);
|
|
1817
|
-
const promptDisposition = responseString(
|
|
1916
|
+
const promptDisposition = responseString(session, "promptDisposition");
|
|
1818
1917
|
if (promptDisposition) lines.push(`Prompt: ${promptDisposition}`);
|
|
1819
|
-
const branch = responseString(
|
|
1820
|
-
if (branch) lines.push(`
|
|
1821
|
-
const source = responseSource(agent);
|
|
1822
|
-
if (source) lines.push(`Source: ${source}`);
|
|
1918
|
+
const branch = responseString(session, "branchName");
|
|
1919
|
+
if (branch) lines.push(`Worktree: ${branch}`);
|
|
1823
1920
|
for (const [label, key] of [
|
|
1824
1921
|
["Workspace head", "workspaceHead"],
|
|
1825
1922
|
["Baseline", "baselineCommit"],
|
|
@@ -1827,72 +1924,90 @@ function renderBranchAgentStatus(agent) {
|
|
|
1827
1924
|
["Worker", "worker"],
|
|
1828
1925
|
["Session", "sessionUrl"]
|
|
1829
1926
|
]) {
|
|
1830
|
-
const value = responseString(
|
|
1927
|
+
const value = responseString(session, key);
|
|
1831
1928
|
if (value) lines.push(`${label}: ${value}`);
|
|
1832
1929
|
}
|
|
1833
|
-
const error = responseString(
|
|
1930
|
+
const error = responseString(session, "error");
|
|
1834
1931
|
if (error) lines.push(`Error: ${error}`);
|
|
1835
|
-
const headCommitError = responseString(
|
|
1932
|
+
const headCommitError = responseString(session, "headCommitError");
|
|
1836
1933
|
if (headCommitError) lines.push(`Head error: ${headCommitError}`);
|
|
1837
|
-
const diffSummary = responseString(
|
|
1934
|
+
const diffSummary = responseString(session, "diffSummary");
|
|
1838
1935
|
if (diffSummary) lines.push("", diffSummary);
|
|
1839
|
-
const summary = responseString(
|
|
1936
|
+
const summary = responseString(session, "summary");
|
|
1840
1937
|
if (summary) lines.push("", summary);
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
if (["provisioning", "queued", "running"].includes(status)) {
|
|
1845
|
-
lines.push("", "Next:", ` r5dctl agent status ${sessionId}`, ` r5dctl agent stop ${sessionId}`);
|
|
1846
|
-
} else if (["completed", "failed", "stopped"].includes(status)) {
|
|
1847
|
-
const worker = responseString(agent, "worker") ?? "<worker>";
|
|
1848
|
-
lines.push("", "Next:", ` r5dctl agent merge --worker ${worker} ${sessionId}`);
|
|
1849
|
-
}
|
|
1938
|
+
const sessionId = responseString(session, "sessionId");
|
|
1939
|
+
if (sessionId && ["provisioning", "queued", "running"].includes(status)) {
|
|
1940
|
+
lines.push("", "Next:", ` r5dctl session status ${sessionId}`, ` r5dctl session stop ${sessionId}`);
|
|
1850
1941
|
}
|
|
1851
1942
|
return `${lines.join("\n")}
|
|
1852
1943
|
`;
|
|
1853
1944
|
}
|
|
1854
|
-
function
|
|
1855
|
-
if (asResponseRecord(result.merge)) return renderBranchAgentStatus(result);
|
|
1856
|
-
const wrapped = {
|
|
1857
|
-
sessionId: result.sessionId,
|
|
1858
|
-
status: result.status,
|
|
1859
|
-
worker: result.worker,
|
|
1860
|
-
merge: result
|
|
1861
|
-
};
|
|
1945
|
+
function renderWorktreeMerge(result, input) {
|
|
1862
1946
|
const lines = [];
|
|
1863
|
-
appendMergeDetails(lines,
|
|
1864
|
-
|
|
1947
|
+
appendMergeDetails(lines, result);
|
|
1948
|
+
const conflictWorktree = responseString(result, "conflictWorktree");
|
|
1949
|
+
const resolverSessionId = responseString(result, "resolverSessionId");
|
|
1950
|
+
const worker = responseString(result, "worker") ?? input.worker;
|
|
1951
|
+
if (resolverSessionId) lines.push("", `Resolver session: ${resolverSessionId}`, ` r5dctl session stop ${resolverSessionId}`);
|
|
1952
|
+
if (conflictWorktree && worker && ["conflicts", "conflict_worktree_created", "resolving"].includes(responseString(result, "status") ?? "")) {
|
|
1953
|
+
lines.push(
|
|
1954
|
+
"",
|
|
1955
|
+
"Finish after resolving and reviewing the conflict worktree:",
|
|
1956
|
+
` r5dctl -p ${input.project} merge finish --worker ${worker} ${conflictWorktree} --summary "<summary>"`
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
return `${lines.join("\n")}
|
|
1865
1960
|
`;
|
|
1866
1961
|
}
|
|
1867
|
-
async function
|
|
1962
|
+
async function dispatchSessionRunCommand(client, command) {
|
|
1868
1963
|
if (command.kind === "start") {
|
|
1869
|
-
const
|
|
1870
|
-
source: command.source,
|
|
1871
|
-
agentType: command.agentType,
|
|
1964
|
+
const commonInput = {
|
|
1872
1965
|
prompt: command.prompt,
|
|
1873
1966
|
worker: command.worker,
|
|
1967
|
+
model: command.model,
|
|
1874
1968
|
requestId: (0, import_node_crypto.randomUUID)()
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1969
|
+
};
|
|
1970
|
+
let input;
|
|
1971
|
+
if (command.worktree !== void 0) {
|
|
1972
|
+
input = { ...commonInput, worktree: command.worktree };
|
|
1973
|
+
} else if (command.newWorktree !== void 0 && command.source !== void 0) {
|
|
1974
|
+
input = { ...commonInput, newWorktree: command.newWorktree, source: command.source };
|
|
1975
|
+
} else {
|
|
1976
|
+
throw new Error("Session start requires an existing or new worktree");
|
|
1977
|
+
}
|
|
1978
|
+
const data2 = await client.projects.sessions.start(command.project, input);
|
|
1979
|
+
return { data: data2, human: renderSessionRunStart(data2) };
|
|
1877
1980
|
}
|
|
1878
1981
|
if (command.kind === "status") {
|
|
1879
|
-
const data2 = await client.
|
|
1880
|
-
return { data: data2, human:
|
|
1982
|
+
const data2 = await client.sessions.status(command.sessionId);
|
|
1983
|
+
return { data: data2, human: renderSessionRunStatus(data2) };
|
|
1881
1984
|
}
|
|
1882
1985
|
if (command.kind === "prompt") {
|
|
1883
|
-
const data2 = await client.
|
|
1884
|
-
|
|
1986
|
+
const data2 = await client.sessions.prompt(command.sessionId, {
|
|
1987
|
+
prompt: command.prompt,
|
|
1988
|
+
worker: command.worker,
|
|
1989
|
+
mode: command.mode,
|
|
1990
|
+
model: command.model,
|
|
1991
|
+
requestId: (0, import_node_crypto.randomUUID)()
|
|
1992
|
+
});
|
|
1993
|
+
return { data: data2, human: renderSessionRunStatus(data2) };
|
|
1885
1994
|
}
|
|
1995
|
+
const data = await client.sessions.stop(command.sessionId);
|
|
1996
|
+
return { data, human: renderSessionRunStatus(data) };
|
|
1997
|
+
}
|
|
1998
|
+
async function dispatchMergeCommand(client, command) {
|
|
1886
1999
|
if (command.kind === "merge") {
|
|
1887
|
-
const data2 = await client.
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
return { data: data2, human: renderBranchAgentStatus(data2) };
|
|
2000
|
+
const data2 = await client.projects.worktrees.merge(command.project, command.sourceWorktree, {
|
|
2001
|
+
conflictMode: command.conflictMode,
|
|
2002
|
+
...command.worker ? { worker: command.worker } : {}
|
|
2003
|
+
});
|
|
2004
|
+
return { data: data2, human: renderWorktreeMerge(data2, command) };
|
|
1893
2005
|
}
|
|
1894
|
-
const data = await client.
|
|
1895
|
-
|
|
2006
|
+
const data = await client.projects.worktrees.finishMerge(command.project, command.conflictWorktree, {
|
|
2007
|
+
worker: command.worker,
|
|
2008
|
+
...command.summary ? { summary: command.summary } : {}
|
|
2009
|
+
});
|
|
2010
|
+
return { data, human: renderWorktreeMerge(data, command) };
|
|
1896
2011
|
}
|
|
1897
2012
|
function parseProjectUpdateArgs(args) {
|
|
1898
2013
|
const input = {};
|
|
@@ -1967,7 +2082,7 @@ function parseSessionUpdateArgs(args) {
|
|
|
1967
2082
|
return input;
|
|
1968
2083
|
}
|
|
1969
2084
|
function parseConversationRenderArgs(args) {
|
|
1970
|
-
const options = { format: "human", includeTools: false, includeSystem: false };
|
|
2085
|
+
const options = { format: "human", includeTools: false, includeSystem: false, follow: false };
|
|
1971
2086
|
for (const arg of args) {
|
|
1972
2087
|
if (arg === "--raw") {
|
|
1973
2088
|
options.format = "raw";
|
|
@@ -1981,6 +2096,11 @@ function parseConversationRenderArgs(args) {
|
|
|
1981
2096
|
options.includeSystem = true;
|
|
1982
2097
|
continue;
|
|
1983
2098
|
}
|
|
2099
|
+
if (arg === "-f" || arg === "--follow" || arg === "--watch") {
|
|
2100
|
+
if (options.follow) throw new Error("--follow may only be provided once");
|
|
2101
|
+
options.follow = true;
|
|
2102
|
+
continue;
|
|
2103
|
+
}
|
|
1984
2104
|
throw new Error(`Unknown conversation argument: ${arg}`);
|
|
1985
2105
|
}
|
|
1986
2106
|
return options;
|
|
@@ -1989,6 +2109,306 @@ function renderConversationResponse(read) {
|
|
|
1989
2109
|
return read.agentText.endsWith("\n") ? read.agentText : `${read.agentText}
|
|
1990
2110
|
`;
|
|
1991
2111
|
}
|
|
2112
|
+
function recordValue(value) {
|
|
2113
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
2114
|
+
}
|
|
2115
|
+
function nodeId(node) {
|
|
2116
|
+
const id = recordValue(node)?.id;
|
|
2117
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
2118
|
+
}
|
|
2119
|
+
function nodeType(node) {
|
|
2120
|
+
const type = recordValue(node)?.type;
|
|
2121
|
+
return typeof type === "string" ? type : void 0;
|
|
2122
|
+
}
|
|
2123
|
+
function assistantNodeText(node) {
|
|
2124
|
+
const record = recordValue(node);
|
|
2125
|
+
return record?.type === "assistant_message" && typeof record.content === "string" ? record.content : void 0;
|
|
2126
|
+
}
|
|
2127
|
+
function conversationRevision(read) {
|
|
2128
|
+
const revision = recordValue(read.conversation)?.revision;
|
|
2129
|
+
return typeof revision === "number" ? revision : 0;
|
|
2130
|
+
}
|
|
2131
|
+
function activeConversationNodes(conversation) {
|
|
2132
|
+
const record = recordValue(conversation);
|
|
2133
|
+
const responses = recordValue(record?.responses);
|
|
2134
|
+
let currentId = typeof record?.head === "string" ? record.head : "";
|
|
2135
|
+
if (!responses || !currentId) return [];
|
|
2136
|
+
const reversed = [];
|
|
2137
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2138
|
+
while (currentId && !visited.has(currentId)) {
|
|
2139
|
+
visited.add(currentId);
|
|
2140
|
+
const node = responses[currentId];
|
|
2141
|
+
if (!node) break;
|
|
2142
|
+
reversed.push(node);
|
|
2143
|
+
const parent = recordValue(node)?.parent;
|
|
2144
|
+
currentId = typeof parent === "string" ? parent : "";
|
|
2145
|
+
}
|
|
2146
|
+
return reversed.reverse();
|
|
2147
|
+
}
|
|
2148
|
+
function isToolEvent(event) {
|
|
2149
|
+
return ["tool_use_start", "input_json_delta", "tool_call", "tool_progress", "shell_result"].includes(event.type);
|
|
2150
|
+
}
|
|
2151
|
+
function withoutToolDetails(node) {
|
|
2152
|
+
const record = recordValue(node);
|
|
2153
|
+
if (!record || record.type !== "assistant_message" || !Array.isArray(record.toolCalls)) return node;
|
|
2154
|
+
return { ...record, toolCalls: [] };
|
|
2155
|
+
}
|
|
2156
|
+
function jsonConversationData(read, includeTools) {
|
|
2157
|
+
if (includeTools) return read;
|
|
2158
|
+
const { conversation: _conversation, thread, ...rest } = read;
|
|
2159
|
+
return {
|
|
2160
|
+
...rest,
|
|
2161
|
+
thread: Array.isArray(thread) ? thread.filter((node) => nodeType(node) !== "tool_result").map((node) => withoutToolDetails(node)) : []
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function jsonFollowEvent(event, includeTools) {
|
|
2165
|
+
if (event.type === "session") {
|
|
2166
|
+
const data = recordValue(event.data);
|
|
2167
|
+
if (!data) return event;
|
|
2168
|
+
const { conversation: _conversation, ...rest } = data;
|
|
2169
|
+
return { ...event, data: rest };
|
|
2170
|
+
}
|
|
2171
|
+
if (includeTools) return event;
|
|
2172
|
+
if (isToolEvent(event)) return null;
|
|
2173
|
+
if (event.type === "response") {
|
|
2174
|
+
const data = recordValue(event.data);
|
|
2175
|
+
const node = data?.node;
|
|
2176
|
+
if (nodeType(node) === "tool_result") return null;
|
|
2177
|
+
return { ...event, data: { ...data, node: withoutToolDetails(node) } };
|
|
2178
|
+
}
|
|
2179
|
+
return event;
|
|
2180
|
+
}
|
|
2181
|
+
function terminalRunStatus(response) {
|
|
2182
|
+
const status = formatStatusValue(response.runStatus) ?? formatStatusValue(response.status);
|
|
2183
|
+
return {
|
|
2184
|
+
terminal: status !== void 0 && ["idle", "completed", "failed", "stopped"].includes(status),
|
|
2185
|
+
failed: status === "failed"
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
function isAbortError(error) {
|
|
2189
|
+
return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && (error.name === "AbortError" || error.message.toLowerCase().includes("aborted"));
|
|
2190
|
+
}
|
|
2191
|
+
async function followR5dctlSession(client, sessionId, options) {
|
|
2192
|
+
const writeOut = options.stdout ?? ((text) => process.stdout.write(text));
|
|
2193
|
+
const writeErr = options.stderr ?? ((text) => process.stderr.write(text));
|
|
2194
|
+
const seenNodeIds = /* @__PURE__ */ new Set();
|
|
2195
|
+
let latestRevision = 0;
|
|
2196
|
+
let streamedAssistantText = "";
|
|
2197
|
+
let reconnects = 0;
|
|
2198
|
+
const emitJson = (value) => writeOut(`${JSON.stringify(value)}
|
|
2199
|
+
`);
|
|
2200
|
+
const renderNode = (node) => {
|
|
2201
|
+
const id = nodeId(node);
|
|
2202
|
+
if (id && seenNodeIds.has(id)) return;
|
|
2203
|
+
if (id) seenNodeIds.add(id);
|
|
2204
|
+
const text = assistantNodeText(node);
|
|
2205
|
+
if (text !== void 0) {
|
|
2206
|
+
let missing = text;
|
|
2207
|
+
if (streamedAssistantText && text.startsWith(streamedAssistantText)) missing = text.slice(streamedAssistantText.length);
|
|
2208
|
+
else if (streamedAssistantText.startsWith(text)) missing = "";
|
|
2209
|
+
if (missing) writeOut(missing);
|
|
2210
|
+
streamedAssistantText = "";
|
|
2211
|
+
if (options.includeTools) {
|
|
2212
|
+
const toolCalls = recordValue(node)?.toolCalls;
|
|
2213
|
+
if (Array.isArray(toolCalls) && toolCalls.length > 0) writeErr(`[tools] ${JSON.stringify(toolCalls)}
|
|
2214
|
+
`);
|
|
2215
|
+
}
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
if (options.includeTools && nodeType(node) === "tool_result") writeErr(`[tool results] ${JSON.stringify(node)}
|
|
2219
|
+
`);
|
|
2220
|
+
};
|
|
2221
|
+
const syncConversation = async (initial = false) => {
|
|
2222
|
+
const read = initial && options.initialConversation ? options.initialConversation : await client.sessions.conversation(sessionId, options.renderOptions);
|
|
2223
|
+
const nodes = Array.isArray(read.thread) ? read.thread : [];
|
|
2224
|
+
const newNodes = nodes.filter((node) => {
|
|
2225
|
+
const id = nodeId(node);
|
|
2226
|
+
return !id || !seenNodeIds.has(id);
|
|
2227
|
+
});
|
|
2228
|
+
if (initial && options.printInitialTranscript) {
|
|
2229
|
+
if (options.json)
|
|
2230
|
+
emitJson({ type: "conversation", revision: conversationRevision(read), data: jsonConversationData(read, options.includeTools) });
|
|
2231
|
+
else writeOut(renderConversationResponse(read));
|
|
2232
|
+
for (const node of nodes) {
|
|
2233
|
+
const id = nodeId(node);
|
|
2234
|
+
if (id) seenNodeIds.add(id);
|
|
2235
|
+
}
|
|
2236
|
+
} else if (options.json && newNodes.length > 0) {
|
|
2237
|
+
const filtered = options.includeTools ? newNodes : newNodes.filter((node) => nodeType(node) !== "tool_result");
|
|
2238
|
+
if (filtered.length > 0) {
|
|
2239
|
+
emitJson({
|
|
2240
|
+
type: "conversation_sync",
|
|
2241
|
+
revision: conversationRevision(read),
|
|
2242
|
+
nodes: options.includeTools ? filtered : filtered.map((node) => withoutToolDetails(node))
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
for (const node of newNodes) {
|
|
2246
|
+
const id = nodeId(node);
|
|
2247
|
+
if (id) seenNodeIds.add(id);
|
|
2248
|
+
}
|
|
2249
|
+
} else {
|
|
2250
|
+
for (const node of newNodes) renderNode(node);
|
|
2251
|
+
}
|
|
2252
|
+
latestRevision = Math.max(latestRevision, conversationRevision(read));
|
|
2253
|
+
return read;
|
|
2254
|
+
};
|
|
2255
|
+
await syncConversation(true);
|
|
2256
|
+
while (!options.signal?.aborted) {
|
|
2257
|
+
try {
|
|
2258
|
+
for await (const event of client.sessions.events(sessionId, { signal: options.signal })) {
|
|
2259
|
+
reconnects = 0;
|
|
2260
|
+
if (event.type === "response") {
|
|
2261
|
+
const data = recordValue(event.data);
|
|
2262
|
+
const revision = data?.conversationRevision;
|
|
2263
|
+
const id = nodeId(data?.node);
|
|
2264
|
+
if (typeof revision === "number" && revision <= latestRevision || id !== void 0 && seenNodeIds.has(id)) continue;
|
|
2265
|
+
}
|
|
2266
|
+
if (options.json) {
|
|
2267
|
+
const outputEvent = jsonFollowEvent(event, options.includeTools);
|
|
2268
|
+
if (outputEvent) emitJson(outputEvent);
|
|
2269
|
+
} else if (event.type === "text_delta" && typeof event.text === "string") {
|
|
2270
|
+
writeOut(event.text);
|
|
2271
|
+
streamedAssistantText += event.text;
|
|
2272
|
+
} else if (event.type === "thinking_delta" && typeof event.text === "string") {
|
|
2273
|
+
writeErr(event.text);
|
|
2274
|
+
} else if (event.type === "tool_use_start" && options.includeTools) {
|
|
2275
|
+
writeErr(`[tool] ${String(event.toolName)}
|
|
2276
|
+
`);
|
|
2277
|
+
} else if (event.type === "input_json_delta" && options.includeTools) {
|
|
2278
|
+
writeErr(String(event.partial_json));
|
|
2279
|
+
} else if ((event.type === "tool_call" || event.type === "tool_progress" || event.type === "shell_result") && options.includeTools) {
|
|
2280
|
+
writeErr(`[${event.type}] ${JSON.stringify(event)}
|
|
2281
|
+
`);
|
|
2282
|
+
} else if (event.type === "status") {
|
|
2283
|
+
writeErr(`[status] ${formatStatusValue(event.status) ?? "updated"}
|
|
2284
|
+
`);
|
|
2285
|
+
} else if (event.type === "agent_running") {
|
|
2286
|
+
writeErr(`[running] ${event.running ? "yes" : "no"}
|
|
2287
|
+
`);
|
|
2288
|
+
} else if (event.type === "stream_reset") {
|
|
2289
|
+
streamedAssistantText = "";
|
|
2290
|
+
writeErr(`[retry] ${event.attempt}/${event.maxAttempts}
|
|
2291
|
+
`);
|
|
2292
|
+
} else if (event.type === "response") {
|
|
2293
|
+
const revision = recordValue(event.data)?.conversationRevision;
|
|
2294
|
+
if (typeof revision === "number") latestRevision = revision;
|
|
2295
|
+
renderNode(recordValue(event.data)?.node);
|
|
2296
|
+
} else if (event.type === "session") {
|
|
2297
|
+
const session = recordValue(event.data);
|
|
2298
|
+
const conversation = session?.conversation;
|
|
2299
|
+
for (const node of activeConversationNodes(conversation)) renderNode(node);
|
|
2300
|
+
const revision = recordValue(conversation)?.revision;
|
|
2301
|
+
if (typeof revision === "number") latestRevision = Math.max(latestRevision, revision);
|
|
2302
|
+
const streamingText = recordValue(session?.streaming)?.text;
|
|
2303
|
+
if (!options.json && typeof streamingText === "string" && streamingText.length > 0) {
|
|
2304
|
+
let missing = streamingText;
|
|
2305
|
+
if (streamedAssistantText && streamingText.startsWith(streamedAssistantText))
|
|
2306
|
+
missing = streamingText.slice(streamedAssistantText.length);
|
|
2307
|
+
else if (streamedAssistantText.startsWith(streamingText)) missing = "";
|
|
2308
|
+
if (missing) writeOut(missing);
|
|
2309
|
+
streamedAssistantText = streamingText;
|
|
2310
|
+
}
|
|
2311
|
+
if (session?.agentRunning === false) {
|
|
2312
|
+
try {
|
|
2313
|
+
const terminal = terminalRunStatus(await client.sessions.status(sessionId));
|
|
2314
|
+
if (terminal.terminal) return terminal.failed ? 1 : 0;
|
|
2315
|
+
} catch (error) {
|
|
2316
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2317
|
+
const status = formatStatusValue(session.status);
|
|
2318
|
+
if (error instanceof import_r5d_api.R5dctlApiError && error.status === 400 && status === "idle") return 0;
|
|
2319
|
+
if (error instanceof import_r5d_api.R5dctlApiError && error.status === 400 && status === "error") return 1;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
if (event.type === "response" && options.json) {
|
|
2324
|
+
const revision = recordValue(event.data)?.conversationRevision;
|
|
2325
|
+
if (typeof revision === "number") latestRevision = Math.max(latestRevision, revision);
|
|
2326
|
+
const id = nodeId(recordValue(event.data)?.node);
|
|
2327
|
+
if (id) seenNodeIds.add(id);
|
|
2328
|
+
}
|
|
2329
|
+
if (event.type === "session" && options.json) {
|
|
2330
|
+
const session = recordValue(event.data);
|
|
2331
|
+
const conversation = session?.conversation;
|
|
2332
|
+
const newNodes = activeConversationNodes(conversation).filter((node) => {
|
|
2333
|
+
const id = nodeId(node);
|
|
2334
|
+
return !id || !seenNodeIds.has(id);
|
|
2335
|
+
});
|
|
2336
|
+
const filteredNodes = options.includeTools ? newNodes : newNodes.filter((node) => nodeType(node) !== "tool_result");
|
|
2337
|
+
if (filteredNodes.length > 0) {
|
|
2338
|
+
emitJson({
|
|
2339
|
+
type: "conversation_sync",
|
|
2340
|
+
revision: recordValue(conversation)?.revision,
|
|
2341
|
+
nodes: options.includeTools ? filteredNodes : filteredNodes.map((node) => withoutToolDetails(node))
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2344
|
+
for (const node of newNodes) {
|
|
2345
|
+
const id = nodeId(node);
|
|
2346
|
+
if (id) seenNodeIds.add(id);
|
|
2347
|
+
}
|
|
2348
|
+
const revision = recordValue(conversation)?.revision;
|
|
2349
|
+
if (typeof revision === "number") latestRevision = Math.max(latestRevision, revision);
|
|
2350
|
+
if (session?.agentRunning === false) {
|
|
2351
|
+
try {
|
|
2352
|
+
const terminal = terminalRunStatus(await client.sessions.status(sessionId));
|
|
2353
|
+
if (terminal.terminal) return terminal.failed ? 1 : 0;
|
|
2354
|
+
} catch (error) {
|
|
2355
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2356
|
+
const status = formatStatusValue(session.status);
|
|
2357
|
+
if (error instanceof import_r5d_api.R5dctlApiError && error.status === 400 && status === "idle") return 0;
|
|
2358
|
+
if (error instanceof import_r5d_api.R5dctlApiError && error.status === 400 && status === "error") return 1;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (event.type === "done" || event.type === "stopped") {
|
|
2363
|
+
await syncConversation();
|
|
2364
|
+
if (!options.json) writeErr(`[${event.type}]
|
|
2365
|
+
`);
|
|
2366
|
+
try {
|
|
2367
|
+
const terminal = terminalRunStatus(await client.sessions.status(sessionId));
|
|
2368
|
+
if (terminal.terminal) return terminal.failed ? 1 : 0;
|
|
2369
|
+
if (event.type === "done") continue;
|
|
2370
|
+
return 0;
|
|
2371
|
+
} catch (error) {
|
|
2372
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2373
|
+
if (error instanceof import_r5d_api.R5dctlApiError && error.status === 400) return 0;
|
|
2374
|
+
throw error;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
if (event.type === "error") {
|
|
2378
|
+
if (!options.json) writeErr(`${typeof event.message === "string" ? event.message : "Session failed"}
|
|
2379
|
+
`);
|
|
2380
|
+
return 1;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
} catch (error) {
|
|
2384
|
+
if (options.signal?.aborted || isAbortError(error)) return 0;
|
|
2385
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2386
|
+
}
|
|
2387
|
+
if (options.signal?.aborted) return 0;
|
|
2388
|
+
try {
|
|
2389
|
+
await syncConversation();
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2392
|
+
}
|
|
2393
|
+
try {
|
|
2394
|
+
const terminal = terminalRunStatus(await client.sessions.status(sessionId));
|
|
2395
|
+
if (terminal.terminal) return terminal.failed ? 1 : 0;
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
if (error instanceof import_r5d_api.R5dctlApiError && [401, 403, 404].includes(error.status)) throw error;
|
|
2398
|
+
}
|
|
2399
|
+
reconnects += 1;
|
|
2400
|
+
if (reconnects > (options.maxReconnects ?? Number.POSITIVE_INFINITY)) {
|
|
2401
|
+
throw new Error("Session event stream ended before the session became idle");
|
|
2402
|
+
}
|
|
2403
|
+
try {
|
|
2404
|
+
await (0, import_promises.setTimeout)(options.reconnectDelayMs ?? Math.min(250 * 2 ** (reconnects - 1), 5e3), void 0, { signal: options.signal });
|
|
2405
|
+
} catch (error) {
|
|
2406
|
+
if (options.signal?.aborted || isAbortError(error)) return 0;
|
|
2407
|
+
throw error;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return 0;
|
|
2411
|
+
}
|
|
1992
2412
|
function parseConversationWorkDetailArgs(args) {
|
|
1993
2413
|
let detail = "compact";
|
|
1994
2414
|
let selected = false;
|
|
@@ -2220,8 +2640,8 @@ Sessions: ${result.sessions.length}
|
|
|
2220
2640
|
throw new Error(`Unexpected conversation inspect-node argument: ${args[4]}`);
|
|
2221
2641
|
}
|
|
2222
2642
|
const sessionId = requireValue(args[2], "Missing session id");
|
|
2223
|
-
const
|
|
2224
|
-
const read = await client.sessions.inspectConversationNode(sessionId,
|
|
2643
|
+
const nodeId2 = requireValue(args[3], "Missing node id");
|
|
2644
|
+
const read = await client.sessions.inspectConversationNode(sessionId, nodeId2);
|
|
2225
2645
|
write(read, renderConversationNodeResponse(read));
|
|
2226
2646
|
return;
|
|
2227
2647
|
}
|
|
@@ -2236,33 +2656,30 @@ Sessions: ${result.sessions.length}
|
|
|
2236
2656
|
if (first === "sessions" && second === "conversation" || first === "conversation") {
|
|
2237
2657
|
const offset = first === "conversation" ? 1 : 2;
|
|
2238
2658
|
const renderOptions = parseConversationRenderArgs(args.slice(offset + 1));
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
}
|
|
2243
|
-
if (first === "sessions" && second === "prompt" || first === "prompt") {
|
|
2244
|
-
const offset = first === "prompt" ? 1 : 2;
|
|
2245
|
-
const read = await client.sessions.prompt(requireValue(args[offset], "Missing session id"), {
|
|
2246
|
-
mode: requireValue(args[offset + 1], "Missing mode"),
|
|
2247
|
-
model: requireValue(args[offset + 2], "Missing model"),
|
|
2248
|
-
message: requireValue(args[offset + 3], "Missing prompt message")
|
|
2249
|
-
});
|
|
2659
|
+
if (renderOptions.follow) throw new Error("Conversation follow must be handled by the CLI stream runner");
|
|
2660
|
+
const { follow: _follow, ...apiRenderOptions } = renderOptions;
|
|
2661
|
+
const read = await client.sessions.conversation(requireValue(args[offset], "Missing session id"), apiRenderOptions);
|
|
2250
2662
|
write(read, renderConversationResponse(read));
|
|
2251
2663
|
return;
|
|
2252
2664
|
}
|
|
2253
2665
|
if (first === "sessions" && second === "answer-questions" || first === "answer-questions") {
|
|
2254
2666
|
const offset = first === "answer-questions" ? 1 : 2;
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2667
|
+
const input = parseAnswerQuestionsCommandArgs(args.slice(offset + 1));
|
|
2668
|
+
const read = await client.sessions.answerQuestions(requireValue(args[offset], "Missing session id"), {
|
|
2669
|
+
...input,
|
|
2670
|
+
requestId: (0, import_node_crypto.randomUUID)()
|
|
2671
|
+
});
|
|
2672
|
+
write(read, renderSessionRunStatus(read));
|
|
2259
2673
|
return;
|
|
2260
2674
|
}
|
|
2261
2675
|
if (first === "sessions" && second === "answer-env-request" || first === "answer-env-request") {
|
|
2262
2676
|
const offset = first === "answer-env-request" ? 1 : 2;
|
|
2263
|
-
const input =
|
|
2264
|
-
const read = await client.sessions.answerEnvRequest(requireValue(args[offset], "Missing session id"),
|
|
2265
|
-
|
|
2677
|
+
const input = parseAnswerEnvRequestCommandArgs(args.slice(offset + 1));
|
|
2678
|
+
const read = await client.sessions.answerEnvRequest(requireValue(args[offset], "Missing session id"), {
|
|
2679
|
+
...input,
|
|
2680
|
+
requestId: (0, import_node_crypto.randomUUID)()
|
|
2681
|
+
});
|
|
2682
|
+
write(read, renderSessionRunStatus(read));
|
|
2266
2683
|
return;
|
|
2267
2684
|
}
|
|
2268
2685
|
throw new Error(`Unknown command: ${args.join(" ")}`);
|
|
@@ -2287,10 +2704,16 @@ function resolveCommandExecution(options, rest) {
|
|
|
2287
2704
|
text: K8S_HELP_TEXT
|
|
2288
2705
|
};
|
|
2289
2706
|
}
|
|
2290
|
-
if (command === "
|
|
2707
|
+
if (command === "session" && commandArgs.length === 0) {
|
|
2708
|
+
return {
|
|
2709
|
+
kind: "cli-help",
|
|
2710
|
+
text: SESSION_RUN_HELP_TEXT
|
|
2711
|
+
};
|
|
2712
|
+
}
|
|
2713
|
+
if (command === "merge" && commandArgs.length === 0) {
|
|
2291
2714
|
return {
|
|
2292
2715
|
kind: "cli-help",
|
|
2293
|
-
text:
|
|
2716
|
+
text: MERGE_HELP_TEXT
|
|
2294
2717
|
};
|
|
2295
2718
|
}
|
|
2296
2719
|
if (trailingHelp) {
|
|
@@ -2652,24 +3075,23 @@ function resolveCommandExecution(options, rest) {
|
|
|
2652
3075
|
if (!sessionId2) {
|
|
2653
3076
|
throw new Error("Session id is required for `conversation inspect-node`");
|
|
2654
3077
|
}
|
|
2655
|
-
const
|
|
2656
|
-
if (!
|
|
3078
|
+
const nodeId2 = options.session ? commandArgs[1] : commandArgs[2];
|
|
3079
|
+
if (!nodeId2) {
|
|
2657
3080
|
throw new Error("Node id is required for `conversation inspect-node`");
|
|
2658
3081
|
}
|
|
2659
3082
|
const remainingArgs = options.session ? commandArgs.slice(2) : commandArgs.slice(3);
|
|
2660
3083
|
return {
|
|
2661
3084
|
kind: "plugin",
|
|
2662
|
-
pluginArgs: ["conversation", "inspect-node", sessionId2,
|
|
3085
|
+
pluginArgs: ["conversation", "inspect-node", sessionId2, nodeId2, ...remainingArgs]
|
|
2663
3086
|
};
|
|
2664
3087
|
}
|
|
2665
3088
|
const sessionId = options.session;
|
|
2666
3089
|
if (!sessionId) {
|
|
2667
3090
|
throw new Error("--session/-s is required for `conversation`");
|
|
2668
3091
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
};
|
|
3092
|
+
const renderOptions = parseConversationRenderArgs(commandArgs);
|
|
3093
|
+
if (renderOptions.follow) return { kind: "conversation-follow", sessionId, renderOptions };
|
|
3094
|
+
return { kind: "plugin", pluginArgs: ["conversation", sessionId, ...commandArgs] };
|
|
2673
3095
|
}
|
|
2674
3096
|
if (command === "shell") {
|
|
2675
3097
|
if (!options.project) {
|
|
@@ -2686,19 +3108,11 @@ function resolveCommandExecution(options, rest) {
|
|
|
2686
3108
|
...parseShellArgs(commandArgs)
|
|
2687
3109
|
};
|
|
2688
3110
|
}
|
|
2689
|
-
if (command === "
|
|
2690
|
-
|
|
2691
|
-
if (!sessionId) {
|
|
2692
|
-
throw new Error("--session/-s is required for `prompt`");
|
|
2693
|
-
}
|
|
2694
|
-
const parsed = parsePromptArgs(commandArgs);
|
|
2695
|
-
return {
|
|
2696
|
-
kind: "plugin",
|
|
2697
|
-
pluginArgs: ["prompt", sessionId, parsed.mode, parsed.model, parsed.message]
|
|
2698
|
-
};
|
|
3111
|
+
if (command === "session") {
|
|
3112
|
+
return { kind: "session-run", command: parseSessionRunCommand(options, commandArgs) };
|
|
2699
3113
|
}
|
|
2700
|
-
if (command === "
|
|
2701
|
-
return { kind: "
|
|
3114
|
+
if (command === "merge") {
|
|
3115
|
+
return { kind: "merge", command: parseMergeCommand(options, commandArgs) };
|
|
2702
3116
|
}
|
|
2703
3117
|
if (command === "answer-questions") {
|
|
2704
3118
|
const sessionId = options.session;
|
|
@@ -2707,7 +3121,7 @@ function resolveCommandExecution(options, rest) {
|
|
|
2707
3121
|
}
|
|
2708
3122
|
return {
|
|
2709
3123
|
kind: "plugin",
|
|
2710
|
-
pluginArgs: ["answer-questions", sessionId, ...
|
|
3124
|
+
pluginArgs: ["answer-questions", sessionId, ...commandArgs]
|
|
2711
3125
|
};
|
|
2712
3126
|
}
|
|
2713
3127
|
if (command === "answer-env-request") {
|
|
@@ -2722,6 +3136,16 @@ function resolveCommandExecution(options, rest) {
|
|
|
2722
3136
|
}
|
|
2723
3137
|
throw new Error(`Unknown command: ${command}`);
|
|
2724
3138
|
}
|
|
3139
|
+
async function followWithInterrupt(client, sessionId, options) {
|
|
3140
|
+
const controller = new AbortController();
|
|
3141
|
+
const detach = () => controller.abort();
|
|
3142
|
+
process.once("SIGINT", detach);
|
|
3143
|
+
try {
|
|
3144
|
+
return await followR5dctlSession(client, sessionId, { ...options, signal: controller.signal });
|
|
3145
|
+
} finally {
|
|
3146
|
+
process.removeListener("SIGINT", detach);
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
2725
3149
|
async function runCommand(argv) {
|
|
2726
3150
|
const { options, rest } = parseGlobalArgs(argv);
|
|
2727
3151
|
if (options.version) {
|
|
@@ -2748,11 +3172,38 @@ async function runCommand(argv) {
|
|
|
2748
3172
|
process.stdout.write(execution.text);
|
|
2749
3173
|
return 0;
|
|
2750
3174
|
}
|
|
2751
|
-
if (execution.kind === "
|
|
2752
|
-
const result = await
|
|
3175
|
+
if (execution.kind === "session-run") {
|
|
3176
|
+
const result = await dispatchSessionRunCommand(client, execution.command);
|
|
3177
|
+
if (execution.command.kind !== "start" || !execution.command.follow) {
|
|
3178
|
+
writeDataOutput(options.json, result.data, result.human);
|
|
3179
|
+
return 0;
|
|
3180
|
+
}
|
|
3181
|
+
const sessionId = requireValue(responseString(result.data, "sessionId"), "Session start did not return a session id");
|
|
3182
|
+
if (options.json) process.stdout.write(`${JSON.stringify({ type: "session_started", data: result.data })}
|
|
3183
|
+
`);
|
|
3184
|
+
else process.stderr.write(result.human);
|
|
3185
|
+
return await followWithInterrupt(client, sessionId, {
|
|
3186
|
+
json: options.json,
|
|
3187
|
+
includeTools: execution.command.includeTools,
|
|
3188
|
+
printInitialTranscript: false
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
if (execution.kind === "merge") {
|
|
3192
|
+
const result = await dispatchMergeCommand(client, execution.command);
|
|
2753
3193
|
writeDataOutput(options.json, result.data, result.human);
|
|
2754
3194
|
return 0;
|
|
2755
3195
|
}
|
|
3196
|
+
if (execution.kind === "conversation-follow") {
|
|
3197
|
+
const { follow: _follow, ...renderOptions } = execution.renderOptions;
|
|
3198
|
+
const initialConversation = await client.sessions.conversation(execution.sessionId, renderOptions);
|
|
3199
|
+
return await followWithInterrupt(client, execution.sessionId, {
|
|
3200
|
+
json: options.json,
|
|
3201
|
+
includeTools: execution.renderOptions.includeTools,
|
|
3202
|
+
renderOptions,
|
|
3203
|
+
initialConversation,
|
|
3204
|
+
printInitialTranscript: true
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
2756
3207
|
if (execution.kind === "shell") {
|
|
2757
3208
|
const project = await client.projects.describe(options.project);
|
|
2758
3209
|
return await (0, import_shell.runR5dctlShell)({
|
|
@@ -2802,7 +3253,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2802
3253
|
0 && (module.exports = {
|
|
2803
3254
|
advanceTransientDevicePollBackoff,
|
|
2804
3255
|
collectK8sUsage,
|
|
2805
|
-
|
|
3256
|
+
dispatchMergeCommand,
|
|
3257
|
+
dispatchSessionRunCommand,
|
|
3258
|
+
followR5dctlSession,
|
|
2806
3259
|
formatK8sCpu,
|
|
2807
3260
|
formatK8sMemory,
|
|
2808
3261
|
formatProcessAge,
|
|
@@ -2813,8 +3266,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2813
3266
|
getTransientDevicePollDelay,
|
|
2814
3267
|
handleAuthLogin,
|
|
2815
3268
|
main,
|
|
3269
|
+
parseAnswerEnvRequestCommandArgs,
|
|
2816
3270
|
parseAnswerFlags,
|
|
2817
|
-
|
|
3271
|
+
parseAnswerQuestionsCommandArgs,
|
|
2818
3272
|
parseConversationRenderArgs,
|
|
2819
3273
|
parseConversationWorkDetailArgs,
|
|
2820
3274
|
parseEnvFlags,
|
|
@@ -2822,15 +3276,14 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2822
3276
|
parseGetEnvsArgs,
|
|
2823
3277
|
parseGlobalArgs,
|
|
2824
3278
|
parseK8sUsageArgs,
|
|
3279
|
+
parseMergeCommand,
|
|
2825
3280
|
parsePromptArgs,
|
|
2826
3281
|
parsePsHistoryArgs,
|
|
2827
3282
|
parsePsListArgs,
|
|
3283
|
+
parseSessionRunCommand,
|
|
2828
3284
|
parseSetEnvArgs,
|
|
2829
3285
|
parseShellArgs,
|
|
2830
3286
|
readDotenvFile,
|
|
2831
|
-
renderBranchAgentMerge,
|
|
2832
|
-
renderBranchAgentStart,
|
|
2833
|
-
renderBranchAgentStatus,
|
|
2834
3287
|
renderConversationNodeResponse,
|
|
2835
3288
|
renderConversationOverviewResponse,
|
|
2836
3289
|
renderConversationResponse,
|
|
@@ -2841,8 +3294,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2841
3294
|
renderProcessHistory,
|
|
2842
3295
|
renderProcessInspection,
|
|
2843
3296
|
renderProcessList,
|
|
3297
|
+
renderSessionRunStart,
|
|
3298
|
+
renderSessionRunStatus,
|
|
2844
3299
|
renderWorkspaceStatus,
|
|
2845
3300
|
renderWorkspaceSync,
|
|
3301
|
+
renderWorktreeMerge,
|
|
2846
3302
|
resolveCommandExecution,
|
|
2847
3303
|
runR5dctlCli,
|
|
2848
3304
|
summarizeEnvData,
|