@ricsam/r5dctl 0.0.73 → 0.0.75
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 +11 -14
- package/dist/cjs/cli.cjs +92 -234
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +91 -230
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +5 -69
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -90,13 +90,6 @@ r5dctl session status <session-id>
|
|
|
90
90
|
r5dctl session prompt --worker <label> --mode agent --model max <session-id> "Also check the migration"
|
|
91
91
|
r5dctl session stop <session-id>
|
|
92
92
|
|
|
93
|
-
r5dctl -p <project> merge fix/tests
|
|
94
|
-
r5dctl -p <project> merge --worker <label> --create-conflict-worktree fix/tests
|
|
95
|
-
r5dctl -p <project> merge --worker <label> --launch-conflict-resolution-agent fix/tests
|
|
96
|
-
r5dctl -p <project> merge --worker <label> --launch-conflict-resolution-agent --no-wait fix/tests
|
|
97
|
-
r5dctl -p <project> merge status <attempt-id>
|
|
98
|
-
r5dctl -p <project> merge finish --worker <label> conflicts/fix-tests --summary "Resolved the conflicts"
|
|
99
|
-
|
|
100
93
|
r5dctl -p <project> -b <branch> shell
|
|
101
94
|
r5dctl -p <project> -b <branch> shell -c "bun test"
|
|
102
95
|
|
|
@@ -109,17 +102,21 @@ r5dctl k8s usage
|
|
|
109
102
|
r5dctl k8s usage --namespace default --workload api --container server
|
|
110
103
|
r5dctl k8s usage --window 7d
|
|
111
104
|
r5dctl k8s usage --workload default/api --window 7d --json
|
|
105
|
+
r5dctl vcluster trusted-workflow add --repo owner/repository --workflow .github/workflows/deploy.yml
|
|
106
|
+
r5dctl vcluster trusted-workflow list
|
|
107
|
+
r5dctl vcluster trusted-workflow remove <workflow-id>
|
|
112
108
|
```
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
Trusted workflows authenticate Kubernetes through the dedicated
|
|
111
|
+
`ricsam/r5d-cluster-auth` GitHub Action. They do not run or authenticate
|
|
112
|
+
`r5dctl` inside GitHub Actions, and no R5D secret needs to be stored in the
|
|
113
|
+
repository. Trust applies to that workflow path on every Git ref and grants
|
|
114
|
+
cluster-admin, so protect workflow changes and do not invoke it for untrusted
|
|
115
|
+
pull requests.
|
|
119
116
|
|
|
120
|
-
|
|
117
|
+
`session start` creates an ordinary agent-mode chat on an explicitly selected existing worktree, or creates a named linked worktree from an explicit `worktree:<branch>` source. It requires project, worker, model, and prompt and never infers them from the current shell. Start returns a durable `provisioning` handle immediately; workspace synchronization, linked-worktree creation, and prompt enqueue continue in the background and are visible through `session status`. Idempotent request retries return the same handle. Prompts return after durable enqueue. Task specialization belongs in the prompt; there are no predefined debug, explore, research, or test agents. `session stop` returns as soon as the agent run is stopped; the stopped worktree's workspace Git synchronization finishes asynchronously behind the response.
|
|
121
118
|
|
|
122
|
-
|
|
119
|
+
Add `-f`/`--follow`/`--watch` to `session start` to poll the provisioning handle and attach after enqueue, or to `conversation` to print the current transcript and follow new output. Assistant text is written to stdout and lifecycle/tool progress to stderr; `--tools` includes tool calls and results. With global `--json`, follow mode emits one JSON event per line. Ctrl-C only detaches and never stops the session. The CLI reconnects transiently interrupted event streams and resynchronizes committed conversation revisions without repeating assistant text.
|
|
123
120
|
|
|
124
121
|
`shell` connects to the same live worker PTY as the web Shell tab. Without `-c` it attaches the local terminal interactively. With `-c`/`--command`, it runs the supplied shell expression, streams combined PTY output, and exits with the remote command's status. Because command output uses a PTY, stdout and stderr are combined and programs may emit colors or other terminal control sequences.
|
|
125
122
|
|
package/dist/cjs/cli.cjs
CHANGED
|
@@ -30,7 +30,6 @@ var cli_exports = {};
|
|
|
30
30
|
__export(cli_exports, {
|
|
31
31
|
advanceTransientDevicePollBackoff: () => advanceTransientDevicePollBackoff,
|
|
32
32
|
collectK8sUsage: () => collectK8sUsage,
|
|
33
|
-
dispatchMergeCommand: () => dispatchMergeCommand,
|
|
34
33
|
dispatchSessionRunCommand: () => dispatchSessionRunCommand,
|
|
35
34
|
followR5dctlSession: () => followR5dctlSession,
|
|
36
35
|
formatK8sCpu: () => formatK8sCpu,
|
|
@@ -42,7 +41,6 @@ __export(cli_exports, {
|
|
|
42
41
|
getR5dctlVersion: () => getR5dctlVersion,
|
|
43
42
|
getTransientDevicePollDelay: () => getTransientDevicePollDelay,
|
|
44
43
|
handleAuthLogin: () => handleAuthLogin,
|
|
45
|
-
isMergeAttemptSettled: () => isMergeAttemptSettled,
|
|
46
44
|
main: () => main,
|
|
47
45
|
parseAnswerEnvRequestCommandArgs: () => parseAnswerEnvRequestCommandArgs,
|
|
48
46
|
parseAnswerFlags: () => parseAnswerFlags,
|
|
@@ -56,7 +54,6 @@ __export(cli_exports, {
|
|
|
56
54
|
parseGetEnvsArgs: () => parseGetEnvsArgs,
|
|
57
55
|
parseGlobalArgs: () => parseGlobalArgs,
|
|
58
56
|
parseK8sUsageArgs: () => parseK8sUsageArgs,
|
|
59
|
-
parseMergeCommand: () => parseMergeCommand,
|
|
60
57
|
parsePromptArgs: () => parsePromptArgs,
|
|
61
58
|
parsePsHistoryArgs: () => parsePsHistoryArgs,
|
|
62
59
|
parsePsListArgs: () => parsePsListArgs,
|
|
@@ -80,10 +77,10 @@ __export(cli_exports, {
|
|
|
80
77
|
renderSessionRunStatus: () => renderSessionRunStatus,
|
|
81
78
|
renderWorkspaceStatus: () => renderWorkspaceStatus,
|
|
82
79
|
renderWorkspaceSync: () => renderWorkspaceSync,
|
|
83
|
-
renderWorktreeMerge: () => renderWorktreeMerge,
|
|
84
80
|
resolveCommandExecution: () => resolveCommandExecution,
|
|
85
81
|
runR5dctlCli: () => runR5dctlCli,
|
|
86
82
|
summarizeEnvData: () => summarizeEnvData,
|
|
83
|
+
waitForSessionProvisioning: () => waitForSessionProvisioning,
|
|
87
84
|
writeConfig: () => writeConfig
|
|
88
85
|
});
|
|
89
86
|
module.exports = __toCommonJS(cli_exports);
|
|
@@ -97,7 +94,7 @@ var import_promises = require("node:timers/promises");
|
|
|
97
94
|
var import_dotenv = require("dotenv");
|
|
98
95
|
var import_r5d_api = require("@ricsam/r5d-api");
|
|
99
96
|
var import_shell = require("./shell.cjs");
|
|
100
|
-
const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent", "large_diff_remediation"
|
|
97
|
+
const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent", "large_diff_remediation"]);
|
|
101
98
|
const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent"]);
|
|
102
99
|
const MODEL_TIERS = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
|
|
103
100
|
const R5DCTL_PACKAGE_NAME = "@ricsam/r5dctl";
|
|
@@ -149,6 +146,9 @@ const PS_HELP_TEXT = [
|
|
|
149
146
|
const K8S_HELP_TEXT = [
|
|
150
147
|
"Usage:",
|
|
151
148
|
" r5dctl k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
149
|
+
" r5dctl vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
150
|
+
" r5dctl vcluster trusted-workflow list",
|
|
151
|
+
" r5dctl vcluster trusted-workflow remove <workflow-id>",
|
|
152
152
|
"",
|
|
153
153
|
"Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
|
|
154
154
|
""
|
|
@@ -165,16 +165,6 @@ const SESSION_RUN_HELP_TEXT = [
|
|
|
165
165
|
"Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
|
|
166
166
|
""
|
|
167
167
|
].join("\n");
|
|
168
|
-
const MERGE_HELP_TEXT = [
|
|
169
|
-
"Usage:",
|
|
170
|
-
" r5dctl -p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] [--no-wait] <source-worktree>",
|
|
171
|
-
" r5dctl -p <project> merge status <attempt-id>",
|
|
172
|
-
' r5dctl -p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
173
|
-
"",
|
|
174
|
-
"Merge a managed worktree into its recorded parent worktree.",
|
|
175
|
-
"Conflict worktrees provision in the background; merge waits by default and --no-wait returns the attempt immediately.",
|
|
176
|
-
""
|
|
177
|
-
].join("\n");
|
|
178
168
|
const SHARED_HELP_ENTRIES = [
|
|
179
169
|
{ section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
|
|
180
170
|
{ section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
|
|
@@ -185,16 +175,16 @@ const SHARED_HELP_ENTRIES = [
|
|
|
185
175
|
},
|
|
186
176
|
{ section: "auth", usage: "auth api-key list", description: "List API keys for the current account." },
|
|
187
177
|
{ section: "auth", usage: "auth api-key revoke <key-id>", description: "Revoke an API key." },
|
|
188
|
-
{ section: "workspace", usage: "workspace status", description: "Inspect
|
|
178
|
+
{ section: "workspace", usage: "workspace status", description: "Inspect per-user workspace Git state and active remediation." },
|
|
189
179
|
{
|
|
190
180
|
section: "workspace",
|
|
191
181
|
usage: "workspace sync [--worker <label>]",
|
|
192
|
-
description: "
|
|
182
|
+
description: "Commit, rebase, and push a connected worker's workspace now."
|
|
193
183
|
},
|
|
194
184
|
{
|
|
195
185
|
section: "workspace",
|
|
196
186
|
usage: "workspace reset [--worker <label>] --confirm",
|
|
197
|
-
description: "Destructively discard a blocked
|
|
187
|
+
description: "Destructively discard a blocked local workspace commit and restore origin/main."
|
|
198
188
|
},
|
|
199
189
|
{ section: "projects", usage: "get projects", description: "List projects you can access." },
|
|
200
190
|
{ section: "projects", usage: "describe project <namespace/name|id>", description: "Show details for a project." },
|
|
@@ -305,6 +295,17 @@ const SHARED_HELP_ENTRIES = [
|
|
|
305
295
|
usage: "k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
306
296
|
description: "Show current and rolling CPU and memory usage for managed-vCluster workload containers."
|
|
307
297
|
},
|
|
298
|
+
{
|
|
299
|
+
section: "kubernetes",
|
|
300
|
+
usage: "vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
301
|
+
description: "Trust an exact GitHub Actions workflow path to authenticate kubectl against your managed vCluster."
|
|
302
|
+
},
|
|
303
|
+
{ section: "kubernetes", usage: "vcluster trusted-workflow list", description: "List trusted GitHub Actions workflows." },
|
|
304
|
+
{
|
|
305
|
+
section: "kubernetes",
|
|
306
|
+
usage: "vcluster trusted-workflow remove <workflow-id>",
|
|
307
|
+
description: "Revoke a trusted GitHub Actions workflow and its outstanding credentials."
|
|
308
|
+
},
|
|
308
309
|
{
|
|
309
310
|
section: "processes",
|
|
310
311
|
usage: "ps list [--worker <label>]",
|
|
@@ -320,22 +321,7 @@ const SHARED_HELP_ENTRIES = [
|
|
|
320
321
|
usage: "ps inspect <run-id>",
|
|
321
322
|
description: "Inspect process metadata and bounded stdout/stderr tails."
|
|
322
323
|
},
|
|
323
|
-
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." }
|
|
324
|
-
{
|
|
325
|
-
section: "merge",
|
|
326
|
-
usage: "-p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] [--no-wait] <source-worktree>",
|
|
327
|
-
description: "Merge a worktree into its recorded parent, with an explicit conflict strategy."
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
section: "merge",
|
|
331
|
-
usage: "-p <project> merge status <attempt-id>",
|
|
332
|
-
description: "Show a merge attempt's provisioning and resolution status."
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
section: "merge",
|
|
336
|
-
usage: '-p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
337
|
-
description: "Publish and finish a manually resolved conflict worktree."
|
|
338
|
-
}
|
|
324
|
+
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." }
|
|
339
325
|
];
|
|
340
326
|
const HELP_SECTION_ORDER = [
|
|
341
327
|
"auth",
|
|
@@ -346,8 +332,7 @@ const HELP_SECTION_ORDER = [
|
|
|
346
332
|
"sessions",
|
|
347
333
|
"kubernetes",
|
|
348
334
|
"processes",
|
|
349
|
-
"shell"
|
|
350
|
-
"merge"
|
|
335
|
+
"shell"
|
|
351
336
|
];
|
|
352
337
|
const HELP_SECTION_TITLES = {
|
|
353
338
|
auth: "Auth",
|
|
@@ -358,8 +343,7 @@ const HELP_SECTION_TITLES = {
|
|
|
358
343
|
sessions: "Sessions",
|
|
359
344
|
kubernetes: "Kubernetes",
|
|
360
345
|
processes: "Processes",
|
|
361
|
-
shell: "Shell"
|
|
362
|
-
merge: "Worktree merging"
|
|
346
|
+
shell: "Shell"
|
|
363
347
|
};
|
|
364
348
|
function getCliHelpText() {
|
|
365
349
|
const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
|
|
@@ -637,56 +621,6 @@ function parseSessionRunCommand(options, args) {
|
|
|
637
621
|
}
|
|
638
622
|
throw new Error(`Unknown session command: ${subcommand}`);
|
|
639
623
|
}
|
|
640
|
-
function parseMergeCommand(options, args) {
|
|
641
|
-
const project = requireValue(options.project, "--project/-p is required for `merge`");
|
|
642
|
-
if (options.branch) throw new Error("--branch/-b is not supported for `merge`; pass the source worktree explicitly");
|
|
643
|
-
if (options.session) throw new Error("--session/-s is not supported for `merge`");
|
|
644
|
-
if (args[0] === "finish") {
|
|
645
|
-
const parsed2 = parseCommandOperands(args.slice(1), /* @__PURE__ */ new Set(["--worker", "--summary"]), /* @__PURE__ */ new Map(), "merge finish");
|
|
646
|
-
const conflictWorktree = requireValue(parsed2.positionals[0], "Conflict worktree is required for `merge finish`");
|
|
647
|
-
if (parsed2.positionals.length > 1) throw new Error(`Unexpected merge finish argument: ${parsed2.positionals[1]}`);
|
|
648
|
-
return {
|
|
649
|
-
kind: "finish",
|
|
650
|
-
project,
|
|
651
|
-
conflictWorktree,
|
|
652
|
-
worker: requireWorker(parsed2.values),
|
|
653
|
-
...parsed2.values.get("--summary")?.trim() ? { summary: parsed2.values.get("--summary").trim() } : {}
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
if (args[0] === "status") {
|
|
657
|
-
const parsed2 = parseCommandOperands(args.slice(1), /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), "merge status");
|
|
658
|
-
const attemptId = requireValue(parsed2.positionals[0], "Attempt id is required for `merge status`");
|
|
659
|
-
if (parsed2.positionals.length > 1) throw new Error(`Unexpected merge status argument: ${parsed2.positionals[1]}`);
|
|
660
|
-
return { kind: "status", project, attemptId };
|
|
661
|
-
}
|
|
662
|
-
const parsed = parseCommandOperands(
|
|
663
|
-
args,
|
|
664
|
-
/* @__PURE__ */ new Set(["--worker"]),
|
|
665
|
-
/* @__PURE__ */ new Map([
|
|
666
|
-
["--create-conflict-worktree", "--create-conflict-worktree"],
|
|
667
|
-
["--launch-conflict-resolution-agent", "--launch-conflict-resolution-agent"],
|
|
668
|
-
["--no-wait", "--no-wait"]
|
|
669
|
-
]),
|
|
670
|
-
"merge"
|
|
671
|
-
);
|
|
672
|
-
const sourceWorktree = requireValue(parsed.positionals[0], "Source worktree is required for `merge`");
|
|
673
|
-
if (parsed.positionals.length > 1) throw new Error(`Unexpected merge argument: ${parsed.positionals[1]}`);
|
|
674
|
-
const createWorktree = parsed.booleans.has("--create-conflict-worktree");
|
|
675
|
-
const launchAgent = parsed.booleans.has("--launch-conflict-resolution-agent");
|
|
676
|
-
if (createWorktree && launchAgent) {
|
|
677
|
-
throw new Error("Use only one of --create-conflict-worktree or --launch-conflict-resolution-agent");
|
|
678
|
-
}
|
|
679
|
-
const conflictMode = createWorktree ? "worktree" : launchAgent ? "agent" : "none";
|
|
680
|
-
const worker = parsed.values.get("--worker")?.trim();
|
|
681
|
-
return {
|
|
682
|
-
kind: "merge",
|
|
683
|
-
project,
|
|
684
|
-
sourceWorktree,
|
|
685
|
-
conflictMode,
|
|
686
|
-
...worker ? { worker } : {},
|
|
687
|
-
...parsed.booleans.has("--no-wait") ? { noWait: true } : {}
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
624
|
function assertAuthLoginArgs(args) {
|
|
691
625
|
const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
|
|
692
626
|
const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
|
|
@@ -1513,16 +1447,15 @@ function renderWorkspaceStatus(status) {
|
|
|
1513
1447
|
`Workspace HEAD: ${status.head ?? "(not initialized)"}`,
|
|
1514
1448
|
`Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
|
|
1515
1449
|
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1516
|
-
`Materialization: ${status.materialization ? `${status.materialization.state}; ${status.materialization.materializedHead ?? "(none)"} -> ${status.materialization.desiredHead ?? "(none)"}; lag ${status.materialization.lagMs}ms` : "not initialized"}`,
|
|
1517
1450
|
...status.workers.map(
|
|
1518
|
-
(worker) => `Worker ${worker.workerLabel}: ${worker.
|
|
1451
|
+
(worker) => `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`
|
|
1519
1452
|
)
|
|
1520
1453
|
];
|
|
1521
1454
|
return `${lines.join("\n")}
|
|
1522
1455
|
`;
|
|
1523
1456
|
}
|
|
1524
1457
|
function renderWorkspaceSync(result) {
|
|
1525
|
-
const head = result.publishedHead ?? result.
|
|
1458
|
+
const head = result.publishedHead ?? result.localHead ?? result.startingHead ?? "(none)";
|
|
1526
1459
|
const discarded = result.discardedPaths.length > 0 ? `
|
|
1527
1460
|
Discarded paths: ${result.discardedPaths.join(", ")}` : "";
|
|
1528
1461
|
const error = result.error ? `
|
|
@@ -1898,30 +1831,6 @@ function formatStatusValue(value) {
|
|
|
1898
1831
|
}
|
|
1899
1832
|
return value === void 0 || value === null ? void 0 : String(value);
|
|
1900
1833
|
}
|
|
1901
|
-
function appendMergeDetails(lines, merge) {
|
|
1902
|
-
const mergeStatus = formatStatusValue(merge.status);
|
|
1903
|
-
lines.push(`Merge: ${mergeStatus ?? "pending"}`);
|
|
1904
|
-
for (const [label, key] of [
|
|
1905
|
-
["Attempt", "attemptId"],
|
|
1906
|
-
["Source worktree", "sourceWorktree"],
|
|
1907
|
-
["Target worktree", "targetWorktree"],
|
|
1908
|
-
["Commit", "commitHash"],
|
|
1909
|
-
["Resolver agent", "resolverSessionId"],
|
|
1910
|
-
["Conflict worktree", "conflictWorktree"],
|
|
1911
|
-
["Resolver worktree", "folderPath"]
|
|
1912
|
-
]) {
|
|
1913
|
-
const value = responseString(merge, key);
|
|
1914
|
-
if (value) lines.push(`${label}: ${value}`);
|
|
1915
|
-
}
|
|
1916
|
-
const conflictedFiles = Array.isArray(merge.conflictedFiles) ? merge.conflictedFiles.filter((file) => typeof file === "string") : [];
|
|
1917
|
-
if (conflictedFiles.length > 0) {
|
|
1918
|
-
lines.push("", "Conflicts:", ...conflictedFiles.map((file) => `- ${file}`));
|
|
1919
|
-
}
|
|
1920
|
-
const message = responseString(merge, "message");
|
|
1921
|
-
if (message) lines.push("", message);
|
|
1922
|
-
const failureReason = responseString(merge, "failureReason");
|
|
1923
|
-
if (failureReason) lines.push(`Failure: ${failureReason}`);
|
|
1924
|
-
}
|
|
1925
1834
|
function renderSessionRunStart(session) {
|
|
1926
1835
|
const lines = [
|
|
1927
1836
|
`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`,
|
|
@@ -1970,28 +1879,6 @@ function renderSessionRunStatus(session) {
|
|
|
1970
1879
|
return `${lines.join("\n")}
|
|
1971
1880
|
`;
|
|
1972
1881
|
}
|
|
1973
|
-
function renderWorktreeMerge(result, input) {
|
|
1974
|
-
const lines = [];
|
|
1975
|
-
appendMergeDetails(lines, result);
|
|
1976
|
-
const status = responseString(result, "status");
|
|
1977
|
-
const conflictWorktree = responseString(result, "conflictWorktree");
|
|
1978
|
-
const resolverSessionId = responseString(result, "resolverSessionId");
|
|
1979
|
-
const attemptId = responseString(result, "attemptId");
|
|
1980
|
-
const worker = responseString(result, "worker") ?? input.worker;
|
|
1981
|
-
if (resolverSessionId) lines.push("", `Resolver session: ${resolverSessionId}`, ` r5dctl session stop ${resolverSessionId}`);
|
|
1982
|
-
if (conflictWorktree && worker && ["conflicts", "conflict_worktree_created", "resolving"].includes(status ?? "")) {
|
|
1983
|
-
lines.push(
|
|
1984
|
-
"",
|
|
1985
|
-
"Finish after resolving and reviewing the conflict worktree:",
|
|
1986
|
-
` r5dctl -p ${input.project} merge finish --worker ${worker} ${conflictWorktree} --summary "<summary>"`
|
|
1987
|
-
);
|
|
1988
|
-
}
|
|
1989
|
-
if (attemptId && (status === "provisioning" || status === "resolving" && result.resolverAgentRunning === false)) {
|
|
1990
|
-
lines.push("", "Check progress:", ` r5dctl -p ${input.project} merge status ${attemptId}`);
|
|
1991
|
-
}
|
|
1992
|
-
return `${lines.join("\n")}
|
|
1993
|
-
`;
|
|
1994
|
-
}
|
|
1995
1882
|
async function dispatchSessionRunCommand(client, command) {
|
|
1996
1883
|
if (command.kind === "start") {
|
|
1997
1884
|
const commonInput = {
|
|
@@ -2028,70 +1915,16 @@ async function dispatchSessionRunCommand(client, command) {
|
|
|
2028
1915
|
const data = await client.sessions.stop(command.sessionId);
|
|
2029
1916
|
return { data, human: renderSessionRunStatus(data) };
|
|
2030
1917
|
}
|
|
2031
|
-
|
|
2032
|
-
const
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
}
|
|
2040
|
-
async function pollMergeAttemptUntilSettled(client, project, attemptId, initial, options) {
|
|
2041
|
-
const intervalMs = options.pollIntervalMs ?? MERGE_POLL_INTERVAL_MS;
|
|
2042
|
-
const timeoutMs = options.pollTimeoutMs ?? MERGE_POLL_TIMEOUT_MS;
|
|
2043
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2044
|
-
const onProgress = options.onProgress ?? (() => {
|
|
2045
|
-
});
|
|
2046
|
-
let latest = initial;
|
|
2047
|
-
let worktreeReadyReported = latest.worktreeReady === true;
|
|
2048
|
-
let consecutiveFailures = 0;
|
|
2049
|
-
let waitedMs = 0;
|
|
2050
|
-
while (!isMergeAttemptSettled(latest)) {
|
|
2051
|
-
if (waitedMs >= timeoutMs) {
|
|
2052
|
-
onProgress(`Still waiting after ${Math.round(timeoutMs / 6e4)} minutes; check later with: r5dctl -p ${project} merge status ${attemptId}`);
|
|
2053
|
-
return latest;
|
|
2054
|
-
}
|
|
2055
|
-
await sleep2(intervalMs);
|
|
2056
|
-
waitedMs += intervalMs;
|
|
2057
|
-
try {
|
|
2058
|
-
latest = await client.projects.worktrees.mergeAttempt(project, attemptId);
|
|
2059
|
-
consecutiveFailures = 0;
|
|
2060
|
-
} catch (error) {
|
|
2061
|
-
consecutiveFailures += 1;
|
|
2062
|
-
if (consecutiveFailures >= MERGE_POLL_MAX_CONSECUTIVE_FAILURES) throw error;
|
|
2063
|
-
continue;
|
|
2064
|
-
}
|
|
2065
|
-
if (!worktreeReadyReported && latest.worktreeReady === true) {
|
|
2066
|
-
worktreeReadyReported = true;
|
|
2067
|
-
onProgress("Conflict worktree ready.");
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
return latest;
|
|
2071
|
-
}
|
|
2072
|
-
async function dispatchMergeCommand(client, command, pollOptions = {}) {
|
|
2073
|
-
if (command.kind === "merge") {
|
|
2074
|
-
const data2 = await client.projects.worktrees.merge(command.project, command.sourceWorktree, {
|
|
2075
|
-
conflictMode: command.conflictMode,
|
|
2076
|
-
...command.worker ? { worker: command.worker } : {}
|
|
2077
|
-
});
|
|
2078
|
-
const attemptId = responseString(data2, "attemptId");
|
|
2079
|
-
if (command.noWait || !attemptId || isMergeAttemptSettled(data2)) {
|
|
2080
|
-
return { data: data2, human: renderWorktreeMerge(data2, command) };
|
|
2081
|
-
}
|
|
2082
|
-
pollOptions.onProgress?.(`Provisioning the conflict worktree (attempt ${attemptId})...`);
|
|
2083
|
-
const settled = await pollMergeAttemptUntilSettled(client, command.project, attemptId, data2, pollOptions);
|
|
2084
|
-
return { data: settled, human: renderWorktreeMerge(settled, command) };
|
|
2085
|
-
}
|
|
2086
|
-
if (command.kind === "status") {
|
|
2087
|
-
const data2 = await client.projects.worktrees.mergeAttempt(command.project, command.attemptId);
|
|
2088
|
-
return { data: data2, human: renderWorktreeMerge(data2, { project: command.project }) };
|
|
1918
|
+
async function waitForSessionProvisioning(client, initial, options = {}) {
|
|
1919
|
+
const sessionId = requireValue(responseString(initial, "sessionId"), "Session start did not return a session id");
|
|
1920
|
+
const wait = options.wait ?? import_promises.setTimeout;
|
|
1921
|
+
const intervalMs = options.intervalMs ?? 500;
|
|
1922
|
+
let current = initial;
|
|
1923
|
+
while (formatStatusValue(current.status) === "provisioning") {
|
|
1924
|
+
await wait(intervalMs);
|
|
1925
|
+
current = await client.sessions.status(sessionId);
|
|
2089
1926
|
}
|
|
2090
|
-
|
|
2091
|
-
worker: command.worker,
|
|
2092
|
-
...command.summary ? { summary: command.summary } : {}
|
|
2093
|
-
});
|
|
2094
|
-
return { data, human: renderWorktreeMerge(data, command) };
|
|
1927
|
+
return current;
|
|
2095
1928
|
}
|
|
2096
1929
|
function parseProjectUpdateArgs(args) {
|
|
2097
1930
|
const input = {};
|
|
@@ -2642,6 +2475,40 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
2642
2475
|
write(report, renderK8sUsageReport(report));
|
|
2643
2476
|
return;
|
|
2644
2477
|
}
|
|
2478
|
+
if (first === "vcluster" && second === "trusted-workflow") {
|
|
2479
|
+
const subcommand = requireValue(args[2], "Missing trusted-workflow command");
|
|
2480
|
+
if (subcommand === "add") {
|
|
2481
|
+
const parsed = parseCommandOperands(args.slice(3), /* @__PURE__ */ new Set(["--repo", "--workflow"]), /* @__PURE__ */ new Map(), "trusted-workflow add");
|
|
2482
|
+
if (parsed.positionals.length > 0) throw new Error(`Unexpected trusted-workflow add value: ${parsed.positionals[0]}`);
|
|
2483
|
+
const workflow = await client.k8s.trustedWorkflows.add({
|
|
2484
|
+
repository: requireValue(parsed.values.get("--repo"), "--repo <owner/repository> is required"),
|
|
2485
|
+
workflowPath: requireValue(parsed.values.get("--workflow"), "--workflow <path> is required")
|
|
2486
|
+
});
|
|
2487
|
+
write(workflow, "Trusted GitHub workflow configured successfully.\n");
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
if (subcommand === "list") {
|
|
2491
|
+
if (args.length > 3) throw new Error(`Unexpected trusted-workflow list value: ${args[3]}`);
|
|
2492
|
+
const workflows = await client.k8s.trustedWorkflows.list();
|
|
2493
|
+
write(
|
|
2494
|
+
workflows,
|
|
2495
|
+
workflows.length === 0 ? "No trusted GitHub workflows.\n" : `${workflows.map(
|
|
2496
|
+
(workflow) => `${workflow.id} ${workflow.repository} ${workflow.workflowPath} ${workflow.revokedAt ? "revoked" : "active"}`
|
|
2497
|
+
).join("\n")}
|
|
2498
|
+
`
|
|
2499
|
+
);
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
if (subcommand === "remove") {
|
|
2503
|
+
const workflowId = requireValue(args[3], "Missing trusted workflow id");
|
|
2504
|
+
if (args.length > 4) throw new Error(`Unexpected trusted-workflow remove value: ${args[4]}`);
|
|
2505
|
+
const result = await client.k8s.trustedWorkflows.revoke(workflowId);
|
|
2506
|
+
if (!result.success) throw new Error("Trusted GitHub workflow was not found or was already revoked");
|
|
2507
|
+
write(result, "Trusted GitHub workflow revoked.\n");
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
throw new Error(`Unknown trusted-workflow command: ${subcommand}`);
|
|
2511
|
+
}
|
|
2645
2512
|
if (first === "ps" && second === "list") {
|
|
2646
2513
|
const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
|
|
2647
2514
|
write(processes, renderProcessList(processes));
|
|
@@ -2875,12 +2742,6 @@ function resolveCommandExecution(options, rest) {
|
|
|
2875
2742
|
text: SESSION_RUN_HELP_TEXT
|
|
2876
2743
|
};
|
|
2877
2744
|
}
|
|
2878
|
-
if (command === "merge" && commandArgs.length === 0) {
|
|
2879
|
-
return {
|
|
2880
|
-
kind: "cli-help",
|
|
2881
|
-
text: MERGE_HELP_TEXT
|
|
2882
|
-
};
|
|
2883
|
-
}
|
|
2884
2745
|
if (trailingHelp) {
|
|
2885
2746
|
const cliOnlyHelp = findCliOnlyCommandHelp(normalizedRest);
|
|
2886
2747
|
if (cliOnlyHelp) {
|
|
@@ -3011,12 +2872,23 @@ function resolveCommandExecution(options, rest) {
|
|
|
3011
2872
|
}
|
|
3012
2873
|
if (command === "k8s") {
|
|
3013
2874
|
const subcommand = commandArgs[0];
|
|
3014
|
-
if (subcommand !== "usage") throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
|
|
3015
2875
|
if (options.project || options.branch || options.session) {
|
|
3016
|
-
throw new Error("Project, branch, and session scopes are not supported for `k8s
|
|
2876
|
+
throw new Error("Project, branch, and session scopes are not supported for `k8s`");
|
|
2877
|
+
}
|
|
2878
|
+
if (subcommand === "usage") {
|
|
2879
|
+
parseK8sUsageArgs(commandArgs.slice(1));
|
|
2880
|
+
return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
|
|
2881
|
+
}
|
|
2882
|
+
throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
|
|
2883
|
+
}
|
|
2884
|
+
if (command === "vcluster") {
|
|
2885
|
+
if (options.project || options.branch || options.session) {
|
|
2886
|
+
throw new Error("Project, branch, and session scopes are not supported for `vcluster`");
|
|
2887
|
+
}
|
|
2888
|
+
if (commandArgs[0] !== "trusted-workflow") {
|
|
2889
|
+
throw new Error(`Unknown vcluster command: ${commandArgs[0] ?? ""}`.trim());
|
|
3017
2890
|
}
|
|
3018
|
-
|
|
3019
|
-
return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
|
|
2891
|
+
return { kind: "plugin", pluginArgs: ["vcluster", "trusted-workflow", ...commandArgs.slice(1)] };
|
|
3020
2892
|
}
|
|
3021
2893
|
if (command === "get") {
|
|
3022
2894
|
const target = commandArgs[0];
|
|
@@ -3268,9 +3140,6 @@ function resolveCommandExecution(options, rest) {
|
|
|
3268
3140
|
if (command === "session") {
|
|
3269
3141
|
return { kind: "session-run", command: parseSessionRunCommand(options, commandArgs) };
|
|
3270
3142
|
}
|
|
3271
|
-
if (command === "merge") {
|
|
3272
|
-
return { kind: "merge", command: parseMergeCommand(options, commandArgs) };
|
|
3273
|
-
}
|
|
3274
3143
|
if (command === "answer-questions") {
|
|
3275
3144
|
const sessionId = options.session;
|
|
3276
3145
|
if (!sessionId) {
|
|
@@ -3332,7 +3201,7 @@ async function runCommand(argv) {
|
|
|
3332
3201
|
if (execution.kind === "session-run") {
|
|
3333
3202
|
if (!options.json) {
|
|
3334
3203
|
if (execution.command.kind === "start") {
|
|
3335
|
-
process.stderr.write("Starting session
|
|
3204
|
+
process.stderr.write("Starting session...\n");
|
|
3336
3205
|
} else if (execution.command.kind === "stop") {
|
|
3337
3206
|
process.stderr.write(`Stopping session ${execution.command.sessionId}...
|
|
3338
3207
|
`);
|
|
@@ -3347,28 +3216,20 @@ async function runCommand(argv) {
|
|
|
3347
3216
|
if (options.json) process.stdout.write(`${JSON.stringify({ type: "session_started", data: result.data })}
|
|
3348
3217
|
`);
|
|
3349
3218
|
else process.stderr.write(result.human);
|
|
3219
|
+
const ready = await waitForSessionProvisioning(client, result.data);
|
|
3220
|
+
const readyStatus = formatStatusValue(ready.status);
|
|
3221
|
+
if (readyStatus === "failed" || readyStatus === "stopped") {
|
|
3222
|
+
if (options.json) process.stdout.write(`${JSON.stringify({ type: "session_status", data: ready })}
|
|
3223
|
+
`);
|
|
3224
|
+
else process.stderr.write(renderSessionRunStatus(ready));
|
|
3225
|
+
return readyStatus === "failed" ? 1 : 0;
|
|
3226
|
+
}
|
|
3350
3227
|
return await followWithInterrupt(client, sessionId, {
|
|
3351
3228
|
json: options.json,
|
|
3352
3229
|
includeTools: execution.command.includeTools,
|
|
3353
3230
|
printInitialTranscript: false
|
|
3354
3231
|
});
|
|
3355
3232
|
}
|
|
3356
|
-
if (execution.kind === "merge") {
|
|
3357
|
-
if (!options.json) {
|
|
3358
|
-
if (execution.command.kind === "merge") {
|
|
3359
|
-
process.stderr.write("Merging (publishing the workspace and computing the merge)...\n");
|
|
3360
|
-
} else if (execution.command.kind === "finish") {
|
|
3361
|
-
process.stderr.write(`Finalizing ${execution.command.conflictWorktree} (publishing the resolver workspace)...
|
|
3362
|
-
`);
|
|
3363
|
-
}
|
|
3364
|
-
}
|
|
3365
|
-
const result = await dispatchMergeCommand(client, execution.command, {
|
|
3366
|
-
...options.json ? {} : { onProgress: (line) => process.stderr.write(`${line}
|
|
3367
|
-
`) }
|
|
3368
|
-
});
|
|
3369
|
-
writeDataOutput(options.json, result.data, result.human);
|
|
3370
|
-
return 0;
|
|
3371
|
-
}
|
|
3372
3233
|
if (execution.kind === "conversation-follow") {
|
|
3373
3234
|
const { follow: _follow, ...renderOptions } = execution.renderOptions;
|
|
3374
3235
|
const initialConversation = await client.sessions.conversation(execution.sessionId, renderOptions);
|
|
@@ -3429,7 +3290,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3429
3290
|
0 && (module.exports = {
|
|
3430
3291
|
advanceTransientDevicePollBackoff,
|
|
3431
3292
|
collectK8sUsage,
|
|
3432
|
-
dispatchMergeCommand,
|
|
3433
3293
|
dispatchSessionRunCommand,
|
|
3434
3294
|
followR5dctlSession,
|
|
3435
3295
|
formatK8sCpu,
|
|
@@ -3441,7 +3301,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3441
3301
|
getR5dctlVersion,
|
|
3442
3302
|
getTransientDevicePollDelay,
|
|
3443
3303
|
handleAuthLogin,
|
|
3444
|
-
isMergeAttemptSettled,
|
|
3445
3304
|
main,
|
|
3446
3305
|
parseAnswerEnvRequestCommandArgs,
|
|
3447
3306
|
parseAnswerFlags,
|
|
@@ -3455,7 +3314,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3455
3314
|
parseGetEnvsArgs,
|
|
3456
3315
|
parseGlobalArgs,
|
|
3457
3316
|
parseK8sUsageArgs,
|
|
3458
|
-
parseMergeCommand,
|
|
3459
3317
|
parsePromptArgs,
|
|
3460
3318
|
parsePsHistoryArgs,
|
|
3461
3319
|
parsePsListArgs,
|
|
@@ -3479,9 +3337,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3479
3337
|
renderSessionRunStatus,
|
|
3480
3338
|
renderWorkspaceStatus,
|
|
3481
3339
|
renderWorkspaceSync,
|
|
3482
|
-
renderWorktreeMerge,
|
|
3483
3340
|
resolveCommandExecution,
|
|
3484
3341
|
runR5dctlCli,
|
|
3485
3342
|
summarizeEnvData,
|
|
3343
|
+
waitForSessionProvisioning,
|
|
3486
3344
|
writeConfig
|
|
3487
3345
|
});
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/cli.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
R5dctlClient
|
|
12
12
|
} from "@ricsam/r5d-api";
|
|
13
13
|
import { runR5dctlShell } from "./shell.mjs";
|
|
14
|
-
const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent", "large_diff_remediation"
|
|
14
|
+
const CHAT_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent", "large_diff_remediation"]);
|
|
15
15
|
const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["ask", "plan", "agent"]);
|
|
16
16
|
const MODEL_TIERS = /* @__PURE__ */ new Set(["low", "medium", "high", "max"]);
|
|
17
17
|
const R5DCTL_PACKAGE_NAME = "@ricsam/r5dctl";
|
|
@@ -63,6 +63,9 @@ const PS_HELP_TEXT = [
|
|
|
63
63
|
const K8S_HELP_TEXT = [
|
|
64
64
|
"Usage:",
|
|
65
65
|
" r5dctl k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
66
|
+
" r5dctl vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
67
|
+
" r5dctl vcluster trusted-workflow list",
|
|
68
|
+
" r5dctl vcluster trusted-workflow remove <workflow-id>",
|
|
66
69
|
"",
|
|
67
70
|
"Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
|
|
68
71
|
""
|
|
@@ -79,16 +82,6 @@ const SESSION_RUN_HELP_TEXT = [
|
|
|
79
82
|
"Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
|
|
80
83
|
""
|
|
81
84
|
].join("\n");
|
|
82
|
-
const MERGE_HELP_TEXT = [
|
|
83
|
-
"Usage:",
|
|
84
|
-
" r5dctl -p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] [--no-wait] <source-worktree>",
|
|
85
|
-
" r5dctl -p <project> merge status <attempt-id>",
|
|
86
|
-
' r5dctl -p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
87
|
-
"",
|
|
88
|
-
"Merge a managed worktree into its recorded parent worktree.",
|
|
89
|
-
"Conflict worktrees provision in the background; merge waits by default and --no-wait returns the attempt immediately.",
|
|
90
|
-
""
|
|
91
|
-
].join("\n");
|
|
92
85
|
const SHARED_HELP_ENTRIES = [
|
|
93
86
|
{ section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
|
|
94
87
|
{ section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
|
|
@@ -99,16 +92,16 @@ const SHARED_HELP_ENTRIES = [
|
|
|
99
92
|
},
|
|
100
93
|
{ section: "auth", usage: "auth api-key list", description: "List API keys for the current account." },
|
|
101
94
|
{ section: "auth", usage: "auth api-key revoke <key-id>", description: "Revoke an API key." },
|
|
102
|
-
{ section: "workspace", usage: "workspace status", description: "Inspect
|
|
95
|
+
{ section: "workspace", usage: "workspace status", description: "Inspect per-user workspace Git state and active remediation." },
|
|
103
96
|
{
|
|
104
97
|
section: "workspace",
|
|
105
98
|
usage: "workspace sync [--worker <label>]",
|
|
106
|
-
description: "
|
|
99
|
+
description: "Commit, rebase, and push a connected worker's workspace now."
|
|
107
100
|
},
|
|
108
101
|
{
|
|
109
102
|
section: "workspace",
|
|
110
103
|
usage: "workspace reset [--worker <label>] --confirm",
|
|
111
|
-
description: "Destructively discard a blocked
|
|
104
|
+
description: "Destructively discard a blocked local workspace commit and restore origin/main."
|
|
112
105
|
},
|
|
113
106
|
{ section: "projects", usage: "get projects", description: "List projects you can access." },
|
|
114
107
|
{ section: "projects", usage: "describe project <namespace/name|id>", description: "Show details for a project." },
|
|
@@ -219,6 +212,17 @@ const SHARED_HELP_ENTRIES = [
|
|
|
219
212
|
usage: "k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
220
213
|
description: "Show current and rolling CPU and memory usage for managed-vCluster workload containers."
|
|
221
214
|
},
|
|
215
|
+
{
|
|
216
|
+
section: "kubernetes",
|
|
217
|
+
usage: "vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
218
|
+
description: "Trust an exact GitHub Actions workflow path to authenticate kubectl against your managed vCluster."
|
|
219
|
+
},
|
|
220
|
+
{ section: "kubernetes", usage: "vcluster trusted-workflow list", description: "List trusted GitHub Actions workflows." },
|
|
221
|
+
{
|
|
222
|
+
section: "kubernetes",
|
|
223
|
+
usage: "vcluster trusted-workflow remove <workflow-id>",
|
|
224
|
+
description: "Revoke a trusted GitHub Actions workflow and its outstanding credentials."
|
|
225
|
+
},
|
|
222
226
|
{
|
|
223
227
|
section: "processes",
|
|
224
228
|
usage: "ps list [--worker <label>]",
|
|
@@ -234,22 +238,7 @@ const SHARED_HELP_ENTRIES = [
|
|
|
234
238
|
usage: "ps inspect <run-id>",
|
|
235
239
|
description: "Inspect process metadata and bounded stdout/stderr tails."
|
|
236
240
|
},
|
|
237
|
-
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." }
|
|
238
|
-
{
|
|
239
|
-
section: "merge",
|
|
240
|
-
usage: "-p <project> merge [--worker <label>] [--create-conflict-worktree|--launch-conflict-resolution-agent] [--no-wait] <source-worktree>",
|
|
241
|
-
description: "Merge a worktree into its recorded parent, with an explicit conflict strategy."
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
section: "merge",
|
|
245
|
-
usage: "-p <project> merge status <attempt-id>",
|
|
246
|
-
description: "Show a merge attempt's provisioning and resolution status."
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
section: "merge",
|
|
250
|
-
usage: '-p <project> merge finish --worker <label> [--summary "<summary>"] <conflict-worktree>',
|
|
251
|
-
description: "Publish and finish a manually resolved conflict worktree."
|
|
252
|
-
}
|
|
241
|
+
{ section: "processes", usage: "ps stop <run-id>", description: "Gracefully stop an active process with SIGTERM." }
|
|
253
242
|
];
|
|
254
243
|
const HELP_SECTION_ORDER = [
|
|
255
244
|
"auth",
|
|
@@ -260,8 +249,7 @@ const HELP_SECTION_ORDER = [
|
|
|
260
249
|
"sessions",
|
|
261
250
|
"kubernetes",
|
|
262
251
|
"processes",
|
|
263
|
-
"shell"
|
|
264
|
-
"merge"
|
|
252
|
+
"shell"
|
|
265
253
|
];
|
|
266
254
|
const HELP_SECTION_TITLES = {
|
|
267
255
|
auth: "Auth",
|
|
@@ -272,8 +260,7 @@ const HELP_SECTION_TITLES = {
|
|
|
272
260
|
sessions: "Sessions",
|
|
273
261
|
kubernetes: "Kubernetes",
|
|
274
262
|
processes: "Processes",
|
|
275
|
-
shell: "Shell"
|
|
276
|
-
merge: "Worktree merging"
|
|
263
|
+
shell: "Shell"
|
|
277
264
|
};
|
|
278
265
|
function getCliHelpText() {
|
|
279
266
|
const entries = [...CLI_ONLY_HELP_ENTRIES, ...SHARED_HELP_ENTRIES];
|
|
@@ -551,56 +538,6 @@ function parseSessionRunCommand(options, args) {
|
|
|
551
538
|
}
|
|
552
539
|
throw new Error(`Unknown session command: ${subcommand}`);
|
|
553
540
|
}
|
|
554
|
-
function parseMergeCommand(options, args) {
|
|
555
|
-
const project = requireValue(options.project, "--project/-p is required for `merge`");
|
|
556
|
-
if (options.branch) throw new Error("--branch/-b is not supported for `merge`; pass the source worktree explicitly");
|
|
557
|
-
if (options.session) throw new Error("--session/-s is not supported for `merge`");
|
|
558
|
-
if (args[0] === "finish") {
|
|
559
|
-
const parsed2 = parseCommandOperands(args.slice(1), /* @__PURE__ */ new Set(["--worker", "--summary"]), /* @__PURE__ */ new Map(), "merge finish");
|
|
560
|
-
const conflictWorktree = requireValue(parsed2.positionals[0], "Conflict worktree is required for `merge finish`");
|
|
561
|
-
if (parsed2.positionals.length > 1) throw new Error(`Unexpected merge finish argument: ${parsed2.positionals[1]}`);
|
|
562
|
-
return {
|
|
563
|
-
kind: "finish",
|
|
564
|
-
project,
|
|
565
|
-
conflictWorktree,
|
|
566
|
-
worker: requireWorker(parsed2.values),
|
|
567
|
-
...parsed2.values.get("--summary")?.trim() ? { summary: parsed2.values.get("--summary").trim() } : {}
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
if (args[0] === "status") {
|
|
571
|
-
const parsed2 = parseCommandOperands(args.slice(1), /* @__PURE__ */ new Set(), /* @__PURE__ */ new Map(), "merge status");
|
|
572
|
-
const attemptId = requireValue(parsed2.positionals[0], "Attempt id is required for `merge status`");
|
|
573
|
-
if (parsed2.positionals.length > 1) throw new Error(`Unexpected merge status argument: ${parsed2.positionals[1]}`);
|
|
574
|
-
return { kind: "status", project, attemptId };
|
|
575
|
-
}
|
|
576
|
-
const parsed = parseCommandOperands(
|
|
577
|
-
args,
|
|
578
|
-
/* @__PURE__ */ new Set(["--worker"]),
|
|
579
|
-
/* @__PURE__ */ new Map([
|
|
580
|
-
["--create-conflict-worktree", "--create-conflict-worktree"],
|
|
581
|
-
["--launch-conflict-resolution-agent", "--launch-conflict-resolution-agent"],
|
|
582
|
-
["--no-wait", "--no-wait"]
|
|
583
|
-
]),
|
|
584
|
-
"merge"
|
|
585
|
-
);
|
|
586
|
-
const sourceWorktree = requireValue(parsed.positionals[0], "Source worktree is required for `merge`");
|
|
587
|
-
if (parsed.positionals.length > 1) throw new Error(`Unexpected merge argument: ${parsed.positionals[1]}`);
|
|
588
|
-
const createWorktree = parsed.booleans.has("--create-conflict-worktree");
|
|
589
|
-
const launchAgent = parsed.booleans.has("--launch-conflict-resolution-agent");
|
|
590
|
-
if (createWorktree && launchAgent) {
|
|
591
|
-
throw new Error("Use only one of --create-conflict-worktree or --launch-conflict-resolution-agent");
|
|
592
|
-
}
|
|
593
|
-
const conflictMode = createWorktree ? "worktree" : launchAgent ? "agent" : "none";
|
|
594
|
-
const worker = parsed.values.get("--worker")?.trim();
|
|
595
|
-
return {
|
|
596
|
-
kind: "merge",
|
|
597
|
-
project,
|
|
598
|
-
sourceWorktree,
|
|
599
|
-
conflictMode,
|
|
600
|
-
...worker ? { worker } : {},
|
|
601
|
-
...parsed.booleans.has("--no-wait") ? { noWait: true } : {}
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
541
|
function assertAuthLoginArgs(args) {
|
|
605
542
|
const valueFlags = /* @__PURE__ */ new Set(["--device-name", "--worker-label"]);
|
|
606
543
|
const booleanFlags = /* @__PURE__ */ new Set(["--no-open", "--no-qr"]);
|
|
@@ -1427,16 +1364,15 @@ function renderWorkspaceStatus(status) {
|
|
|
1427
1364
|
`Workspace HEAD: ${status.head ?? "(not initialized)"}`,
|
|
1428
1365
|
`Latest sync: ${status.latestSync ? `${status.latestSync.outcome} on ${status.latestSync.workerLabel}` : "(none)"}`,
|
|
1429
1366
|
`Remediation: ${status.incident ? `${status.incident.kind} ${status.incident.status} (${status.incident.id}) on ${status.incident.originWorkerLabel}` : "none"}`,
|
|
1430
|
-
`Materialization: ${status.materialization ? `${status.materialization.state}; ${status.materialization.materializedHead ?? "(none)"} -> ${status.materialization.desiredHead ?? "(none)"}; lag ${status.materialization.lagMs}ms` : "not initialized"}`,
|
|
1431
1367
|
...status.workers.map(
|
|
1432
|
-
(worker) => `Worker ${worker.workerLabel}: ${worker.
|
|
1368
|
+
(worker) => `Worker ${worker.workerLabel}: ${worker.configured ? "configured" : "configuring"}; head ${worker.localHead ?? "(none)"}; pending checkouts ${worker.pendingCheckouts.length}`
|
|
1433
1369
|
)
|
|
1434
1370
|
];
|
|
1435
1371
|
return `${lines.join("\n")}
|
|
1436
1372
|
`;
|
|
1437
1373
|
}
|
|
1438
1374
|
function renderWorkspaceSync(result) {
|
|
1439
|
-
const head = result.publishedHead ?? result.
|
|
1375
|
+
const head = result.publishedHead ?? result.localHead ?? result.startingHead ?? "(none)";
|
|
1440
1376
|
const discarded = result.discardedPaths.length > 0 ? `
|
|
1441
1377
|
Discarded paths: ${result.discardedPaths.join(", ")}` : "";
|
|
1442
1378
|
const error = result.error ? `
|
|
@@ -1812,30 +1748,6 @@ function formatStatusValue(value) {
|
|
|
1812
1748
|
}
|
|
1813
1749
|
return value === void 0 || value === null ? void 0 : String(value);
|
|
1814
1750
|
}
|
|
1815
|
-
function appendMergeDetails(lines, merge) {
|
|
1816
|
-
const mergeStatus = formatStatusValue(merge.status);
|
|
1817
|
-
lines.push(`Merge: ${mergeStatus ?? "pending"}`);
|
|
1818
|
-
for (const [label, key] of [
|
|
1819
|
-
["Attempt", "attemptId"],
|
|
1820
|
-
["Source worktree", "sourceWorktree"],
|
|
1821
|
-
["Target worktree", "targetWorktree"],
|
|
1822
|
-
["Commit", "commitHash"],
|
|
1823
|
-
["Resolver agent", "resolverSessionId"],
|
|
1824
|
-
["Conflict worktree", "conflictWorktree"],
|
|
1825
|
-
["Resolver worktree", "folderPath"]
|
|
1826
|
-
]) {
|
|
1827
|
-
const value = responseString(merge, key);
|
|
1828
|
-
if (value) lines.push(`${label}: ${value}`);
|
|
1829
|
-
}
|
|
1830
|
-
const conflictedFiles = Array.isArray(merge.conflictedFiles) ? merge.conflictedFiles.filter((file) => typeof file === "string") : [];
|
|
1831
|
-
if (conflictedFiles.length > 0) {
|
|
1832
|
-
lines.push("", "Conflicts:", ...conflictedFiles.map((file) => `- ${file}`));
|
|
1833
|
-
}
|
|
1834
|
-
const message = responseString(merge, "message");
|
|
1835
|
-
if (message) lines.push("", message);
|
|
1836
|
-
const failureReason = responseString(merge, "failureReason");
|
|
1837
|
-
if (failureReason) lines.push(`Failure: ${failureReason}`);
|
|
1838
|
-
}
|
|
1839
1751
|
function renderSessionRunStart(session) {
|
|
1840
1752
|
const lines = [
|
|
1841
1753
|
`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`,
|
|
@@ -1884,28 +1796,6 @@ function renderSessionRunStatus(session) {
|
|
|
1884
1796
|
return `${lines.join("\n")}
|
|
1885
1797
|
`;
|
|
1886
1798
|
}
|
|
1887
|
-
function renderWorktreeMerge(result, input) {
|
|
1888
|
-
const lines = [];
|
|
1889
|
-
appendMergeDetails(lines, result);
|
|
1890
|
-
const status = responseString(result, "status");
|
|
1891
|
-
const conflictWorktree = responseString(result, "conflictWorktree");
|
|
1892
|
-
const resolverSessionId = responseString(result, "resolverSessionId");
|
|
1893
|
-
const attemptId = responseString(result, "attemptId");
|
|
1894
|
-
const worker = responseString(result, "worker") ?? input.worker;
|
|
1895
|
-
if (resolverSessionId) lines.push("", `Resolver session: ${resolverSessionId}`, ` r5dctl session stop ${resolverSessionId}`);
|
|
1896
|
-
if (conflictWorktree && worker && ["conflicts", "conflict_worktree_created", "resolving"].includes(status ?? "")) {
|
|
1897
|
-
lines.push(
|
|
1898
|
-
"",
|
|
1899
|
-
"Finish after resolving and reviewing the conflict worktree:",
|
|
1900
|
-
` r5dctl -p ${input.project} merge finish --worker ${worker} ${conflictWorktree} --summary "<summary>"`
|
|
1901
|
-
);
|
|
1902
|
-
}
|
|
1903
|
-
if (attemptId && (status === "provisioning" || status === "resolving" && result.resolverAgentRunning === false)) {
|
|
1904
|
-
lines.push("", "Check progress:", ` r5dctl -p ${input.project} merge status ${attemptId}`);
|
|
1905
|
-
}
|
|
1906
|
-
return `${lines.join("\n")}
|
|
1907
|
-
`;
|
|
1908
|
-
}
|
|
1909
1799
|
async function dispatchSessionRunCommand(client, command) {
|
|
1910
1800
|
if (command.kind === "start") {
|
|
1911
1801
|
const commonInput = {
|
|
@@ -1942,70 +1832,16 @@ async function dispatchSessionRunCommand(client, command) {
|
|
|
1942
1832
|
const data = await client.sessions.stop(command.sessionId);
|
|
1943
1833
|
return { data, human: renderSessionRunStatus(data) };
|
|
1944
1834
|
}
|
|
1945
|
-
|
|
1946
|
-
const
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
}
|
|
1954
|
-
async function pollMergeAttemptUntilSettled(client, project, attemptId, initial, options) {
|
|
1955
|
-
const intervalMs = options.pollIntervalMs ?? MERGE_POLL_INTERVAL_MS;
|
|
1956
|
-
const timeoutMs = options.pollTimeoutMs ?? MERGE_POLL_TIMEOUT_MS;
|
|
1957
|
-
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
1958
|
-
const onProgress = options.onProgress ?? (() => {
|
|
1959
|
-
});
|
|
1960
|
-
let latest = initial;
|
|
1961
|
-
let worktreeReadyReported = latest.worktreeReady === true;
|
|
1962
|
-
let consecutiveFailures = 0;
|
|
1963
|
-
let waitedMs = 0;
|
|
1964
|
-
while (!isMergeAttemptSettled(latest)) {
|
|
1965
|
-
if (waitedMs >= timeoutMs) {
|
|
1966
|
-
onProgress(`Still waiting after ${Math.round(timeoutMs / 6e4)} minutes; check later with: r5dctl -p ${project} merge status ${attemptId}`);
|
|
1967
|
-
return latest;
|
|
1968
|
-
}
|
|
1969
|
-
await sleep2(intervalMs);
|
|
1970
|
-
waitedMs += intervalMs;
|
|
1971
|
-
try {
|
|
1972
|
-
latest = await client.projects.worktrees.mergeAttempt(project, attemptId);
|
|
1973
|
-
consecutiveFailures = 0;
|
|
1974
|
-
} catch (error) {
|
|
1975
|
-
consecutiveFailures += 1;
|
|
1976
|
-
if (consecutiveFailures >= MERGE_POLL_MAX_CONSECUTIVE_FAILURES) throw error;
|
|
1977
|
-
continue;
|
|
1978
|
-
}
|
|
1979
|
-
if (!worktreeReadyReported && latest.worktreeReady === true) {
|
|
1980
|
-
worktreeReadyReported = true;
|
|
1981
|
-
onProgress("Conflict worktree ready.");
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
return latest;
|
|
1985
|
-
}
|
|
1986
|
-
async function dispatchMergeCommand(client, command, pollOptions = {}) {
|
|
1987
|
-
if (command.kind === "merge") {
|
|
1988
|
-
const data2 = await client.projects.worktrees.merge(command.project, command.sourceWorktree, {
|
|
1989
|
-
conflictMode: command.conflictMode,
|
|
1990
|
-
...command.worker ? { worker: command.worker } : {}
|
|
1991
|
-
});
|
|
1992
|
-
const attemptId = responseString(data2, "attemptId");
|
|
1993
|
-
if (command.noWait || !attemptId || isMergeAttemptSettled(data2)) {
|
|
1994
|
-
return { data: data2, human: renderWorktreeMerge(data2, command) };
|
|
1995
|
-
}
|
|
1996
|
-
pollOptions.onProgress?.(`Provisioning the conflict worktree (attempt ${attemptId})...`);
|
|
1997
|
-
const settled = await pollMergeAttemptUntilSettled(client, command.project, attemptId, data2, pollOptions);
|
|
1998
|
-
return { data: settled, human: renderWorktreeMerge(settled, command) };
|
|
1999
|
-
}
|
|
2000
|
-
if (command.kind === "status") {
|
|
2001
|
-
const data2 = await client.projects.worktrees.mergeAttempt(command.project, command.attemptId);
|
|
2002
|
-
return { data: data2, human: renderWorktreeMerge(data2, { project: command.project }) };
|
|
1835
|
+
async function waitForSessionProvisioning(client, initial, options = {}) {
|
|
1836
|
+
const sessionId = requireValue(responseString(initial, "sessionId"), "Session start did not return a session id");
|
|
1837
|
+
const wait = options.wait ?? sleep;
|
|
1838
|
+
const intervalMs = options.intervalMs ?? 500;
|
|
1839
|
+
let current = initial;
|
|
1840
|
+
while (formatStatusValue(current.status) === "provisioning") {
|
|
1841
|
+
await wait(intervalMs);
|
|
1842
|
+
current = await client.sessions.status(sessionId);
|
|
2003
1843
|
}
|
|
2004
|
-
|
|
2005
|
-
worker: command.worker,
|
|
2006
|
-
...command.summary ? { summary: command.summary } : {}
|
|
2007
|
-
});
|
|
2008
|
-
return { data, human: renderWorktreeMerge(data, command) };
|
|
1844
|
+
return current;
|
|
2009
1845
|
}
|
|
2010
1846
|
function parseProjectUpdateArgs(args) {
|
|
2011
1847
|
const input = {};
|
|
@@ -2556,6 +2392,40 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
2556
2392
|
write(report, renderK8sUsageReport(report));
|
|
2557
2393
|
return;
|
|
2558
2394
|
}
|
|
2395
|
+
if (first === "vcluster" && second === "trusted-workflow") {
|
|
2396
|
+
const subcommand = requireValue(args[2], "Missing trusted-workflow command");
|
|
2397
|
+
if (subcommand === "add") {
|
|
2398
|
+
const parsed = parseCommandOperands(args.slice(3), /* @__PURE__ */ new Set(["--repo", "--workflow"]), /* @__PURE__ */ new Map(), "trusted-workflow add");
|
|
2399
|
+
if (parsed.positionals.length > 0) throw new Error(`Unexpected trusted-workflow add value: ${parsed.positionals[0]}`);
|
|
2400
|
+
const workflow = await client.k8s.trustedWorkflows.add({
|
|
2401
|
+
repository: requireValue(parsed.values.get("--repo"), "--repo <owner/repository> is required"),
|
|
2402
|
+
workflowPath: requireValue(parsed.values.get("--workflow"), "--workflow <path> is required")
|
|
2403
|
+
});
|
|
2404
|
+
write(workflow, "Trusted GitHub workflow configured successfully.\n");
|
|
2405
|
+
return;
|
|
2406
|
+
}
|
|
2407
|
+
if (subcommand === "list") {
|
|
2408
|
+
if (args.length > 3) throw new Error(`Unexpected trusted-workflow list value: ${args[3]}`);
|
|
2409
|
+
const workflows = await client.k8s.trustedWorkflows.list();
|
|
2410
|
+
write(
|
|
2411
|
+
workflows,
|
|
2412
|
+
workflows.length === 0 ? "No trusted GitHub workflows.\n" : `${workflows.map(
|
|
2413
|
+
(workflow) => `${workflow.id} ${workflow.repository} ${workflow.workflowPath} ${workflow.revokedAt ? "revoked" : "active"}`
|
|
2414
|
+
).join("\n")}
|
|
2415
|
+
`
|
|
2416
|
+
);
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (subcommand === "remove") {
|
|
2420
|
+
const workflowId = requireValue(args[3], "Missing trusted workflow id");
|
|
2421
|
+
if (args.length > 4) throw new Error(`Unexpected trusted-workflow remove value: ${args[4]}`);
|
|
2422
|
+
const result = await client.k8s.trustedWorkflows.revoke(workflowId);
|
|
2423
|
+
if (!result.success) throw new Error("Trusted GitHub workflow was not found or was already revoked");
|
|
2424
|
+
write(result, "Trusted GitHub workflow revoked.\n");
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
throw new Error(`Unknown trusted-workflow command: ${subcommand}`);
|
|
2428
|
+
}
|
|
2559
2429
|
if (first === "ps" && second === "list") {
|
|
2560
2430
|
const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
|
|
2561
2431
|
write(processes, renderProcessList(processes));
|
|
@@ -2789,12 +2659,6 @@ function resolveCommandExecution(options, rest) {
|
|
|
2789
2659
|
text: SESSION_RUN_HELP_TEXT
|
|
2790
2660
|
};
|
|
2791
2661
|
}
|
|
2792
|
-
if (command === "merge" && commandArgs.length === 0) {
|
|
2793
|
-
return {
|
|
2794
|
-
kind: "cli-help",
|
|
2795
|
-
text: MERGE_HELP_TEXT
|
|
2796
|
-
};
|
|
2797
|
-
}
|
|
2798
2662
|
if (trailingHelp) {
|
|
2799
2663
|
const cliOnlyHelp = findCliOnlyCommandHelp(normalizedRest);
|
|
2800
2664
|
if (cliOnlyHelp) {
|
|
@@ -2925,12 +2789,23 @@ function resolveCommandExecution(options, rest) {
|
|
|
2925
2789
|
}
|
|
2926
2790
|
if (command === "k8s") {
|
|
2927
2791
|
const subcommand = commandArgs[0];
|
|
2928
|
-
if (subcommand !== "usage") throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
|
|
2929
2792
|
if (options.project || options.branch || options.session) {
|
|
2930
|
-
throw new Error("Project, branch, and session scopes are not supported for `k8s
|
|
2793
|
+
throw new Error("Project, branch, and session scopes are not supported for `k8s`");
|
|
2794
|
+
}
|
|
2795
|
+
if (subcommand === "usage") {
|
|
2796
|
+
parseK8sUsageArgs(commandArgs.slice(1));
|
|
2797
|
+
return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
|
|
2798
|
+
}
|
|
2799
|
+
throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
|
|
2800
|
+
}
|
|
2801
|
+
if (command === "vcluster") {
|
|
2802
|
+
if (options.project || options.branch || options.session) {
|
|
2803
|
+
throw new Error("Project, branch, and session scopes are not supported for `vcluster`");
|
|
2804
|
+
}
|
|
2805
|
+
if (commandArgs[0] !== "trusted-workflow") {
|
|
2806
|
+
throw new Error(`Unknown vcluster command: ${commandArgs[0] ?? ""}`.trim());
|
|
2931
2807
|
}
|
|
2932
|
-
|
|
2933
|
-
return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
|
|
2808
|
+
return { kind: "plugin", pluginArgs: ["vcluster", "trusted-workflow", ...commandArgs.slice(1)] };
|
|
2934
2809
|
}
|
|
2935
2810
|
if (command === "get") {
|
|
2936
2811
|
const target = commandArgs[0];
|
|
@@ -3182,9 +3057,6 @@ function resolveCommandExecution(options, rest) {
|
|
|
3182
3057
|
if (command === "session") {
|
|
3183
3058
|
return { kind: "session-run", command: parseSessionRunCommand(options, commandArgs) };
|
|
3184
3059
|
}
|
|
3185
|
-
if (command === "merge") {
|
|
3186
|
-
return { kind: "merge", command: parseMergeCommand(options, commandArgs) };
|
|
3187
|
-
}
|
|
3188
3060
|
if (command === "answer-questions") {
|
|
3189
3061
|
const sessionId = options.session;
|
|
3190
3062
|
if (!sessionId) {
|
|
@@ -3246,7 +3118,7 @@ async function runCommand(argv) {
|
|
|
3246
3118
|
if (execution.kind === "session-run") {
|
|
3247
3119
|
if (!options.json) {
|
|
3248
3120
|
if (execution.command.kind === "start") {
|
|
3249
|
-
process.stderr.write("Starting session
|
|
3121
|
+
process.stderr.write("Starting session...\n");
|
|
3250
3122
|
} else if (execution.command.kind === "stop") {
|
|
3251
3123
|
process.stderr.write(`Stopping session ${execution.command.sessionId}...
|
|
3252
3124
|
`);
|
|
@@ -3261,28 +3133,20 @@ async function runCommand(argv) {
|
|
|
3261
3133
|
if (options.json) process.stdout.write(`${JSON.stringify({ type: "session_started", data: result.data })}
|
|
3262
3134
|
`);
|
|
3263
3135
|
else process.stderr.write(result.human);
|
|
3136
|
+
const ready = await waitForSessionProvisioning(client, result.data);
|
|
3137
|
+
const readyStatus = formatStatusValue(ready.status);
|
|
3138
|
+
if (readyStatus === "failed" || readyStatus === "stopped") {
|
|
3139
|
+
if (options.json) process.stdout.write(`${JSON.stringify({ type: "session_status", data: ready })}
|
|
3140
|
+
`);
|
|
3141
|
+
else process.stderr.write(renderSessionRunStatus(ready));
|
|
3142
|
+
return readyStatus === "failed" ? 1 : 0;
|
|
3143
|
+
}
|
|
3264
3144
|
return await followWithInterrupt(client, sessionId, {
|
|
3265
3145
|
json: options.json,
|
|
3266
3146
|
includeTools: execution.command.includeTools,
|
|
3267
3147
|
printInitialTranscript: false
|
|
3268
3148
|
});
|
|
3269
3149
|
}
|
|
3270
|
-
if (execution.kind === "merge") {
|
|
3271
|
-
if (!options.json) {
|
|
3272
|
-
if (execution.command.kind === "merge") {
|
|
3273
|
-
process.stderr.write("Merging (publishing the workspace and computing the merge)...\n");
|
|
3274
|
-
} else if (execution.command.kind === "finish") {
|
|
3275
|
-
process.stderr.write(`Finalizing ${execution.command.conflictWorktree} (publishing the resolver workspace)...
|
|
3276
|
-
`);
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
const result = await dispatchMergeCommand(client, execution.command, {
|
|
3280
|
-
...options.json ? {} : { onProgress: (line) => process.stderr.write(`${line}
|
|
3281
|
-
`) }
|
|
3282
|
-
});
|
|
3283
|
-
writeDataOutput(options.json, result.data, result.human);
|
|
3284
|
-
return 0;
|
|
3285
|
-
}
|
|
3286
3150
|
if (execution.kind === "conversation-follow") {
|
|
3287
3151
|
const { follow: _follow, ...renderOptions } = execution.renderOptions;
|
|
3288
3152
|
const initialConversation = await client.sessions.conversation(execution.sessionId, renderOptions);
|
|
@@ -3342,7 +3206,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
3342
3206
|
export {
|
|
3343
3207
|
advanceTransientDevicePollBackoff,
|
|
3344
3208
|
collectK8sUsage,
|
|
3345
|
-
dispatchMergeCommand,
|
|
3346
3209
|
dispatchSessionRunCommand,
|
|
3347
3210
|
followR5dctlSession,
|
|
3348
3211
|
formatK8sCpu,
|
|
@@ -3354,7 +3217,6 @@ export {
|
|
|
3354
3217
|
getR5dctlVersion,
|
|
3355
3218
|
getTransientDevicePollDelay,
|
|
3356
3219
|
handleAuthLogin,
|
|
3357
|
-
isMergeAttemptSettled,
|
|
3358
3220
|
main,
|
|
3359
3221
|
parseAnswerEnvRequestCommandArgs,
|
|
3360
3222
|
parseAnswerFlags,
|
|
@@ -3368,7 +3230,6 @@ export {
|
|
|
3368
3230
|
parseGetEnvsArgs,
|
|
3369
3231
|
parseGlobalArgs,
|
|
3370
3232
|
parseK8sUsageArgs,
|
|
3371
|
-
parseMergeCommand,
|
|
3372
3233
|
parsePromptArgs,
|
|
3373
3234
|
parsePsHistoryArgs,
|
|
3374
3235
|
parsePsListArgs,
|
|
@@ -3392,9 +3253,9 @@ export {
|
|
|
3392
3253
|
renderSessionRunStatus,
|
|
3393
3254
|
renderWorkspaceStatus,
|
|
3394
3255
|
renderWorkspaceSync,
|
|
3395
|
-
renderWorktreeMerge,
|
|
3396
3256
|
resolveCommandExecution,
|
|
3397
3257
|
runR5dctlCli,
|
|
3398
3258
|
summarizeEnvData,
|
|
3259
|
+
waitForSessionProvisioning,
|
|
3399
3260
|
writeConfig
|
|
3400
3261
|
};
|
package/dist/mjs/package.json
CHANGED
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlK8sResourceHistory, type R5dctlK8sUsage, type R5dctlK8sWorkload, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessRun, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorktreeSource, type
|
|
1
|
+
import { R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlK8sResourceHistory, type R5dctlK8sUsage, type R5dctlK8sWorkload, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessRun, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorktreeSource, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
|
|
2
2
|
export type GlobalOptions = {
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
json: boolean;
|
|
@@ -32,9 +32,6 @@ type CommandExecutionPlan = {
|
|
|
32
32
|
} | {
|
|
33
33
|
kind: "session-run";
|
|
34
34
|
command: R5dctlSessionRunCommand;
|
|
35
|
-
} | {
|
|
36
|
-
kind: "merge";
|
|
37
|
-
command: R5dctlMergeCommand;
|
|
38
35
|
} | {
|
|
39
36
|
kind: "conversation-follow";
|
|
40
37
|
sessionId: string;
|
|
@@ -79,26 +76,7 @@ export type R5dctlSessionRunCommand = R5dctlSessionStartCommand | {
|
|
|
79
76
|
kind: "stop";
|
|
80
77
|
sessionId: string;
|
|
81
78
|
};
|
|
82
|
-
export type R5dctlMergeCommand = {
|
|
83
|
-
kind: "merge";
|
|
84
|
-
project: string;
|
|
85
|
-
sourceWorktree: string;
|
|
86
|
-
worker?: string;
|
|
87
|
-
conflictMode: R5dctlMergeConflictMode;
|
|
88
|
-
noWait?: boolean;
|
|
89
|
-
} | {
|
|
90
|
-
kind: "status";
|
|
91
|
-
project: string;
|
|
92
|
-
attemptId: string;
|
|
93
|
-
} | {
|
|
94
|
-
kind: "finish";
|
|
95
|
-
project: string;
|
|
96
|
-
conflictWorktree: string;
|
|
97
|
-
worker: string;
|
|
98
|
-
summary?: string;
|
|
99
|
-
};
|
|
100
79
|
export declare function parseSessionRunCommand(options: Pick<GlobalOptions, "project" | "branch" | "session">, args: string[]): R5dctlSessionRunCommand;
|
|
101
|
-
export declare function parseMergeCommand(options: Pick<GlobalOptions, "project" | "branch" | "session">, args: string[]): R5dctlMergeCommand;
|
|
102
80
|
export declare function parseGlobalArgs(argv: string[]): {
|
|
103
81
|
options: GlobalOptions;
|
|
104
82
|
rest: string[];
|
|
@@ -224,18 +202,6 @@ export type R5dctlCommandResponse = {
|
|
|
224
202
|
error?: string;
|
|
225
203
|
summary?: string;
|
|
226
204
|
diffSummary?: string;
|
|
227
|
-
attemptId?: string;
|
|
228
|
-
sourceWorktree?: string;
|
|
229
|
-
targetWorktree?: string;
|
|
230
|
-
commitHash?: string;
|
|
231
|
-
resolverSessionId?: string;
|
|
232
|
-
conflictWorktree?: string;
|
|
233
|
-
folderPath?: string;
|
|
234
|
-
conflictedFiles?: string[];
|
|
235
|
-
failureReason?: string;
|
|
236
|
-
message?: string;
|
|
237
|
-
worktreeReady?: boolean;
|
|
238
|
-
resolverAgentRunning?: boolean;
|
|
239
205
|
};
|
|
240
206
|
export type R5dctlSessionRunClient = {
|
|
241
207
|
projects: {
|
|
@@ -255,47 +221,17 @@ export type R5dctlSessionRunClient = {
|
|
|
255
221
|
stop: (sessionId: string) => Promise<R5dctlCommandResponse>;
|
|
256
222
|
};
|
|
257
223
|
};
|
|
258
|
-
export type R5dctlMergeClient = {
|
|
259
|
-
projects: {
|
|
260
|
-
worktrees: {
|
|
261
|
-
merge: (projectRef: string, sourceWorktree: string, input: {
|
|
262
|
-
worker?: string;
|
|
263
|
-
conflictMode: R5dctlMergeConflictMode;
|
|
264
|
-
}) => Promise<R5dctlCommandResponse>;
|
|
265
|
-
finishMerge: (projectRef: string, conflictWorktree: string, input: {
|
|
266
|
-
worker: string;
|
|
267
|
-
summary?: string;
|
|
268
|
-
}) => Promise<R5dctlCommandResponse>;
|
|
269
|
-
mergeAttempt: (projectRef: string, attemptId: string) => Promise<R5dctlCommandResponse>;
|
|
270
|
-
};
|
|
271
|
-
};
|
|
272
|
-
};
|
|
273
224
|
export declare function renderSessionRunStart(session: R5dctlCommandResponse): string;
|
|
274
225
|
export declare function renderSessionRunStatus(session: R5dctlCommandResponse): string;
|
|
275
|
-
export declare function renderWorktreeMerge(result: R5dctlCommandResponse, input: {
|
|
276
|
-
project: string;
|
|
277
|
-
worker?: string;
|
|
278
|
-
}): string;
|
|
279
226
|
export type R5dctlCommandDispatchResult = {
|
|
280
227
|
data: R5dctlCommandResponse;
|
|
281
228
|
human: string;
|
|
282
229
|
};
|
|
283
230
|
export declare function dispatchSessionRunCommand(client: R5dctlSessionRunClient, command: R5dctlSessionRunCommand): Promise<R5dctlCommandDispatchResult>;
|
|
284
|
-
export
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
sleep?: (ms: number) => Promise<void>;
|
|
289
|
-
};
|
|
290
|
-
/**
|
|
291
|
-
* Whether a merge attempt response is settled for the CLI: terminal, waiting
|
|
292
|
-
* on the user (a ready manual conflict worktree), or actively resolving with
|
|
293
|
-
* its agent running. Anything else is still provisioning or starting. A
|
|
294
|
-
* "resolving" response without the resolverAgentRunning field comes from a
|
|
295
|
-
* server that starts the agent in-request, so it counts as settled.
|
|
296
|
-
*/
|
|
297
|
-
export declare function isMergeAttemptSettled(response: R5dctlCommandResponse): boolean;
|
|
298
|
-
export declare function dispatchMergeCommand(client: R5dctlMergeClient, command: R5dctlMergeCommand, pollOptions?: R5dctlMergePollOptions): Promise<R5dctlCommandDispatchResult>;
|
|
231
|
+
export declare function waitForSessionProvisioning(client: Pick<R5dctlSessionRunClient, "sessions">, initial: R5dctlCommandResponse, options?: {
|
|
232
|
+
intervalMs?: number;
|
|
233
|
+
wait?: (milliseconds: number) => Promise<unknown>;
|
|
234
|
+
}): Promise<R5dctlCommandResponse>;
|
|
299
235
|
export declare function parseConversationRenderArgs(args: string[]): ConversationRenderOptions;
|
|
300
236
|
export declare function renderConversationResponse(read: R5dctlConversationResponse): string;
|
|
301
237
|
type R5dctlFollowClient = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5dctl",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.75",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/cjs/cli.cjs",
|
|
6
6
|
"module": "./dist/mjs/cli.mjs",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"r5dctl": "dist/cjs/main.cjs"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ricsam/r5d-api": "^0.0.
|
|
29
|
+
"@ricsam/r5d-api": "^0.0.75",
|
|
30
30
|
"dotenv": "^17",
|
|
31
31
|
"qrcode": "^1.5.4",
|
|
32
32
|
"ws": "^8.18.3"
|