@ricsam/r5dctl 0.0.126 → 0.0.128

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/dist/mjs/cli.mjs CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  R5dctlClient
12
12
  } from "@ricsam/r5d-api";
13
13
  import { runR5dctlShell } from "./shell.mjs";
14
+ import { ACCOUNT_HELP_ENTRIES, executeAccountCommand, getAccountHelp, parseAccountCommand } from "./account.mjs";
14
15
  const CHAT_MODES = /* @__PURE__ */ new Set(["plan", "agent", "large_diff_remediation"]);
15
16
  const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["plan", "agent"]);
16
17
  const R5DCTL_PROMPT_DELIVERIES = /* @__PURE__ */ new Set(["queue", "steer", "force"]);
@@ -58,20 +59,10 @@ const PS_HELP_TEXT = [
58
59
  " r5dctl ps inspect <run-id>",
59
60
  " r5dctl ps stop <run-id>",
60
61
  "",
61
- "List, inspect, or gracefully stop r5d-managed worker processes.",
62
+ "List, inspect, or gracefully stop agent processes on workers.",
62
63
  "Use -p/--project, -b/--branch, or -s/--session to filter list and history.",
63
64
  ""
64
65
  ].join("\n");
65
- const K8S_HELP_TEXT = [
66
- "Usage:",
67
- " r5dctl k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
68
- " r5dctl vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
69
- " r5dctl vcluster trusted-workflow list",
70
- " r5dctl vcluster trusted-workflow remove <workflow-id>",
71
- "",
72
- "Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
73
- ""
74
- ].join("\n");
75
66
  const SESSION_RUN_HELP_TEXT = [
76
67
  "Usage:",
77
68
  ' r5dctl -p <project> session start --worker <label> --worktree <branch> --model <tier> [-f|--follow|--watch] [--tools] [--parent-session <session-id>] "<prompt>"',
@@ -104,6 +95,7 @@ const SESSION_RUN_HELP_TEXT = [
104
95
  ""
105
96
  ].join("\n");
106
97
  const SHARED_HELP_ENTRIES = [
98
+ ...ACCOUNT_HELP_ENTRIES.map((entry) => ({ ...entry, section: "account" })),
107
99
  { section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
108
100
  { section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
109
101
  {
@@ -263,26 +255,10 @@ const SHARED_HELP_ENTRIES = [
263
255
  usage: "-s <session-id> answer-env-request --worker <label> --mode <mode> --model <tier> [-e KEY=value] [--context <text>]",
264
256
  description: "Answer a pending environment variable request with values, context, or both."
265
257
  },
266
- {
267
- section: "kubernetes",
268
- usage: "k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
269
- description: "Show current and rolling CPU and memory usage for managed-vCluster workload containers."
270
- },
271
- {
272
- section: "kubernetes",
273
- usage: "vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
274
- description: "Trust an exact GitHub Actions workflow path to authenticate kubectl against your managed vCluster."
275
- },
276
- { section: "kubernetes", usage: "vcluster trusted-workflow list", description: "List trusted GitHub Actions workflows." },
277
- {
278
- section: "kubernetes",
279
- usage: "vcluster trusted-workflow remove <workflow-id>",
280
- description: "Revoke a trusted GitHub Actions workflow and its outstanding credentials."
281
- },
282
258
  {
283
259
  section: "processes",
284
260
  usage: "ps list [--worker <label>]",
285
- description: "List active r5d-managed worker processes; global project, branch, and session filters apply."
261
+ description: "List active agent processes on workers; global project, branch, and session filters apply."
286
262
  },
287
263
  {
288
264
  section: "processes",
@@ -298,23 +274,23 @@ const SHARED_HELP_ENTRIES = [
298
274
  ];
299
275
  const HELP_SECTION_ORDER = [
300
276
  "auth",
277
+ "account",
301
278
  "workspace",
302
279
  "projects",
303
280
  "branches",
304
281
  "envs",
305
282
  "sessions",
306
- "kubernetes",
307
283
  "processes",
308
284
  "shell"
309
285
  ];
310
286
  const HELP_SECTION_TITLES = {
311
287
  auth: "Auth",
288
+ account: "Account instructions, environment variables, and secret files",
312
289
  workspace: "Canonical workspace",
313
290
  projects: "Projects",
314
291
  branches: "Branches",
315
292
  envs: "Environment variables",
316
293
  sessions: "Sessions",
317
- kubernetes: "Kubernetes",
318
294
  processes: "Processes",
319
295
  shell: "Shell"
320
296
  };
@@ -638,7 +614,10 @@ async function deleteBranchThroughPlatform(client, projectRef, branchName, optio
638
614
  const branchId = branch.branchId;
639
615
  let accepted;
640
616
  try {
641
- accepted = await requireServerRouteSupport("delete branch", () => client.projects.branches.delete(projectRef, branchName, { branchId }));
617
+ accepted = await requireServerRouteSupport(
618
+ "delete branch",
619
+ () => client.projects.branches.delete(projectRef, branchName, { branchId })
620
+ );
642
621
  } catch (error) {
643
622
  const landed = error instanceof R5dctlApiError ? await resolveUncertainAcceptance(client, projectRef, branchId, error) : null;
644
623
  if (!landed) throw error;
@@ -979,12 +958,31 @@ function parseGlobalArgs(argv) {
979
958
  version: false
980
959
  };
981
960
  const rest = [];
961
+ const seenGlobalFlags = /* @__PURE__ */ new Set();
962
+ let duplicateGlobalFlag;
982
963
  for (let i = 0; i < argv.length; i += 1) {
983
964
  const arg = argv[i];
965
+ if (rest[0] === "account" && (arg === "--value" || arg === "--file" || arg === "--description")) {
966
+ rest.push(arg);
967
+ if (i + 1 < argv.length) rest.push(argv[++i]);
968
+ continue;
969
+ }
970
+ if (rest[0] === "account" && arg === "") {
971
+ rest.push(arg);
972
+ continue;
973
+ }
984
974
  if (!arg) {
985
975
  continue;
986
976
  }
977
+ const globalName = { "-p": "--project", "-b": "--branch", "-s": "--session", "-h": "--help", "-v": "--version" }[arg] ?? arg.split("=", 1)[0];
978
+ if (["--base-url", "--json", "--config", "--token", "--api-key", "--project", "--branch", "--session", "--help", "--version"].includes(
979
+ globalName
980
+ )) {
981
+ if (seenGlobalFlags.has(globalName)) duplicateGlobalFlag ??= globalName;
982
+ seenGlobalFlags.add(globalName);
983
+ }
987
984
  if (arg === "--") {
985
+ if (rest[0] === "account") rest.push(arg);
988
986
  rest.push(...argv.slice(i + 1));
989
987
  break;
990
988
  }
@@ -1065,6 +1063,7 @@ function parseGlobalArgs(argv) {
1065
1063
  }
1066
1064
  rest.push(arg);
1067
1065
  }
1066
+ if (rest[0] === "account" && duplicateGlobalFlag) throw new Error(`${duplicateGlobalFlag} may only be provided once`);
1068
1067
  return { options, rest };
1069
1068
  }
1070
1069
  function parseConfig(configPath) {
@@ -1553,36 +1552,6 @@ function parseRecentSessionArgs(args) {
1553
1552
  if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new Error("--limit must be an integer between 1 and 200");
1554
1553
  return { limit };
1555
1554
  }
1556
- function parseK8sUsageArgs(args) {
1557
- const parsed = { window: "24h" };
1558
- const seen = /* @__PURE__ */ new Set();
1559
- for (let index = 0; index < args.length; index += 1) {
1560
- const arg = args[index];
1561
- if (!arg) continue;
1562
- const option = ["--namespace", "--workload", "--container", "--window"].find(
1563
- (candidate) => arg === candidate || arg.startsWith(`${candidate}=`)
1564
- );
1565
- if (!option) throw new Error(`Unknown k8s usage argument: ${arg}`);
1566
- if (seen.has(option)) throw new Error(`Use ${option} only once`);
1567
- seen.add(option);
1568
- const inlineValue = parseLongOptionWithEquals(arg, option);
1569
- const candidateValue = inlineValue ?? args[index + 1];
1570
- if (!candidateValue || candidateValue.startsWith("--")) throw new Error(`Missing value for ${option}`);
1571
- const value = candidateValue;
1572
- if (inlineValue === void 0) index += 1;
1573
- if (option === "--window") {
1574
- if (value !== "24h" && value !== "7d") throw new Error("--window must be 24h or 7d");
1575
- parsed.window = value;
1576
- } else if (option === "--namespace") {
1577
- parsed.namespace = value;
1578
- } else if (option === "--workload") {
1579
- parsed.workload = value;
1580
- } else {
1581
- parsed.container = value;
1582
- }
1583
- }
1584
- return parsed;
1585
- }
1586
1555
  async function openInBrowser(url) {
1587
1556
  if (process.platform === "darwin") {
1588
1557
  spawn("open", [url], { stdio: "ignore", detached: true }).unref();
@@ -1773,7 +1742,9 @@ function renderSessionList(sessions, nowMs = Date.now()) {
1773
1742
  if (sessions.length === 0) {
1774
1743
  return "No sessions found.\n";
1775
1744
  }
1776
- return `${sessions.map((session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")).join("\n")}
1745
+ return `${sessions.map(
1746
+ (session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")
1747
+ ).join("\n")}
1777
1748
  `;
1778
1749
  }
1779
1750
  function renderRecentSessionList(sessions) {
@@ -1845,171 +1816,6 @@ Error: ${result.error}` : "";
1845
1816
  return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
1846
1817
  `;
1847
1818
  }
1848
- function summarizeK8sHistory(history) {
1849
- if (history.envelope.length === 0) {
1850
- return {
1851
- cpuMinNanoCores: null,
1852
- cpuMaxNanoCores: null,
1853
- memoryMinBytes: null,
1854
- memoryMaxBytes: null,
1855
- sampleCount: 0
1856
- };
1857
- }
1858
- return {
1859
- cpuMinNanoCores: Math.min(...history.envelope.map((point) => point.cpuMinNanoCores)),
1860
- cpuMaxNanoCores: Math.max(...history.envelope.map((point) => point.cpuMaxNanoCores)),
1861
- memoryMinBytes: Math.min(...history.envelope.map((point) => point.memoryMinBytes)),
1862
- memoryMaxBytes: Math.max(...history.envelope.map((point) => point.memoryMaxBytes)),
1863
- sampleCount: history.envelope.reduce((total, point) => total + point.sampleCount, 0)
1864
- };
1865
- }
1866
- async function mapWithConcurrency(items, concurrency, fn) {
1867
- const results = new Array(items.length);
1868
- let nextIndex = 0;
1869
- await Promise.all(
1870
- Array.from({ length: Math.min(concurrency, items.length) }, async () => {
1871
- while (nextIndex < items.length) {
1872
- const index = nextIndex++;
1873
- const item = items[index];
1874
- if (item !== void 0) results[index] = await fn(item);
1875
- }
1876
- })
1877
- );
1878
- return results;
1879
- }
1880
- async function collectK8sUsage(client, options) {
1881
- const resources = await client.k8s.resources();
1882
- const candidates = resources.workloads.flatMap((workload) => workload.containers.map((container) => ({ workload, container }))).filter(({ workload, container }) => {
1883
- const workloadMatches = !options.workload || options.workload === workload.name || options.workload === `${workload.namespace}/${workload.name}` || options.workload === workload.uid;
1884
- return (!options.namespace || options.namespace === workload.namespace) && workloadMatches && (!options.container || options.container === container.name);
1885
- }).sort(
1886
- (left, right) => left.workload.namespace.localeCompare(right.workload.namespace) || left.workload.name.localeCompare(right.workload.name) || left.container.name.localeCompare(right.container.name)
1887
- );
1888
- if (candidates.length === 0 && (options.namespace || options.workload || options.container)) {
1889
- throw new Error("No managed-vCluster workload containers matched the requested filters");
1890
- }
1891
- const entries = await mapWithConcurrency(candidates, 8, async ({ workload, container }) => {
1892
- const history = await client.k8s.resourceHistory({ workloadUid: workload.uid, containerName: container.name, window: options.window });
1893
- return {
1894
- workload: {
1895
- uid: workload.uid,
1896
- kind: workload.kind,
1897
- namespace: workload.namespace,
1898
- name: workload.name,
1899
- replicas: workload.replicas,
1900
- readyReplicas: workload.readyReplicas
1901
- },
1902
- container: {
1903
- name: container.name,
1904
- currentUsage: container.currentUsage,
1905
- pods: container.pods
1906
- },
1907
- sampledPodCount: container.pods.filter((pod) => pod.cpuNanoCores !== null || pod.memoryBytes !== null).length,
1908
- summary: summarizeK8sHistory(history),
1909
- history
1910
- };
1911
- });
1912
- return {
1913
- window: options.window,
1914
- collectedAt: resources.collectedAt,
1915
- metricsFreshAt: resources.metricsFreshAt,
1916
- metricsStale: resources.metricsStale,
1917
- filters: {
1918
- namespace: options.namespace,
1919
- workload: options.workload,
1920
- container: options.container
1921
- },
1922
- entries
1923
- };
1924
- }
1925
- function formatDecimal(value) {
1926
- if (value >= 100) return value.toFixed(0);
1927
- if (value >= 10) return value.toFixed(1).replace(/\.0$/, "");
1928
- return value.toFixed(2).replace(/\.0+$/, "").replace(/(\.\d)0$/, "$1");
1929
- }
1930
- function formatK8sCpu(value) {
1931
- if (value === null) return "-";
1932
- const milliCores = value / 1e6;
1933
- return `${formatDecimal(milliCores)}m`;
1934
- }
1935
- function formatK8sMemory(value) {
1936
- if (value === null) return "-";
1937
- const units = ["B", "Ki", "Mi", "Gi", "Ti"];
1938
- let amount = value;
1939
- let unitIndex = 0;
1940
- while (amount >= 1024 && unitIndex < units.length - 1) {
1941
- amount /= 1024;
1942
- unitIndex += 1;
1943
- }
1944
- return `${formatDecimal(amount)}${units[unitIndex]}`;
1945
- }
1946
- function getK8sUsageState(entry) {
1947
- const hasHistory = entry.history.envelope.length > 0;
1948
- const terminalPods = entry.container.pods.filter((pod) => pod.phase === "Succeeded" || pod.phase === "Failed").length;
1949
- const inactive = entry.sampledPodCount === 0 && (entry.workload.replicas === 0 || entry.container.pods.length > 0 && terminalPods === entry.container.pods.length || entry.workload.kind === "CronJob" && entry.container.pods.length === 0);
1950
- if (inactive) return "inactive";
1951
- if (entry.sampledPodCount === 0) return hasHistory ? entry.history.stale ? "stale" : "no current" : "no data";
1952
- if (!hasHistory) return "warming";
1953
- if (entry.history.stale) return "stale";
1954
- if (entry.history.incomplete) return "partial";
1955
- return "healthy";
1956
- }
1957
- function abbreviatedK8sKind(kind) {
1958
- switch (kind) {
1959
- case "Deployment":
1960
- return "deploy";
1961
- case "StatefulSet":
1962
- return "sts";
1963
- case "DaemonSet":
1964
- return "ds";
1965
- case "CronJob":
1966
- return "cron";
1967
- case "ReplicaSet":
1968
- return "rs";
1969
- case "Job":
1970
- return "job";
1971
- case "Pod":
1972
- return "pod";
1973
- }
1974
- }
1975
- function renderK8sUsageReport(report) {
1976
- const freshness = report.metricsFreshAt ?? "unavailable";
1977
- const lines = [
1978
- `Window: ${report.window} Collector: ${report.metricsStale ? "stale" : "current"} Fresh: ${freshness}`,
1979
- "PODS counts pods with current metrics."
1980
- ];
1981
- if (report.entries.length === 0) return `${lines.join("\n")}
1982
- No managed-vCluster workload containers found.
1983
- `;
1984
- const headers = ["WORKLOAD", "CONTAINER", "PODS", "CPU NOW", "CPU MAX", "MEM NOW", "MEM MAX", "STATE"];
1985
- const grouped = /* @__PURE__ */ new Map();
1986
- for (const entry of report.entries) {
1987
- const entries = grouped.get(entry.workload.namespace) ?? [];
1988
- entries.push(entry);
1989
- grouped.set(entry.workload.namespace, entries);
1990
- }
1991
- for (const [namespace, entries] of grouped) {
1992
- const rows = entries.map((entry) => {
1993
- const state = getK8sUsageState(entry);
1994
- return [
1995
- `${abbreviatedK8sKind(entry.workload.kind)}/${entry.workload.name}`,
1996
- entry.container.name,
1997
- String(entry.sampledPodCount),
1998
- formatK8sCpu(entry.container.currentUsage.cpuNanoCores),
1999
- formatK8sCpu(entry.summary.cpuMaxNanoCores),
2000
- formatK8sMemory(entry.container.currentUsage.memoryBytes),
2001
- formatK8sMemory(entry.summary.memoryMaxBytes),
2002
- state === "healthy" ? "-" : state
2003
- ];
2004
- });
2005
- const widths = headers.map((header, column) => Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0)));
2006
- const formatRow = (row) => row.map((value, column) => column === row.length - 1 ? value : value.padEnd(widths[column] ?? value.length)).join(" ");
2007
- const separator = widths.map((width) => "-".repeat(width)).join(" ");
2008
- lines.push("", `Namespace: ${namespace}`, formatRow(headers), separator, ...rows.map(formatRow));
2009
- }
2010
- return `${lines.join("\n")}
2011
- `;
2012
- }
2013
1819
  function renderSessionDescription(session) {
2014
1820
  return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
2015
1821
  }
@@ -2936,45 +2742,6 @@ async function executeR5dctlCommand(client, json, args) {
2936
2742
  write(result, renderWorkspaceSync(result));
2937
2743
  return;
2938
2744
  }
2939
- if (first === "k8s" && second === "usage") {
2940
- const report = await collectK8sUsage(client, parseK8sUsageArgs(args.slice(2)));
2941
- write(report, renderK8sUsageReport(report));
2942
- return;
2943
- }
2944
- if (first === "vcluster" && second === "trusted-workflow") {
2945
- const subcommand = requireValue(args[2], "Missing trusted-workflow command");
2946
- if (subcommand === "add") {
2947
- const parsed = parseCommandOperands(args.slice(3), /* @__PURE__ */ new Set(["--repo", "--workflow"]), /* @__PURE__ */ new Map(), "trusted-workflow add");
2948
- if (parsed.positionals.length > 0) throw new Error(`Unexpected trusted-workflow add value: ${parsed.positionals[0]}`);
2949
- const workflow = await client.k8s.trustedWorkflows.add({
2950
- repository: requireValue(parsed.values.get("--repo"), "--repo <owner/repository> is required"),
2951
- workflowPath: requireValue(parsed.values.get("--workflow"), "--workflow <path> is required")
2952
- });
2953
- write(workflow, "Trusted GitHub workflow configured successfully.\n");
2954
- return;
2955
- }
2956
- if (subcommand === "list") {
2957
- if (args.length > 3) throw new Error(`Unexpected trusted-workflow list value: ${args[3]}`);
2958
- const workflows = await client.k8s.trustedWorkflows.list();
2959
- write(
2960
- workflows,
2961
- workflows.length === 0 ? "No trusted GitHub workflows.\n" : `${workflows.map(
2962
- (workflow) => `${workflow.id} ${workflow.repository} ${workflow.workflowPath} ${workflow.revokedAt ? "revoked" : "active"}`
2963
- ).join("\n")}
2964
- `
2965
- );
2966
- return;
2967
- }
2968
- if (subcommand === "remove") {
2969
- const workflowId = requireValue(args[3], "Missing trusted workflow id");
2970
- if (args.length > 4) throw new Error(`Unexpected trusted-workflow remove value: ${args[4]}`);
2971
- const result = await client.k8s.trustedWorkflows.revoke(workflowId);
2972
- if (!result.success) throw new Error("Trusted GitHub workflow was not found or was already revoked");
2973
- write(result, "Trusted GitHub workflow revoked.\n");
2974
- return;
2975
- }
2976
- throw new Error(`Unknown trusted-workflow command: ${subcommand}`);
2977
- }
2978
2745
  if (first === "ps" && second === "list") {
2979
2746
  const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
2980
2747
  write(processes, renderProcessList(processes));
@@ -3010,7 +2777,9 @@ async function executeR5dctlCommand(client, json, args) {
3010
2777
  });
3011
2778
  write(result2, renderSessionClaimResponse(result2, nowMs));
3012
2779
  if (result2.outcome === "held_by_other") {
3013
- throw new Error(`Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`);
2780
+ throw new Error(
2781
+ `Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`
2782
+ );
3014
2783
  }
3015
2784
  return;
3016
2785
  }
@@ -3030,12 +2799,20 @@ async function executeR5dctlCommand(client, json, args) {
3030
2799
  return;
3031
2800
  }
3032
2801
  if (!current.heldByActor) {
3033
- write({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs));
2802
+ write(
2803
+ { outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current },
2804
+ renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs)
2805
+ );
3034
2806
  throw new Error(`Claim ${claim.topic} is held by session ${current.holderSessionId}, not by ${list.actorSessionId}`);
3035
2807
  }
3036
2808
  generation = current.generation;
3037
2809
  }
3038
- const result = claim.action === "renew" ? await client.claims.renew({ topic: claim.topic, ...scope, generation, ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {} }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
2810
+ const result = claim.action === "renew" ? await client.claims.renew({
2811
+ topic: claim.topic,
2812
+ ...scope,
2813
+ generation,
2814
+ ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {}
2815
+ }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
3039
2816
  write(result, renderSessionClaimResponse(result, nowMs));
3040
2817
  const succeeded = result.outcome === "renewed" || result.outcome === "released" || claim.action === "release" && result.outcome === "missing";
3041
2818
  if (!succeeded) throw new Error(`Claim ${claim.topic}: ${CLAIM_OUTCOME_TEXT[result.outcome] ?? result.outcome}`);
@@ -3152,7 +2929,10 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
3152
2929
  const offset = first === "describe" ? 2 : 3;
3153
2930
  const projectRef = requireValue(args[offset], "Missing project reference");
3154
2931
  const branchId = requireValue(args[offset + 1], "Missing branch id");
3155
- const status = await requireServerRouteSupport("describe branch-deletion", () => client.projects.branches.describeDeletion(projectRef, branchId));
2932
+ const status = await requireServerRouteSupport(
2933
+ "describe branch-deletion",
2934
+ () => client.projects.branches.describeDeletion(projectRef, branchId)
2935
+ );
3156
2936
  write(status, renderBranchDeletionStatus(status));
3157
2937
  return;
3158
2938
  }
@@ -3297,25 +3077,26 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
3297
3077
  throw new Error(`Unknown command: ${args.join(" ")}`);
3298
3078
  }
3299
3079
  function resolveCommandExecution(options, rest) {
3300
- const trailingHelp = isHelpFlag(rest.at(-1));
3080
+ const accountLiteralHelp = rest[0] === "account" && ["--value", "--file", "--description"].includes(rest.at(-2) ?? "");
3081
+ const trailingHelp = isHelpFlag(rest.at(-1)) && !accountLiteralHelp;
3301
3082
  const normalizedRest = trailingHelp ? rest.slice(0, -1) : rest;
3302
3083
  const command = normalizedRest[0];
3303
3084
  if (!command) {
3304
3085
  throw new Error("Missing command");
3305
3086
  }
3306
3087
  const commandArgs = normalizedRest.slice(1);
3088
+ if (command === "account") {
3089
+ if (trailingHelp || commandArgs.length <= 1) {
3090
+ return { kind: "cli-help", text: getAccountHelp(commandArgs.slice(0, 2)) };
3091
+ }
3092
+ return { kind: "account", command: parseAccountCommand(commandArgs) };
3093
+ }
3307
3094
  if (command === "ps" && commandArgs.length === 0) {
3308
3095
  return {
3309
3096
  kind: "cli-help",
3310
3097
  text: PS_HELP_TEXT
3311
3098
  };
3312
3099
  }
3313
- if (command === "k8s" && commandArgs.length === 0) {
3314
- return {
3315
- kind: "cli-help",
3316
- text: K8S_HELP_TEXT
3317
- };
3318
- }
3319
3100
  if (command === "session" && commandArgs.length === 0) {
3320
3101
  return {
3321
3102
  kind: "cli-help",
@@ -3337,10 +3118,7 @@ function resolveCommandExecution(options, rest) {
3337
3118
  text: sharedHelp
3338
3119
  };
3339
3120
  }
3340
- return {
3341
- kind: "plugin",
3342
- pluginArgs: [...normalizedRest, "--help"]
3343
- };
3121
+ throw new Error(`Unknown command: ${normalizedRest.join(" ")}`);
3344
3122
  }
3345
3123
  if (command === "projects" || command === "sessions" || command === "workspace") {
3346
3124
  return {
@@ -3450,26 +3228,6 @@ function resolveCommandExecution(options, rest) {
3450
3228
  }
3451
3229
  throw new Error(`Unknown ps command: ${subcommand ?? ""}`.trim());
3452
3230
  }
3453
- if (command === "k8s") {
3454
- const subcommand = commandArgs[0];
3455
- if (options.project || options.branch || options.session) {
3456
- throw new Error("Project, branch, and session scopes are not supported for `k8s`");
3457
- }
3458
- if (subcommand === "usage") {
3459
- parseK8sUsageArgs(commandArgs.slice(1));
3460
- return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
3461
- }
3462
- throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
3463
- }
3464
- if (command === "vcluster") {
3465
- if (options.project || options.branch || options.session) {
3466
- throw new Error("Project, branch, and session scopes are not supported for `vcluster`");
3467
- }
3468
- if (commandArgs[0] !== "trusted-workflow") {
3469
- throw new Error(`Unknown vcluster command: ${commandArgs[0] ?? ""}`.trim());
3470
- }
3471
- return { kind: "plugin", pluginArgs: ["vcluster", "trusted-workflow", ...commandArgs.slice(1)] };
3472
- }
3473
3231
  if (command === "get") {
3474
3232
  const target = commandArgs[0];
3475
3233
  if (target === "projects") {
@@ -3844,6 +3602,10 @@ async function runCommand(argv) {
3844
3602
  const clientOptions = resolveClientOptions(options, config);
3845
3603
  const client = new R5dctlClient(clientOptions);
3846
3604
  const execution = resolveCommandExecution(options, rest);
3605
+ if (execution.kind === "account") {
3606
+ await executeAccountCommand(client, execution.command, options.json);
3607
+ return 0;
3608
+ }
3847
3609
  if (execution.kind === "auth-login") {
3848
3610
  await handleAuthLogin(client, options, execution.commandArgs, config);
3849
3611
  return 0;
@@ -3951,18 +3713,14 @@ export {
3951
3713
  branchDeletionConfirmationMessage,
3952
3714
  branchDeletionsConfirmationMessage,
3953
3715
  branchDeletionsFailureMessage,
3954
- collectK8sUsage,
3955
3716
  createBranchThroughPlatform,
3956
3717
  deleteBranchThroughPlatform,
3957
3718
  deleteBranchesThroughPlatform,
3958
3719
  dispatchSessionRunCommand,
3959
3720
  followR5dctlSession,
3960
- formatK8sCpu,
3961
- formatK8sMemory,
3962
3721
  formatProcessAge,
3963
3722
  formatProcessLane,
3964
3723
  getCliHelpText,
3965
- getK8sUsageState,
3966
3724
  getProjectEnvValue,
3967
3725
  getR5dctlVersion,
3968
3726
  getTransientDevicePollDelay,
@@ -3983,7 +3741,6 @@ export {
3983
3741
  parseEnvRequestResponseArgs,
3984
3742
  parseGetEnvsArgs,
3985
3743
  parseGlobalArgs,
3986
- parseK8sUsageArgs,
3987
3744
  parsePromptArgs,
3988
3745
  parsePsHistoryArgs,
3989
3746
  parsePsListArgs,
@@ -4008,7 +3765,6 @@ export {
4008
3765
  renderConversationWorkResponse,
4009
3766
  renderDeviceAuthorizationPrompt,
4010
3767
  renderEnvValues,
4011
- renderK8sUsageReport,
4012
3768
  renderProcessHistory,
4013
3769
  renderProcessInspection,
4014
3770
  renderProcessList,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.126",
3
+ "version": "0.0.128",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,67 @@
1
+ import { type R5dctlClient } from "@ricsam/r5d-api";
2
+ export declare const ACCOUNT_HELP_ENTRIES: readonly [{
3
+ readonly usage: "account instructions get";
4
+ readonly description: "Write account AGENTS.md exactly to stdout; --json prints its metadata and content.";
5
+ }, {
6
+ readonly usage: "account instructions set (--file <path>|--stdin)";
7
+ readonly description: "Replace account AGENTS.md, including clearing it with empty input.";
8
+ }, {
9
+ readonly usage: "account env list";
10
+ readonly description: "List account environment variable metadata without revealing values.";
11
+ }, {
12
+ readonly usage: "account env set <name> [--description <text>] [--value <value>|--file <path>|--stdin]";
13
+ readonly description: "Set an account environment variable, or edit an existing entry's description.";
14
+ }, {
15
+ readonly usage: "account env revoke <name>";
16
+ readonly description: "Remove the stored value while retaining its name and description.";
17
+ }, {
18
+ readonly usage: "account env delete <name>";
19
+ readonly description: "Delete an account environment entry.";
20
+ }, {
21
+ readonly usage: "account secrets list";
22
+ readonly description: "List account secret-file metadata without revealing contents.";
23
+ }, {
24
+ readonly usage: "account secrets set <name> [--description <text>] [--file <path>|--stdin]";
25
+ readonly description: "Store a secret file whose path is delivered through the named environment variable.";
26
+ }, {
27
+ readonly usage: "account secrets revoke <name>";
28
+ readonly description: "Remove a stored secret file's contents while retaining its metadata.";
29
+ }, {
30
+ readonly usage: "account secrets delete <name>";
31
+ readonly description: "Delete an account secret-file entry.";
32
+ }];
33
+ type ValueSource = {
34
+ kind: "value";
35
+ value: string;
36
+ } | {
37
+ kind: "file";
38
+ path: string;
39
+ } | {
40
+ kind: "stdin";
41
+ };
42
+ export type AccountCommand = {
43
+ group: "instructions";
44
+ action: "get";
45
+ } | {
46
+ group: "instructions";
47
+ action: "set";
48
+ source: ValueSource;
49
+ } | {
50
+ group: "env" | "secrets";
51
+ action: "list";
52
+ } | {
53
+ group: "env" | "secrets";
54
+ action: "set";
55
+ name: string;
56
+ description?: string;
57
+ source?: ValueSource;
58
+ } | {
59
+ group: "env" | "secrets";
60
+ action: "revoke" | "delete";
61
+ name: string;
62
+ };
63
+ /** Keep input values out of every parser error, including unknown flags and operands. */
64
+ export declare function parseAccountCommand(args: string[]): AccountCommand;
65
+ export declare function getAccountHelp(args: string[]): string;
66
+ export declare function executeAccountCommand(client: R5dctlClient, command: AccountCommand, json: boolean): Promise<void>;
67
+ export {};