@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 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
- `session start` creates an ordinary agent-mode chat on an explicitly selected existing worktree, or creates a named worktree from an explicit `worktree:<branch>` source. It requires project, worker, model, and prompt and never infers them from the current shell. Starts and prompts return after durable enqueue while execution continues asynchronously. 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 publication finishes asynchronously behind the response, and `merge` republishes at its own boundary before reading the worktree.
115
-
116
- Add `-f`/`--follow`/`--watch` to `session start` to 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.
117
-
118
- `merge` merges a managed source worktree into its recorded parent. By default conflicts are reported without creating more work. `--create-conflict-worktree` prepares a manual conflict worktree, while `--launch-conflict-resolution-agent` starts the dedicated merge-conflict resolver; those strategies are mutually exclusive. A worker-bound shell credential supplies its own worker automatically; otherwise pass `--worker`. Finish a reviewed manual resolution with `merge finish`.
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
- Merging is lockless and optimistic: nothing blocks agents from continuing to work on the involved branches. Every merge pins the branch tips it previewed and publishes through an atomic compare-and-swap, and a concurrent publication simply retries. When a resolver agent finishes after the target advanced, the platform automatically continues the merge publishing directly if the refreshed merge is conflict-free, or launching a fresh resolver attempt (whose prompt references the superseded session's resolution) repeating until one resolution publishes. Superseded attempts surface the follow-up attempt id in `merge status`. Only manual conflict worktrees stop at `stale`: re-run `merge` to start a fresh resolution.
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
- The server answers merge requests as soon as the result is durably recorded; the slow per-member workspace propagation always completes asynchronously behind the response. For a clean merge that means the merged commit is already published when the command returns. For a conflict strategy the server responds with the merge attempt in a `provisioning` state and prepares the conflict worktree (and starts the resolver agent) in the background; the CLI polls the attempt until the worktree is ready — and, for the agent strategy, until the resolver agent is running — before printing the final result. Pass `--no-wait` to skip the wait and get the attempt id immediately, and use `merge status <attempt-id>` at any time to re-check a provisioning, failed, or completed attempt (a provisioning failure surfaces there as a `failed` attempt with its reason). Ctrl-C during the wait only detaches the CLI; provisioning continues server-side.
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", "merge_conflict_resolution"]);
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 the per-user canonical workspace and active remediation." },
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: "Publish a connected worker into the canonical workspace now."
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 remediation candidate and restore canonical state."
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.ready ? "ready" : "bootstrapping"}; publication ${worker.publicationState}; head ${worker.localHead ?? "(none)"}; desired ${worker.desiredCanonicalHead ?? "(none)"}; generations ${worker.publishedGeneration}/${worker.dirtyGeneration}; pending checkouts ${worker.pendingCheckouts.length}; CAS retries ${worker.casRetries}; fan-out lag ${worker.fanoutLagMs ?? "unknown"}ms`
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.candidateHead ?? result.startingHead ?? "(none)";
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
- const MERGE_POLL_INTERVAL_MS = 2e3;
2032
- const MERGE_POLL_TIMEOUT_MS = 20 * 6e4;
2033
- const MERGE_POLL_MAX_CONSECUTIVE_FAILURES = 5;
2034
- function isMergeAttemptSettled(response) {
2035
- const status = responseString(response, "status");
2036
- if (!status) return true;
2037
- if (status === "resolving") return response.resolverAgentRunning !== false;
2038
- return status !== "provisioning";
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
- const data = await client.projects.worktrees.finishMerge(command.project, command.conflictWorktree, {
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 usage`");
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
- parseK8sUsageArgs(commandArgs.slice(1));
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 (publishing the workspace and provisioning the worktree)...\n");
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
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.73",
3
+ "version": "0.0.75",
4
4
  "type": "commonjs"
5
5
  }
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", "merge_conflict_resolution"]);
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 the per-user canonical workspace and active remediation." },
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: "Publish a connected worker into the canonical workspace now."
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 remediation candidate and restore canonical state."
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.ready ? "ready" : "bootstrapping"}; publication ${worker.publicationState}; head ${worker.localHead ?? "(none)"}; desired ${worker.desiredCanonicalHead ?? "(none)"}; generations ${worker.publishedGeneration}/${worker.dirtyGeneration}; pending checkouts ${worker.pendingCheckouts.length}; CAS retries ${worker.casRetries}; fan-out lag ${worker.fanoutLagMs ?? "unknown"}ms`
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.candidateHead ?? result.startingHead ?? "(none)";
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
- const MERGE_POLL_INTERVAL_MS = 2e3;
1946
- const MERGE_POLL_TIMEOUT_MS = 20 * 6e4;
1947
- const MERGE_POLL_MAX_CONSECUTIVE_FAILURES = 5;
1948
- function isMergeAttemptSettled(response) {
1949
- const status = responseString(response, "status");
1950
- if (!status) return true;
1951
- if (status === "resolving") return response.resolverAgentRunning !== false;
1952
- return status !== "provisioning";
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
- const data = await client.projects.worktrees.finishMerge(command.project, command.conflictWorktree, {
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 usage`");
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
- parseK8sUsageArgs(commandArgs.slice(1));
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 (publishing the workspace and provisioning the worktree)...\n");
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
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.73",
3
+ "version": "0.0.75",
4
4
  "type": "module"
5
5
  }
@@ -1,4 +1,4 @@
1
- import { R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlK8sResourceHistory, type R5dctlK8sUsage, type R5dctlK8sWorkload, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessRun, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorktreeSource, type R5dctlMergeConflictMode, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
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 type R5dctlMergePollOptions = {
285
- onProgress?: (line: string) => void;
286
- pollIntervalMs?: number;
287
- pollTimeoutMs?: number;
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.73",
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.73",
29
+ "@ricsam/r5d-api": "^0.0.75",
30
30
  "dotenv": "^17",
31
31
  "qrcode": "^1.5.4",
32
32
  "ws": "^8.18.3"