@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/cjs/cli.cjs
CHANGED
|
@@ -34,18 +34,14 @@ __export(cli_exports, {
|
|
|
34
34
|
branchDeletionConfirmationMessage: () => branchDeletionConfirmationMessage,
|
|
35
35
|
branchDeletionsConfirmationMessage: () => branchDeletionsConfirmationMessage,
|
|
36
36
|
branchDeletionsFailureMessage: () => branchDeletionsFailureMessage,
|
|
37
|
-
collectK8sUsage: () => collectK8sUsage,
|
|
38
37
|
createBranchThroughPlatform: () => createBranchThroughPlatform,
|
|
39
38
|
deleteBranchThroughPlatform: () => deleteBranchThroughPlatform,
|
|
40
39
|
deleteBranchesThroughPlatform: () => deleteBranchesThroughPlatform,
|
|
41
40
|
dispatchSessionRunCommand: () => dispatchSessionRunCommand,
|
|
42
41
|
followR5dctlSession: () => followR5dctlSession,
|
|
43
|
-
formatK8sCpu: () => formatK8sCpu,
|
|
44
|
-
formatK8sMemory: () => formatK8sMemory,
|
|
45
42
|
formatProcessAge: () => formatProcessAge,
|
|
46
43
|
formatProcessLane: () => formatProcessLane,
|
|
47
44
|
getCliHelpText: () => getCliHelpText,
|
|
48
|
-
getK8sUsageState: () => getK8sUsageState,
|
|
49
45
|
getProjectEnvValue: () => getProjectEnvValue,
|
|
50
46
|
getR5dctlVersion: () => getR5dctlVersion,
|
|
51
47
|
getTransientDevicePollDelay: () => getTransientDevicePollDelay,
|
|
@@ -66,7 +62,6 @@ __export(cli_exports, {
|
|
|
66
62
|
parseEnvRequestResponseArgs: () => parseEnvRequestResponseArgs,
|
|
67
63
|
parseGetEnvsArgs: () => parseGetEnvsArgs,
|
|
68
64
|
parseGlobalArgs: () => parseGlobalArgs,
|
|
69
|
-
parseK8sUsageArgs: () => parseK8sUsageArgs,
|
|
70
65
|
parsePromptArgs: () => parsePromptArgs,
|
|
71
66
|
parsePsHistoryArgs: () => parsePsHistoryArgs,
|
|
72
67
|
parsePsListArgs: () => parsePsListArgs,
|
|
@@ -91,7 +86,6 @@ __export(cli_exports, {
|
|
|
91
86
|
renderConversationWorkResponse: () => renderConversationWorkResponse,
|
|
92
87
|
renderDeviceAuthorizationPrompt: () => renderDeviceAuthorizationPrompt,
|
|
93
88
|
renderEnvValues: () => renderEnvValues,
|
|
94
|
-
renderK8sUsageReport: () => renderK8sUsageReport,
|
|
95
89
|
renderProcessHistory: () => renderProcessHistory,
|
|
96
90
|
renderProcessInspection: () => renderProcessInspection,
|
|
97
91
|
renderProcessList: () => renderProcessList,
|
|
@@ -122,6 +116,7 @@ var import_promises = require("node:timers/promises");
|
|
|
122
116
|
var import_dotenv = require("dotenv");
|
|
123
117
|
var import_r5d_api = require("@ricsam/r5d-api");
|
|
124
118
|
var import_shell = require("./shell.cjs");
|
|
119
|
+
var import_account = require("./account.cjs");
|
|
125
120
|
const CHAT_MODES = /* @__PURE__ */ new Set(["plan", "agent", "large_diff_remediation"]);
|
|
126
121
|
const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["plan", "agent"]);
|
|
127
122
|
const R5DCTL_PROMPT_DELIVERIES = /* @__PURE__ */ new Set(["queue", "steer", "force"]);
|
|
@@ -169,20 +164,10 @@ const PS_HELP_TEXT = [
|
|
|
169
164
|
" r5dctl ps inspect <run-id>",
|
|
170
165
|
" r5dctl ps stop <run-id>",
|
|
171
166
|
"",
|
|
172
|
-
"List, inspect, or gracefully stop
|
|
167
|
+
"List, inspect, or gracefully stop agent processes on workers.",
|
|
173
168
|
"Use -p/--project, -b/--branch, or -s/--session to filter list and history.",
|
|
174
169
|
""
|
|
175
170
|
].join("\n");
|
|
176
|
-
const K8S_HELP_TEXT = [
|
|
177
|
-
"Usage:",
|
|
178
|
-
" r5dctl k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
179
|
-
" r5dctl vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
180
|
-
" r5dctl vcluster trusted-workflow list",
|
|
181
|
-
" r5dctl vcluster trusted-workflow remove <workflow-id>",
|
|
182
|
-
"",
|
|
183
|
-
"Show current and rolling CPU and memory usage for managed-vCluster workload containers.",
|
|
184
|
-
""
|
|
185
|
-
].join("\n");
|
|
186
171
|
const SESSION_RUN_HELP_TEXT = [
|
|
187
172
|
"Usage:",
|
|
188
173
|
' r5dctl -p <project> session start --worker <label> --worktree <branch> --model <tier> [-f|--follow|--watch] [--tools] [--parent-session <session-id>] "<prompt>"',
|
|
@@ -215,6 +200,7 @@ const SESSION_RUN_HELP_TEXT = [
|
|
|
215
200
|
""
|
|
216
201
|
].join("\n");
|
|
217
202
|
const SHARED_HELP_ENTRIES = [
|
|
203
|
+
...import_account.ACCOUNT_HELP_ENTRIES.map((entry) => ({ ...entry, section: "account" })),
|
|
218
204
|
{ section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
|
|
219
205
|
{ section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
|
|
220
206
|
{
|
|
@@ -374,26 +360,10 @@ const SHARED_HELP_ENTRIES = [
|
|
|
374
360
|
usage: "-s <session-id> answer-env-request --worker <label> --mode <mode> --model <tier> [-e KEY=value] [--context <text>]",
|
|
375
361
|
description: "Answer a pending environment variable request with values, context, or both."
|
|
376
362
|
},
|
|
377
|
-
{
|
|
378
|
-
section: "kubernetes",
|
|
379
|
-
usage: "k8s usage [--namespace <name>] [--workload <name|namespace/name|uid>] [--container <name>] [--window <24h|7d>]",
|
|
380
|
-
description: "Show current and rolling CPU and memory usage for managed-vCluster workload containers."
|
|
381
|
-
},
|
|
382
|
-
{
|
|
383
|
-
section: "kubernetes",
|
|
384
|
-
usage: "vcluster trusted-workflow add --repo <owner/repository> --workflow <.github/workflows/file.yml>",
|
|
385
|
-
description: "Trust an exact GitHub Actions workflow path to authenticate kubectl against your managed vCluster."
|
|
386
|
-
},
|
|
387
|
-
{ section: "kubernetes", usage: "vcluster trusted-workflow list", description: "List trusted GitHub Actions workflows." },
|
|
388
|
-
{
|
|
389
|
-
section: "kubernetes",
|
|
390
|
-
usage: "vcluster trusted-workflow remove <workflow-id>",
|
|
391
|
-
description: "Revoke a trusted GitHub Actions workflow and its outstanding credentials."
|
|
392
|
-
},
|
|
393
363
|
{
|
|
394
364
|
section: "processes",
|
|
395
365
|
usage: "ps list [--worker <label>]",
|
|
396
|
-
description: "List active
|
|
366
|
+
description: "List active agent processes on workers; global project, branch, and session filters apply."
|
|
397
367
|
},
|
|
398
368
|
{
|
|
399
369
|
section: "processes",
|
|
@@ -409,23 +379,23 @@ const SHARED_HELP_ENTRIES = [
|
|
|
409
379
|
];
|
|
410
380
|
const HELP_SECTION_ORDER = [
|
|
411
381
|
"auth",
|
|
382
|
+
"account",
|
|
412
383
|
"workspace",
|
|
413
384
|
"projects",
|
|
414
385
|
"branches",
|
|
415
386
|
"envs",
|
|
416
387
|
"sessions",
|
|
417
|
-
"kubernetes",
|
|
418
388
|
"processes",
|
|
419
389
|
"shell"
|
|
420
390
|
];
|
|
421
391
|
const HELP_SECTION_TITLES = {
|
|
422
392
|
auth: "Auth",
|
|
393
|
+
account: "Account instructions, environment variables, and secret files",
|
|
423
394
|
workspace: "Canonical workspace",
|
|
424
395
|
projects: "Projects",
|
|
425
396
|
branches: "Branches",
|
|
426
397
|
envs: "Environment variables",
|
|
427
398
|
sessions: "Sessions",
|
|
428
|
-
kubernetes: "Kubernetes",
|
|
429
399
|
processes: "Processes",
|
|
430
400
|
shell: "Shell"
|
|
431
401
|
};
|
|
@@ -749,7 +719,10 @@ async function deleteBranchThroughPlatform(client, projectRef, branchName, optio
|
|
|
749
719
|
const branchId = branch.branchId;
|
|
750
720
|
let accepted;
|
|
751
721
|
try {
|
|
752
|
-
accepted = await requireServerRouteSupport(
|
|
722
|
+
accepted = await requireServerRouteSupport(
|
|
723
|
+
"delete branch",
|
|
724
|
+
() => client.projects.branches.delete(projectRef, branchName, { branchId })
|
|
725
|
+
);
|
|
753
726
|
} catch (error) {
|
|
754
727
|
const landed = error instanceof import_r5d_api.R5dctlApiError ? await resolveUncertainAcceptance(client, projectRef, branchId, error) : null;
|
|
755
728
|
if (!landed) throw error;
|
|
@@ -1090,12 +1063,31 @@ function parseGlobalArgs(argv) {
|
|
|
1090
1063
|
version: false
|
|
1091
1064
|
};
|
|
1092
1065
|
const rest = [];
|
|
1066
|
+
const seenGlobalFlags = /* @__PURE__ */ new Set();
|
|
1067
|
+
let duplicateGlobalFlag;
|
|
1093
1068
|
for (let i = 0; i < argv.length; i += 1) {
|
|
1094
1069
|
const arg = argv[i];
|
|
1070
|
+
if (rest[0] === "account" && (arg === "--value" || arg === "--file" || arg === "--description")) {
|
|
1071
|
+
rest.push(arg);
|
|
1072
|
+
if (i + 1 < argv.length) rest.push(argv[++i]);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
if (rest[0] === "account" && arg === "") {
|
|
1076
|
+
rest.push(arg);
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1095
1079
|
if (!arg) {
|
|
1096
1080
|
continue;
|
|
1097
1081
|
}
|
|
1082
|
+
const globalName = { "-p": "--project", "-b": "--branch", "-s": "--session", "-h": "--help", "-v": "--version" }[arg] ?? arg.split("=", 1)[0];
|
|
1083
|
+
if (["--base-url", "--json", "--config", "--token", "--api-key", "--project", "--branch", "--session", "--help", "--version"].includes(
|
|
1084
|
+
globalName
|
|
1085
|
+
)) {
|
|
1086
|
+
if (seenGlobalFlags.has(globalName)) duplicateGlobalFlag ??= globalName;
|
|
1087
|
+
seenGlobalFlags.add(globalName);
|
|
1088
|
+
}
|
|
1098
1089
|
if (arg === "--") {
|
|
1090
|
+
if (rest[0] === "account") rest.push(arg);
|
|
1099
1091
|
rest.push(...argv.slice(i + 1));
|
|
1100
1092
|
break;
|
|
1101
1093
|
}
|
|
@@ -1176,6 +1168,7 @@ function parseGlobalArgs(argv) {
|
|
|
1176
1168
|
}
|
|
1177
1169
|
rest.push(arg);
|
|
1178
1170
|
}
|
|
1171
|
+
if (rest[0] === "account" && duplicateGlobalFlag) throw new Error(`${duplicateGlobalFlag} may only be provided once`);
|
|
1179
1172
|
return { options, rest };
|
|
1180
1173
|
}
|
|
1181
1174
|
function parseConfig(configPath) {
|
|
@@ -1664,36 +1657,6 @@ function parseRecentSessionArgs(args) {
|
|
|
1664
1657
|
if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new Error("--limit must be an integer between 1 and 200");
|
|
1665
1658
|
return { limit };
|
|
1666
1659
|
}
|
|
1667
|
-
function parseK8sUsageArgs(args) {
|
|
1668
|
-
const parsed = { window: "24h" };
|
|
1669
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1670
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
1671
|
-
const arg = args[index];
|
|
1672
|
-
if (!arg) continue;
|
|
1673
|
-
const option = ["--namespace", "--workload", "--container", "--window"].find(
|
|
1674
|
-
(candidate) => arg === candidate || arg.startsWith(`${candidate}=`)
|
|
1675
|
-
);
|
|
1676
|
-
if (!option) throw new Error(`Unknown k8s usage argument: ${arg}`);
|
|
1677
|
-
if (seen.has(option)) throw new Error(`Use ${option} only once`);
|
|
1678
|
-
seen.add(option);
|
|
1679
|
-
const inlineValue = parseLongOptionWithEquals(arg, option);
|
|
1680
|
-
const candidateValue = inlineValue ?? args[index + 1];
|
|
1681
|
-
if (!candidateValue || candidateValue.startsWith("--")) throw new Error(`Missing value for ${option}`);
|
|
1682
|
-
const value = candidateValue;
|
|
1683
|
-
if (inlineValue === void 0) index += 1;
|
|
1684
|
-
if (option === "--window") {
|
|
1685
|
-
if (value !== "24h" && value !== "7d") throw new Error("--window must be 24h or 7d");
|
|
1686
|
-
parsed.window = value;
|
|
1687
|
-
} else if (option === "--namespace") {
|
|
1688
|
-
parsed.namespace = value;
|
|
1689
|
-
} else if (option === "--workload") {
|
|
1690
|
-
parsed.workload = value;
|
|
1691
|
-
} else {
|
|
1692
|
-
parsed.container = value;
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
return parsed;
|
|
1696
|
-
}
|
|
1697
1660
|
async function openInBrowser(url) {
|
|
1698
1661
|
if (process.platform === "darwin") {
|
|
1699
1662
|
(0, import_node_child_process.spawn)("open", [url], { stdio: "ignore", detached: true }).unref();
|
|
@@ -1884,7 +1847,9 @@ function renderSessionList(sessions, nowMs = Date.now()) {
|
|
|
1884
1847
|
if (sessions.length === 0) {
|
|
1885
1848
|
return "No sessions found.\n";
|
|
1886
1849
|
}
|
|
1887
|
-
return `${sessions.map(
|
|
1850
|
+
return `${sessions.map(
|
|
1851
|
+
(session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")
|
|
1852
|
+
).join("\n")}
|
|
1888
1853
|
`;
|
|
1889
1854
|
}
|
|
1890
1855
|
function renderRecentSessionList(sessions) {
|
|
@@ -1956,171 +1921,6 @@ Error: ${result.error}` : "";
|
|
|
1956
1921
|
return `Workspace sync ${result.outcome} on ${result.workerLabel}; HEAD ${head}; ${result.diffSizeBytes} bytes.${discarded}${error}
|
|
1957
1922
|
`;
|
|
1958
1923
|
}
|
|
1959
|
-
function summarizeK8sHistory(history) {
|
|
1960
|
-
if (history.envelope.length === 0) {
|
|
1961
|
-
return {
|
|
1962
|
-
cpuMinNanoCores: null,
|
|
1963
|
-
cpuMaxNanoCores: null,
|
|
1964
|
-
memoryMinBytes: null,
|
|
1965
|
-
memoryMaxBytes: null,
|
|
1966
|
-
sampleCount: 0
|
|
1967
|
-
};
|
|
1968
|
-
}
|
|
1969
|
-
return {
|
|
1970
|
-
cpuMinNanoCores: Math.min(...history.envelope.map((point) => point.cpuMinNanoCores)),
|
|
1971
|
-
cpuMaxNanoCores: Math.max(...history.envelope.map((point) => point.cpuMaxNanoCores)),
|
|
1972
|
-
memoryMinBytes: Math.min(...history.envelope.map((point) => point.memoryMinBytes)),
|
|
1973
|
-
memoryMaxBytes: Math.max(...history.envelope.map((point) => point.memoryMaxBytes)),
|
|
1974
|
-
sampleCount: history.envelope.reduce((total, point) => total + point.sampleCount, 0)
|
|
1975
|
-
};
|
|
1976
|
-
}
|
|
1977
|
-
async function mapWithConcurrency(items, concurrency, fn) {
|
|
1978
|
-
const results = new Array(items.length);
|
|
1979
|
-
let nextIndex = 0;
|
|
1980
|
-
await Promise.all(
|
|
1981
|
-
Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
1982
|
-
while (nextIndex < items.length) {
|
|
1983
|
-
const index = nextIndex++;
|
|
1984
|
-
const item = items[index];
|
|
1985
|
-
if (item !== void 0) results[index] = await fn(item);
|
|
1986
|
-
}
|
|
1987
|
-
})
|
|
1988
|
-
);
|
|
1989
|
-
return results;
|
|
1990
|
-
}
|
|
1991
|
-
async function collectK8sUsage(client, options) {
|
|
1992
|
-
const resources = await client.k8s.resources();
|
|
1993
|
-
const candidates = resources.workloads.flatMap((workload) => workload.containers.map((container) => ({ workload, container }))).filter(({ workload, container }) => {
|
|
1994
|
-
const workloadMatches = !options.workload || options.workload === workload.name || options.workload === `${workload.namespace}/${workload.name}` || options.workload === workload.uid;
|
|
1995
|
-
return (!options.namespace || options.namespace === workload.namespace) && workloadMatches && (!options.container || options.container === container.name);
|
|
1996
|
-
}).sort(
|
|
1997
|
-
(left, right) => left.workload.namespace.localeCompare(right.workload.namespace) || left.workload.name.localeCompare(right.workload.name) || left.container.name.localeCompare(right.container.name)
|
|
1998
|
-
);
|
|
1999
|
-
if (candidates.length === 0 && (options.namespace || options.workload || options.container)) {
|
|
2000
|
-
throw new Error("No managed-vCluster workload containers matched the requested filters");
|
|
2001
|
-
}
|
|
2002
|
-
const entries = await mapWithConcurrency(candidates, 8, async ({ workload, container }) => {
|
|
2003
|
-
const history = await client.k8s.resourceHistory({ workloadUid: workload.uid, containerName: container.name, window: options.window });
|
|
2004
|
-
return {
|
|
2005
|
-
workload: {
|
|
2006
|
-
uid: workload.uid,
|
|
2007
|
-
kind: workload.kind,
|
|
2008
|
-
namespace: workload.namespace,
|
|
2009
|
-
name: workload.name,
|
|
2010
|
-
replicas: workload.replicas,
|
|
2011
|
-
readyReplicas: workload.readyReplicas
|
|
2012
|
-
},
|
|
2013
|
-
container: {
|
|
2014
|
-
name: container.name,
|
|
2015
|
-
currentUsage: container.currentUsage,
|
|
2016
|
-
pods: container.pods
|
|
2017
|
-
},
|
|
2018
|
-
sampledPodCount: container.pods.filter((pod) => pod.cpuNanoCores !== null || pod.memoryBytes !== null).length,
|
|
2019
|
-
summary: summarizeK8sHistory(history),
|
|
2020
|
-
history
|
|
2021
|
-
};
|
|
2022
|
-
});
|
|
2023
|
-
return {
|
|
2024
|
-
window: options.window,
|
|
2025
|
-
collectedAt: resources.collectedAt,
|
|
2026
|
-
metricsFreshAt: resources.metricsFreshAt,
|
|
2027
|
-
metricsStale: resources.metricsStale,
|
|
2028
|
-
filters: {
|
|
2029
|
-
namespace: options.namespace,
|
|
2030
|
-
workload: options.workload,
|
|
2031
|
-
container: options.container
|
|
2032
|
-
},
|
|
2033
|
-
entries
|
|
2034
|
-
};
|
|
2035
|
-
}
|
|
2036
|
-
function formatDecimal(value) {
|
|
2037
|
-
if (value >= 100) return value.toFixed(0);
|
|
2038
|
-
if (value >= 10) return value.toFixed(1).replace(/\.0$/, "");
|
|
2039
|
-
return value.toFixed(2).replace(/\.0+$/, "").replace(/(\.\d)0$/, "$1");
|
|
2040
|
-
}
|
|
2041
|
-
function formatK8sCpu(value) {
|
|
2042
|
-
if (value === null) return "-";
|
|
2043
|
-
const milliCores = value / 1e6;
|
|
2044
|
-
return `${formatDecimal(milliCores)}m`;
|
|
2045
|
-
}
|
|
2046
|
-
function formatK8sMemory(value) {
|
|
2047
|
-
if (value === null) return "-";
|
|
2048
|
-
const units = ["B", "Ki", "Mi", "Gi", "Ti"];
|
|
2049
|
-
let amount = value;
|
|
2050
|
-
let unitIndex = 0;
|
|
2051
|
-
while (amount >= 1024 && unitIndex < units.length - 1) {
|
|
2052
|
-
amount /= 1024;
|
|
2053
|
-
unitIndex += 1;
|
|
2054
|
-
}
|
|
2055
|
-
return `${formatDecimal(amount)}${units[unitIndex]}`;
|
|
2056
|
-
}
|
|
2057
|
-
function getK8sUsageState(entry) {
|
|
2058
|
-
const hasHistory = entry.history.envelope.length > 0;
|
|
2059
|
-
const terminalPods = entry.container.pods.filter((pod) => pod.phase === "Succeeded" || pod.phase === "Failed").length;
|
|
2060
|
-
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);
|
|
2061
|
-
if (inactive) return "inactive";
|
|
2062
|
-
if (entry.sampledPodCount === 0) return hasHistory ? entry.history.stale ? "stale" : "no current" : "no data";
|
|
2063
|
-
if (!hasHistory) return "warming";
|
|
2064
|
-
if (entry.history.stale) return "stale";
|
|
2065
|
-
if (entry.history.incomplete) return "partial";
|
|
2066
|
-
return "healthy";
|
|
2067
|
-
}
|
|
2068
|
-
function abbreviatedK8sKind(kind) {
|
|
2069
|
-
switch (kind) {
|
|
2070
|
-
case "Deployment":
|
|
2071
|
-
return "deploy";
|
|
2072
|
-
case "StatefulSet":
|
|
2073
|
-
return "sts";
|
|
2074
|
-
case "DaemonSet":
|
|
2075
|
-
return "ds";
|
|
2076
|
-
case "CronJob":
|
|
2077
|
-
return "cron";
|
|
2078
|
-
case "ReplicaSet":
|
|
2079
|
-
return "rs";
|
|
2080
|
-
case "Job":
|
|
2081
|
-
return "job";
|
|
2082
|
-
case "Pod":
|
|
2083
|
-
return "pod";
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
function renderK8sUsageReport(report) {
|
|
2087
|
-
const freshness = report.metricsFreshAt ?? "unavailable";
|
|
2088
|
-
const lines = [
|
|
2089
|
-
`Window: ${report.window} Collector: ${report.metricsStale ? "stale" : "current"} Fresh: ${freshness}`,
|
|
2090
|
-
"PODS counts pods with current metrics."
|
|
2091
|
-
];
|
|
2092
|
-
if (report.entries.length === 0) return `${lines.join("\n")}
|
|
2093
|
-
No managed-vCluster workload containers found.
|
|
2094
|
-
`;
|
|
2095
|
-
const headers = ["WORKLOAD", "CONTAINER", "PODS", "CPU NOW", "CPU MAX", "MEM NOW", "MEM MAX", "STATE"];
|
|
2096
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
2097
|
-
for (const entry of report.entries) {
|
|
2098
|
-
const entries = grouped.get(entry.workload.namespace) ?? [];
|
|
2099
|
-
entries.push(entry);
|
|
2100
|
-
grouped.set(entry.workload.namespace, entries);
|
|
2101
|
-
}
|
|
2102
|
-
for (const [namespace, entries] of grouped) {
|
|
2103
|
-
const rows = entries.map((entry) => {
|
|
2104
|
-
const state = getK8sUsageState(entry);
|
|
2105
|
-
return [
|
|
2106
|
-
`${abbreviatedK8sKind(entry.workload.kind)}/${entry.workload.name}`,
|
|
2107
|
-
entry.container.name,
|
|
2108
|
-
String(entry.sampledPodCount),
|
|
2109
|
-
formatK8sCpu(entry.container.currentUsage.cpuNanoCores),
|
|
2110
|
-
formatK8sCpu(entry.summary.cpuMaxNanoCores),
|
|
2111
|
-
formatK8sMemory(entry.container.currentUsage.memoryBytes),
|
|
2112
|
-
formatK8sMemory(entry.summary.memoryMaxBytes),
|
|
2113
|
-
state === "healthy" ? "-" : state
|
|
2114
|
-
];
|
|
2115
|
-
});
|
|
2116
|
-
const widths = headers.map((header, column) => Math.max(header.length, ...rows.map((row) => row[column]?.length ?? 0)));
|
|
2117
|
-
const formatRow = (row) => row.map((value, column) => column === row.length - 1 ? value : value.padEnd(widths[column] ?? value.length)).join(" ");
|
|
2118
|
-
const separator = widths.map((width) => "-".repeat(width)).join(" ");
|
|
2119
|
-
lines.push("", `Namespace: ${namespace}`, formatRow(headers), separator, ...rows.map(formatRow));
|
|
2120
|
-
}
|
|
2121
|
-
return `${lines.join("\n")}
|
|
2122
|
-
`;
|
|
2123
|
-
}
|
|
2124
1924
|
function renderSessionDescription(session) {
|
|
2125
1925
|
return [`Session: ${session.id}`, `Project: ${session.projectPath}`, `Branch: ${session.branchName}`].join("\n") + "\n";
|
|
2126
1926
|
}
|
|
@@ -3047,45 +2847,6 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
3047
2847
|
write(result, renderWorkspaceSync(result));
|
|
3048
2848
|
return;
|
|
3049
2849
|
}
|
|
3050
|
-
if (first === "k8s" && second === "usage") {
|
|
3051
|
-
const report = await collectK8sUsage(client, parseK8sUsageArgs(args.slice(2)));
|
|
3052
|
-
write(report, renderK8sUsageReport(report));
|
|
3053
|
-
return;
|
|
3054
|
-
}
|
|
3055
|
-
if (first === "vcluster" && second === "trusted-workflow") {
|
|
3056
|
-
const subcommand = requireValue(args[2], "Missing trusted-workflow command");
|
|
3057
|
-
if (subcommand === "add") {
|
|
3058
|
-
const parsed = parseCommandOperands(args.slice(3), /* @__PURE__ */ new Set(["--repo", "--workflow"]), /* @__PURE__ */ new Map(), "trusted-workflow add");
|
|
3059
|
-
if (parsed.positionals.length > 0) throw new Error(`Unexpected trusted-workflow add value: ${parsed.positionals[0]}`);
|
|
3060
|
-
const workflow = await client.k8s.trustedWorkflows.add({
|
|
3061
|
-
repository: requireValue(parsed.values.get("--repo"), "--repo <owner/repository> is required"),
|
|
3062
|
-
workflowPath: requireValue(parsed.values.get("--workflow"), "--workflow <path> is required")
|
|
3063
|
-
});
|
|
3064
|
-
write(workflow, "Trusted GitHub workflow configured successfully.\n");
|
|
3065
|
-
return;
|
|
3066
|
-
}
|
|
3067
|
-
if (subcommand === "list") {
|
|
3068
|
-
if (args.length > 3) throw new Error(`Unexpected trusted-workflow list value: ${args[3]}`);
|
|
3069
|
-
const workflows = await client.k8s.trustedWorkflows.list();
|
|
3070
|
-
write(
|
|
3071
|
-
workflows,
|
|
3072
|
-
workflows.length === 0 ? "No trusted GitHub workflows.\n" : `${workflows.map(
|
|
3073
|
-
(workflow) => `${workflow.id} ${workflow.repository} ${workflow.workflowPath} ${workflow.revokedAt ? "revoked" : "active"}`
|
|
3074
|
-
).join("\n")}
|
|
3075
|
-
`
|
|
3076
|
-
);
|
|
3077
|
-
return;
|
|
3078
|
-
}
|
|
3079
|
-
if (subcommand === "remove") {
|
|
3080
|
-
const workflowId = requireValue(args[3], "Missing trusted workflow id");
|
|
3081
|
-
if (args.length > 4) throw new Error(`Unexpected trusted-workflow remove value: ${args[4]}`);
|
|
3082
|
-
const result = await client.k8s.trustedWorkflows.revoke(workflowId);
|
|
3083
|
-
if (!result.success) throw new Error("Trusted GitHub workflow was not found or was already revoked");
|
|
3084
|
-
write(result, "Trusted GitHub workflow revoked.\n");
|
|
3085
|
-
return;
|
|
3086
|
-
}
|
|
3087
|
-
throw new Error(`Unknown trusted-workflow command: ${subcommand}`);
|
|
3088
|
-
}
|
|
3089
2850
|
if (first === "ps" && second === "list") {
|
|
3090
2851
|
const processes = await client.processes.list(parsePsListArgs(args.slice(2)));
|
|
3091
2852
|
write(processes, renderProcessList(processes));
|
|
@@ -3121,7 +2882,9 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
3121
2882
|
});
|
|
3122
2883
|
write(result2, renderSessionClaimResponse(result2, nowMs));
|
|
3123
2884
|
if (result2.outcome === "held_by_other") {
|
|
3124
|
-
throw new Error(
|
|
2885
|
+
throw new Error(
|
|
2886
|
+
`Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`
|
|
2887
|
+
);
|
|
3125
2888
|
}
|
|
3126
2889
|
return;
|
|
3127
2890
|
}
|
|
@@ -3141,12 +2904,20 @@ async function executeR5dctlCommand(client, json, args) {
|
|
|
3141
2904
|
return;
|
|
3142
2905
|
}
|
|
3143
2906
|
if (!current.heldByActor) {
|
|
3144
|
-
write(
|
|
2907
|
+
write(
|
|
2908
|
+
{ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current },
|
|
2909
|
+
renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs)
|
|
2910
|
+
);
|
|
3145
2911
|
throw new Error(`Claim ${claim.topic} is held by session ${current.holderSessionId}, not by ${list.actorSessionId}`);
|
|
3146
2912
|
}
|
|
3147
2913
|
generation = current.generation;
|
|
3148
2914
|
}
|
|
3149
|
-
const result = claim.action === "renew" ? await client.claims.renew({
|
|
2915
|
+
const result = claim.action === "renew" ? await client.claims.renew({
|
|
2916
|
+
topic: claim.topic,
|
|
2917
|
+
...scope,
|
|
2918
|
+
generation,
|
|
2919
|
+
...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {}
|
|
2920
|
+
}) : await client.claims.release({ topic: claim.topic, ...scope, generation });
|
|
3150
2921
|
write(result, renderSessionClaimResponse(result, nowMs));
|
|
3151
2922
|
const succeeded = result.outcome === "renewed" || result.outcome === "released" || claim.action === "release" && result.outcome === "missing";
|
|
3152
2923
|
if (!succeeded) throw new Error(`Claim ${claim.topic}: ${CLAIM_OUTCOME_TEXT[result.outcome] ?? result.outcome}`);
|
|
@@ -3263,7 +3034,10 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
|
|
|
3263
3034
|
const offset = first === "describe" ? 2 : 3;
|
|
3264
3035
|
const projectRef = requireValue(args[offset], "Missing project reference");
|
|
3265
3036
|
const branchId = requireValue(args[offset + 1], "Missing branch id");
|
|
3266
|
-
const status = await requireServerRouteSupport(
|
|
3037
|
+
const status = await requireServerRouteSupport(
|
|
3038
|
+
"describe branch-deletion",
|
|
3039
|
+
() => client.projects.branches.describeDeletion(projectRef, branchId)
|
|
3040
|
+
);
|
|
3267
3041
|
write(status, renderBranchDeletionStatus(status));
|
|
3268
3042
|
return;
|
|
3269
3043
|
}
|
|
@@ -3408,25 +3182,26 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
|
|
|
3408
3182
|
throw new Error(`Unknown command: ${args.join(" ")}`);
|
|
3409
3183
|
}
|
|
3410
3184
|
function resolveCommandExecution(options, rest) {
|
|
3411
|
-
const
|
|
3185
|
+
const accountLiteralHelp = rest[0] === "account" && ["--value", "--file", "--description"].includes(rest.at(-2) ?? "");
|
|
3186
|
+
const trailingHelp = isHelpFlag(rest.at(-1)) && !accountLiteralHelp;
|
|
3412
3187
|
const normalizedRest = trailingHelp ? rest.slice(0, -1) : rest;
|
|
3413
3188
|
const command = normalizedRest[0];
|
|
3414
3189
|
if (!command) {
|
|
3415
3190
|
throw new Error("Missing command");
|
|
3416
3191
|
}
|
|
3417
3192
|
const commandArgs = normalizedRest.slice(1);
|
|
3193
|
+
if (command === "account") {
|
|
3194
|
+
if (trailingHelp || commandArgs.length <= 1) {
|
|
3195
|
+
return { kind: "cli-help", text: (0, import_account.getAccountHelp)(commandArgs.slice(0, 2)) };
|
|
3196
|
+
}
|
|
3197
|
+
return { kind: "account", command: (0, import_account.parseAccountCommand)(commandArgs) };
|
|
3198
|
+
}
|
|
3418
3199
|
if (command === "ps" && commandArgs.length === 0) {
|
|
3419
3200
|
return {
|
|
3420
3201
|
kind: "cli-help",
|
|
3421
3202
|
text: PS_HELP_TEXT
|
|
3422
3203
|
};
|
|
3423
3204
|
}
|
|
3424
|
-
if (command === "k8s" && commandArgs.length === 0) {
|
|
3425
|
-
return {
|
|
3426
|
-
kind: "cli-help",
|
|
3427
|
-
text: K8S_HELP_TEXT
|
|
3428
|
-
};
|
|
3429
|
-
}
|
|
3430
3205
|
if (command === "session" && commandArgs.length === 0) {
|
|
3431
3206
|
return {
|
|
3432
3207
|
kind: "cli-help",
|
|
@@ -3448,10 +3223,7 @@ function resolveCommandExecution(options, rest) {
|
|
|
3448
3223
|
text: sharedHelp
|
|
3449
3224
|
};
|
|
3450
3225
|
}
|
|
3451
|
-
|
|
3452
|
-
kind: "plugin",
|
|
3453
|
-
pluginArgs: [...normalizedRest, "--help"]
|
|
3454
|
-
};
|
|
3226
|
+
throw new Error(`Unknown command: ${normalizedRest.join(" ")}`);
|
|
3455
3227
|
}
|
|
3456
3228
|
if (command === "projects" || command === "sessions" || command === "workspace") {
|
|
3457
3229
|
return {
|
|
@@ -3561,26 +3333,6 @@ function resolveCommandExecution(options, rest) {
|
|
|
3561
3333
|
}
|
|
3562
3334
|
throw new Error(`Unknown ps command: ${subcommand ?? ""}`.trim());
|
|
3563
3335
|
}
|
|
3564
|
-
if (command === "k8s") {
|
|
3565
|
-
const subcommand = commandArgs[0];
|
|
3566
|
-
if (options.project || options.branch || options.session) {
|
|
3567
|
-
throw new Error("Project, branch, and session scopes are not supported for `k8s`");
|
|
3568
|
-
}
|
|
3569
|
-
if (subcommand === "usage") {
|
|
3570
|
-
parseK8sUsageArgs(commandArgs.slice(1));
|
|
3571
|
-
return { kind: "plugin", pluginArgs: ["k8s", "usage", ...commandArgs.slice(1)] };
|
|
3572
|
-
}
|
|
3573
|
-
throw new Error(`Unknown k8s command: ${subcommand ?? ""}`.trim());
|
|
3574
|
-
}
|
|
3575
|
-
if (command === "vcluster") {
|
|
3576
|
-
if (options.project || options.branch || options.session) {
|
|
3577
|
-
throw new Error("Project, branch, and session scopes are not supported for `vcluster`");
|
|
3578
|
-
}
|
|
3579
|
-
if (commandArgs[0] !== "trusted-workflow") {
|
|
3580
|
-
throw new Error(`Unknown vcluster command: ${commandArgs[0] ?? ""}`.trim());
|
|
3581
|
-
}
|
|
3582
|
-
return { kind: "plugin", pluginArgs: ["vcluster", "trusted-workflow", ...commandArgs.slice(1)] };
|
|
3583
|
-
}
|
|
3584
3336
|
if (command === "get") {
|
|
3585
3337
|
const target = commandArgs[0];
|
|
3586
3338
|
if (target === "projects") {
|
|
@@ -3955,6 +3707,10 @@ async function runCommand(argv) {
|
|
|
3955
3707
|
const clientOptions = resolveClientOptions(options, config);
|
|
3956
3708
|
const client = new import_r5d_api.R5dctlClient(clientOptions);
|
|
3957
3709
|
const execution = resolveCommandExecution(options, rest);
|
|
3710
|
+
if (execution.kind === "account") {
|
|
3711
|
+
await (0, import_account.executeAccountCommand)(client, execution.command, options.json);
|
|
3712
|
+
return 0;
|
|
3713
|
+
}
|
|
3958
3714
|
if (execution.kind === "auth-login") {
|
|
3959
3715
|
await handleAuthLogin(client, options, execution.commandArgs, config);
|
|
3960
3716
|
return 0;
|
|
@@ -4063,18 +3819,14 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
4063
3819
|
branchDeletionConfirmationMessage,
|
|
4064
3820
|
branchDeletionsConfirmationMessage,
|
|
4065
3821
|
branchDeletionsFailureMessage,
|
|
4066
|
-
collectK8sUsage,
|
|
4067
3822
|
createBranchThroughPlatform,
|
|
4068
3823
|
deleteBranchThroughPlatform,
|
|
4069
3824
|
deleteBranchesThroughPlatform,
|
|
4070
3825
|
dispatchSessionRunCommand,
|
|
4071
3826
|
followR5dctlSession,
|
|
4072
|
-
formatK8sCpu,
|
|
4073
|
-
formatK8sMemory,
|
|
4074
3827
|
formatProcessAge,
|
|
4075
3828
|
formatProcessLane,
|
|
4076
3829
|
getCliHelpText,
|
|
4077
|
-
getK8sUsageState,
|
|
4078
3830
|
getProjectEnvValue,
|
|
4079
3831
|
getR5dctlVersion,
|
|
4080
3832
|
getTransientDevicePollDelay,
|
|
@@ -4095,7 +3847,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
4095
3847
|
parseEnvRequestResponseArgs,
|
|
4096
3848
|
parseGetEnvsArgs,
|
|
4097
3849
|
parseGlobalArgs,
|
|
4098
|
-
parseK8sUsageArgs,
|
|
4099
3850
|
parsePromptArgs,
|
|
4100
3851
|
parsePsHistoryArgs,
|
|
4101
3852
|
parsePsListArgs,
|
|
@@ -4120,7 +3871,6 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
4120
3871
|
renderConversationWorkResponse,
|
|
4121
3872
|
renderDeviceAuthorizationPrompt,
|
|
4122
3873
|
renderEnvValues,
|
|
4123
|
-
renderK8sUsageReport,
|
|
4124
3874
|
renderProcessHistory,
|
|
4125
3875
|
renderProcessInspection,
|
|
4126
3876
|
renderProcessList,
|
package/dist/cjs/package.json
CHANGED