@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/README.md +36 -18
- package/dist/cjs/cli.cjs +63 -313
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +63 -307
- package/dist/mjs/package.json +1 -1
- package/dist/types/account.d.ts +67 -0
- package/dist/types/cli.d.ts +5 -43
- package/package.json +2 -2
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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({
|
|
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(
|
|
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
|
|
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
|
-
|
|
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,
|
package/dist/mjs/package.json
CHANGED
|
@@ -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 {};
|