@ornncompute/cli 0.2.24 → 1.0.25
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 +298 -426
- package/package.json +12 -6
- package/src/admin-capacity-reservations.mjs +280 -0
- package/src/admin-command-handlers.mjs +57 -0
- package/src/admin-enrollment.mjs +680 -0
- package/src/admin-mutations.mjs +187 -0
- package/src/admin-node-diagnostics.mjs +412 -0
- package/src/admin-node-lifecycle.mjs +908 -0
- package/src/admin-shell.mjs +322 -0
- package/src/api-client.mjs +81 -28
- package/src/atomic-output.mjs +112 -0
- package/src/auth-store.mjs +182 -11
- package/src/capability-catalog.mjs +7 -0
- package/src/catalog-shell.mjs +1350 -0
- package/src/cli.mjs +123 -10266
- package/src/connect-mode-handlers.mjs +170 -0
- package/src/device-auth.mjs +178 -61
- package/src/evidence-commands.mjs +1340 -0
- package/src/external-bucket-commands.mjs +1374 -0
- package/src/interaction-runtime.mjs +310 -0
- package/src/kubernetes-commands.mjs +2229 -0
- package/src/launch-mode-handlers.mjs +119 -0
- package/src/machine-reference.mjs +277 -0
- package/src/mount-commands.mjs +1146 -0
- package/src/session-commands.mjs +565 -0
- package/src/slurm-commands.mjs +263 -0
- package/src/startup.mjs +34 -11
- package/src/storage-commands.mjs +1144 -0
- package/src/update.mjs +172 -24
- package/src/vm-commands.mjs +4273 -0
- package/src/vpn-secret-output.mjs +150 -0
- package/src/wait-runtime.mjs +231 -0
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +126 -0
- package/vendor/capabilities/capabilities/admin-enrollment.js +321 -0
- package/vendor/capabilities/capabilities/admin-node-diagnostics.js +88 -0
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +235 -0
- package/vendor/capabilities/capabilities/admin.js +89 -0
- package/vendor/capabilities/capabilities/capacity-offers.js +368 -0
- package/vendor/capabilities/capabilities/cli.js +156 -0
- package/vendor/capabilities/capabilities/clusters.js +150 -1
- package/vendor/capabilities/capabilities/compute.js +522 -0
- package/vendor/capabilities/capabilities/evidence.js +145 -0
- package/vendor/capabilities/capabilities/external-buckets.js +191 -0
- package/vendor/capabilities/capabilities/fleet.js +21 -5
- package/vendor/capabilities/capabilities/identity.js +114 -3
- package/vendor/capabilities/capabilities/listings.js +14 -8
- package/vendor/capabilities/capabilities/mounts.js +176 -0
- package/vendor/capabilities/capabilities/networks.js +1257 -0
- package/vendor/capabilities/capabilities/observability.js +38 -8
- package/vendor/capabilities/capabilities/tokens.js +20 -8
- package/vendor/capabilities/capabilities/volumes.js +178 -0
- package/vendor/capabilities/capability.js +19 -13
- package/vendor/capabilities/cli-catalog.js +65 -0
- package/vendor/capabilities/cli-migrations.js +288 -0
- package/vendor/capabilities/cli.js +7 -0
- package/vendor/capabilities/software-catalog.js +36 -0
- package/vendor/capabilities/spec-catalog.js +2 -2
- package/src/catalog-dispatch.mjs +0 -605
- package/src/color.mjs +0 -71
- package/vendor/capabilities/capabilities/clusters.d.ts +0 -2
- package/vendor/capabilities/capabilities/fleet.d.ts +0 -8
- package/vendor/capabilities/capabilities/identity.d.ts +0 -3
- package/vendor/capabilities/capabilities/listings.d.ts +0 -51
- package/vendor/capabilities/capabilities/observability.d.ts +0 -5
- package/vendor/capabilities/capabilities/staff-access.d.ts +0 -29
- package/vendor/capabilities/capabilities/staff-access.js +0 -278
- package/vendor/capabilities/capabilities/staff-commerce.d.ts +0 -160
- package/vendor/capabilities/capabilities/staff-commerce.js +0 -689
- package/vendor/capabilities/capabilities/staff-fleet.d.ts +0 -140
- package/vendor/capabilities/capabilities/staff-fleet.js +0 -538
- package/vendor/capabilities/capabilities/staff-gtm.d.ts +0 -142
- package/vendor/capabilities/capabilities/staff-gtm.js +0 -521
- package/vendor/capabilities/capabilities/staff-shared.d.ts +0 -47
- package/vendor/capabilities/capabilities/staff-shared.js +0 -98
- package/vendor/capabilities/capabilities/tokens.d.ts +0 -10
- package/vendor/capabilities/capabilities/users.d.ts +0 -2
- package/vendor/capabilities/capability.d.ts +0 -93
- package/vendor/capabilities/catalog.d.ts +0 -3
- package/vendor/capabilities/catalog.js +0 -26
- package/vendor/capabilities/index.d.ts +0 -16
- package/vendor/capabilities/index.js +0 -16
- package/vendor/capabilities/names.d.ts +0 -6
- package/vendor/capabilities/role.d.ts +0 -6
- package/vendor/capabilities/role.js +0 -17
- package/vendor/capabilities/spec-catalog.d.ts +0 -78
package/src/catalog-dispatch.mjs
DELETED
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { createInterface } from "node:readline/promises";
|
|
3
|
-
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
cliRequest,
|
|
7
|
-
loadRequiredSession,
|
|
8
|
-
operatorRequest,
|
|
9
|
-
resolveApiBaseUrl,
|
|
10
|
-
} from "./api-client.mjs";
|
|
11
|
-
|
|
12
|
-
async function collectSse(url, headers, durationMs, maxEvents, fetchImpl) {
|
|
13
|
-
const controller = new AbortController();
|
|
14
|
-
const timer = setTimeout(() => controller.abort(), durationMs);
|
|
15
|
-
const events = [];
|
|
16
|
-
try {
|
|
17
|
-
const res = await fetchImpl(url, { method: "GET", headers, signal: controller.signal });
|
|
18
|
-
if (!res.ok || !res.body) {
|
|
19
|
-
const detail = await res.text().catch(() => "");
|
|
20
|
-
throw new Error(detail.trim() || `http_${res.status}`);
|
|
21
|
-
}
|
|
22
|
-
const reader = res.body.getReader();
|
|
23
|
-
const decoder = new TextDecoder();
|
|
24
|
-
let buffer = "";
|
|
25
|
-
while (events.length < maxEvents) {
|
|
26
|
-
const { done, value } = await reader.read();
|
|
27
|
-
if (done) break;
|
|
28
|
-
buffer += decoder.decode(value, { stream: true });
|
|
29
|
-
const parts = buffer.split("\n\n");
|
|
30
|
-
buffer = parts.pop() ?? "";
|
|
31
|
-
for (const part of parts) {
|
|
32
|
-
const data = part
|
|
33
|
-
.split("\n")
|
|
34
|
-
.filter((line) => line.startsWith("data:"))
|
|
35
|
-
.map((line) => line.slice(5).trim())
|
|
36
|
-
.join("\n");
|
|
37
|
-
if (!data) continue;
|
|
38
|
-
try {
|
|
39
|
-
events.push(JSON.parse(data));
|
|
40
|
-
} catch {
|
|
41
|
-
events.push({ raw: data });
|
|
42
|
-
}
|
|
43
|
-
if (events.length >= maxEvents) break;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
await reader.cancel();
|
|
48
|
-
} catch {
|
|
49
|
-
// already closed
|
|
50
|
-
}
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (!(error instanceof Error) || error.name !== "AbortError") throw error;
|
|
53
|
-
} finally {
|
|
54
|
-
clearTimeout(timer);
|
|
55
|
-
}
|
|
56
|
-
return { events };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function loadCapabilities() {
|
|
60
|
-
try {
|
|
61
|
-
return await import("@ornn/capabilities");
|
|
62
|
-
} catch {
|
|
63
|
-
return await import("../vendor/capabilities/index.js");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function parseYes(value) {
|
|
68
|
-
return /^(y|yes)$/i.test(String(value ?? "").trim());
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function confirmCatalogMutation({ stdin, stdout, yes, label }) {
|
|
72
|
-
if (yes) return true;
|
|
73
|
-
const input = stdin ?? defaultStdin;
|
|
74
|
-
const output = stdout ?? defaultStdout;
|
|
75
|
-
if (!input.isTTY || !output.isTTY) {
|
|
76
|
-
throw new Error("Refusing to create without --yes on a non-interactive terminal.");
|
|
77
|
-
}
|
|
78
|
-
const rl = createInterface({ input, output });
|
|
79
|
-
try {
|
|
80
|
-
const answer = await rl.question(`${label} [y/N] `);
|
|
81
|
-
return parseYes(answer);
|
|
82
|
-
} finally {
|
|
83
|
-
rl.close();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function splitCatalogPath(input) {
|
|
88
|
-
const trimmed = input.startsWith("/") ? input : `/${input}`;
|
|
89
|
-
const queryIndex = trimmed.indexOf("?");
|
|
90
|
-
return queryIndex === -1
|
|
91
|
-
? { pathname: trimmed, search: "" }
|
|
92
|
-
: { pathname: trimmed.slice(0, queryIndex), search: trimmed.slice(queryIndex) };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function catalogSegments(pathname) {
|
|
96
|
-
return pathname.split("/").filter(Boolean);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function isComposedStaffPath(pathname) {
|
|
100
|
-
const parts = catalogSegments(pathname);
|
|
101
|
-
if (parts[0] === "internal" && parts[1] === "commerce-reservations") return true;
|
|
102
|
-
if (parts[0] === "internal" && parts[1] === "users") return true;
|
|
103
|
-
if (parts[0] === "internal" && parts[1] === "reservations" && parts[3] === "transfer") return true;
|
|
104
|
-
if (parts[0] === "internal" && parts[1] === "nodes" && parts[3] === "deploy") return true;
|
|
105
|
-
if (parts[0] === "internal" && parts[1] === "nodes" && parts[3] === "reservation-access") return true;
|
|
106
|
-
return (
|
|
107
|
-
parts[0] === "internal" &&
|
|
108
|
-
parts[1] === "nodes" &&
|
|
109
|
-
parts[3] === "off-grid" &&
|
|
110
|
-
parts[4] === "resolve"
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function canonicalStaffCatalogPath(pathname) {
|
|
115
|
-
const parts = catalogSegments(pathname);
|
|
116
|
-
if (parts[0] === "provisioning" && parts[1] === "operators") {
|
|
117
|
-
return `/internal/v1/operators${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
118
|
-
}
|
|
119
|
-
if (parts[0] === "provisioning" && parts[1] === "facilities") {
|
|
120
|
-
return `/internal/v1/facilities${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
121
|
-
}
|
|
122
|
-
if (parts[0] === "inventory") {
|
|
123
|
-
return `/internal/v1/listings${parts.length > 1 ? `/${parts.slice(1).join("/")}` : ""}`;
|
|
124
|
-
}
|
|
125
|
-
if (
|
|
126
|
-
parts[0] === "internal" &&
|
|
127
|
-
parts[1] === "reservations" &&
|
|
128
|
-
parts[3] !== "network-validation" &&
|
|
129
|
-
parts[3] !== "node-rollout" &&
|
|
130
|
-
!(parts[3] === "nodes" && parts[4] === "assign")
|
|
131
|
-
) {
|
|
132
|
-
return `/internal/v1/reservations${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
133
|
-
}
|
|
134
|
-
if (parts[0] === "internal" && parts[1] === "bids") {
|
|
135
|
-
return `/internal/v1/bids${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
136
|
-
}
|
|
137
|
-
if (parts[0] === "internal" && parts[1] === "invoices") {
|
|
138
|
-
return `/internal/v1/invoices${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
139
|
-
}
|
|
140
|
-
if (parts[0] === "enrollment" && parts[1] === "tokens") {
|
|
141
|
-
return `/enrollment-tokens${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
142
|
-
}
|
|
143
|
-
if (parts[0] === "internal" && parts[1] === "enrollment-tokens") {
|
|
144
|
-
return `/enrollment-tokens${parts.length > 2 ? `/${parts.slice(2).join("/")}` : ""}`;
|
|
145
|
-
}
|
|
146
|
-
if (parts[0] === "internal" && parts[1] === "organizations" && parts.length === 3) {
|
|
147
|
-
return `/internal/v1/organizations/${parts[2]}`;
|
|
148
|
-
}
|
|
149
|
-
return pathname;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function mapStaffCatalogPath(path) {
|
|
153
|
-
const { pathname, search } = splitCatalogPath(path);
|
|
154
|
-
if (isComposedStaffPath(pathname)) {
|
|
155
|
-
return `/v1/staff${pathname}${search}`;
|
|
156
|
-
}
|
|
157
|
-
const canonical = canonicalStaffCatalogPath(pathname);
|
|
158
|
-
if (
|
|
159
|
-
canonical.startsWith("/internal/v1/") ||
|
|
160
|
-
canonical.startsWith("/commerce/") ||
|
|
161
|
-
canonical.startsWith("/v1/organizations/")
|
|
162
|
-
) {
|
|
163
|
-
return `${canonical}${search}`;
|
|
164
|
-
}
|
|
165
|
-
if (canonical.startsWith("/v1/")) {
|
|
166
|
-
return `${canonical}${search}`;
|
|
167
|
-
}
|
|
168
|
-
return `/v1/orchestrator${canonical}${search}`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function mapCustomerCatalogPath(path) {
|
|
172
|
-
const { pathname, search } = splitCatalogPath(path);
|
|
173
|
-
const parts = catalogSegments(pathname);
|
|
174
|
-
if (parts[0] === "inventory" || parts[0] === "listings" || parts[0] === "marketplace") {
|
|
175
|
-
return `/v1/workflows/commerce${pathname}${search}`;
|
|
176
|
-
}
|
|
177
|
-
if (pathname.startsWith("/v1/") || parts[0] === "commerce") {
|
|
178
|
-
return `${pathname}${search}`;
|
|
179
|
-
}
|
|
180
|
-
return `/v1/workflows/compute${pathname}${search}`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function catalogFetch({ env, fetchImpl }) {
|
|
184
|
-
return async (path, init) => {
|
|
185
|
-
const method = init?.method ?? "GET";
|
|
186
|
-
const observabilityPrefix = "/v1/observability";
|
|
187
|
-
const staffObservability =
|
|
188
|
-
path.startsWith(`${observabilityPrefix}/nodes/`) ||
|
|
189
|
-
path.startsWith(`${observabilityPrefix}/nodes?`);
|
|
190
|
-
const staffPath =
|
|
191
|
-
staffObservability ||
|
|
192
|
-
path.startsWith("/internal") ||
|
|
193
|
-
path.startsWith("/provisioning") ||
|
|
194
|
-
path === "/inventory" ||
|
|
195
|
-
path.startsWith("/inventory/") ||
|
|
196
|
-
path === "/enrollment-tokens" ||
|
|
197
|
-
path.startsWith("/enrollment-tokens/") ||
|
|
198
|
-
path.startsWith("/enrollment/");
|
|
199
|
-
const request = staffPath ? operatorRequest : cliRequest;
|
|
200
|
-
const requestPath = staffPath ? mapStaffCatalogPath(path) : mapCustomerCatalogPath(path);
|
|
201
|
-
if (init?.headers?.Accept === "text/event-stream") {
|
|
202
|
-
const session = await loadRequiredSession({ env });
|
|
203
|
-
const base = resolveApiBaseUrl({ env, session });
|
|
204
|
-
const parsed = new URL(
|
|
205
|
-
requestPath,
|
|
206
|
-
`${base.replace(/\/+$/, "")}/`,
|
|
207
|
-
);
|
|
208
|
-
const duration = Number(parsed.searchParams.get("duration") ?? "20") * 1000;
|
|
209
|
-
const maxEvents = Number(parsed.searchParams.get("max_events") ?? "40");
|
|
210
|
-
return collectSse(
|
|
211
|
-
parsed.toString(),
|
|
212
|
-
{
|
|
213
|
-
Accept: "text/event-stream",
|
|
214
|
-
Authorization: `Bearer ${session.accessToken}`,
|
|
215
|
-
"X-Ornn-Credential-Type": "user",
|
|
216
|
-
},
|
|
217
|
-
duration,
|
|
218
|
-
maxEvents,
|
|
219
|
-
fetchImpl,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
let body = init?.body;
|
|
223
|
-
if (
|
|
224
|
-
body &&
|
|
225
|
-
typeof body === "object" &&
|
|
226
|
-
typeof body.identity_file === "string" &&
|
|
227
|
-
body.identity_file.trim()
|
|
228
|
-
) {
|
|
229
|
-
const privateKey = readFileSync(body.identity_file.trim(), "utf8");
|
|
230
|
-
body = { ...body, private_key: privateKey };
|
|
231
|
-
delete body.identity_file;
|
|
232
|
-
}
|
|
233
|
-
return request({
|
|
234
|
-
endpoint: requestPath,
|
|
235
|
-
env,
|
|
236
|
-
fetchImpl,
|
|
237
|
-
method,
|
|
238
|
-
...(body !== undefined ? { body } : {}),
|
|
239
|
-
});
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export async function runCatalogCapability(capability, input, context) {
|
|
244
|
-
const { parseRole } = await loadCapabilities();
|
|
245
|
-
const role = parseRole(context.role) ?? (context.staff ? "reviewer" : "user");
|
|
246
|
-
return capability.execute(
|
|
247
|
-
{
|
|
248
|
-
actor: context.actor ?? "",
|
|
249
|
-
role,
|
|
250
|
-
confirmed: context.confirmed === true,
|
|
251
|
-
fetch: catalogFetch(context),
|
|
252
|
-
},
|
|
253
|
-
input,
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export async function listingsCreateCommand(args, context) {
|
|
258
|
-
const { createListingCapability } = await loadCapabilities();
|
|
259
|
-
const options = context.parseCommandOptions(
|
|
260
|
-
args,
|
|
261
|
-
{
|
|
262
|
-
boolean: ["json", "yes"],
|
|
263
|
-
value: [
|
|
264
|
-
"gpu-type",
|
|
265
|
-
"cpu",
|
|
266
|
-
"node-model",
|
|
267
|
-
"fabric-type",
|
|
268
|
-
"node-count",
|
|
269
|
-
"gpus-per-node",
|
|
270
|
-
"site-nickname",
|
|
271
|
-
"site-operator",
|
|
272
|
-
"ram",
|
|
273
|
-
"storage",
|
|
274
|
-
"available-start-at",
|
|
275
|
-
"available-end-at",
|
|
276
|
-
"buy-now-price-per-gpu-hour",
|
|
277
|
-
"cost-per-gpu-hour",
|
|
278
|
-
"deposit-percent",
|
|
279
|
-
],
|
|
280
|
-
},
|
|
281
|
-
"Usage: ornn listings create [--gpu-type H100] [--node-count 8] [--yes] [--json]",
|
|
282
|
-
);
|
|
283
|
-
const input = {
|
|
284
|
-
gpu_type: options.gpuType,
|
|
285
|
-
cpu: options.cpu,
|
|
286
|
-
node_model: options.nodeModel,
|
|
287
|
-
fabric_type: options.fabricType,
|
|
288
|
-
node_count: options.nodeCount != null ? Number(options.nodeCount) : undefined,
|
|
289
|
-
gpus_per_node: options.gpusPerNode != null ? Number(options.gpusPerNode) : undefined,
|
|
290
|
-
site_nickname: options.siteNickname,
|
|
291
|
-
site_operator: options.siteOperator,
|
|
292
|
-
ram: options.ram,
|
|
293
|
-
storage: options.storage,
|
|
294
|
-
available_start_at: options.availableStartAt,
|
|
295
|
-
available_end_at: options.availableEndAt,
|
|
296
|
-
buy_now_price_per_gpu_hour:
|
|
297
|
-
options.buyNowPricePerGpuHour != null ? Number(options.buyNowPricePerGpuHour) : undefined,
|
|
298
|
-
cost_per_gpu_hour: options.costPerGpuHour != null ? Number(options.costPerGpuHour) : undefined,
|
|
299
|
-
deposit_percent: options.depositPercent != null ? Number(options.depositPercent) : undefined,
|
|
300
|
-
};
|
|
301
|
-
const preview = await runCatalogCapability(createListingCapability, input, {
|
|
302
|
-
...context,
|
|
303
|
-
confirmed: false,
|
|
304
|
-
staff: true,
|
|
305
|
-
role: "reviewer",
|
|
306
|
-
});
|
|
307
|
-
if (preview?.ok === false) {
|
|
308
|
-
throw new Error(preview.error ?? "listing create failed");
|
|
309
|
-
}
|
|
310
|
-
const confirmed = await confirmCatalogMutation({
|
|
311
|
-
stdin: context.stdin,
|
|
312
|
-
stdout: context.stdout,
|
|
313
|
-
yes: options.yes,
|
|
314
|
-
label: "Create this listing?",
|
|
315
|
-
});
|
|
316
|
-
if (!confirmed) {
|
|
317
|
-
context.stdout.write("Aborted.\n");
|
|
318
|
-
return 1;
|
|
319
|
-
}
|
|
320
|
-
const result = await runCatalogCapability(createListingCapability, input, {
|
|
321
|
-
...context,
|
|
322
|
-
confirmed: true,
|
|
323
|
-
staff: true,
|
|
324
|
-
role: "reviewer",
|
|
325
|
-
});
|
|
326
|
-
if (options.json) {
|
|
327
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
328
|
-
} else {
|
|
329
|
-
context.stdout.write(`${JSON.stringify(result)}\n`);
|
|
330
|
-
}
|
|
331
|
-
return 0;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
export async function operatorListCommand(args, context) {
|
|
335
|
-
const { listOperatorsCapability } = await loadCapabilities();
|
|
336
|
-
context.parseCommandOptions(args, { boolean: ["json"] }, "Usage: ornn operator list [--json]");
|
|
337
|
-
const result = await runCatalogCapability(listOperatorsCapability, {}, {
|
|
338
|
-
...context,
|
|
339
|
-
staff: true,
|
|
340
|
-
role: "reviewer",
|
|
341
|
-
});
|
|
342
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
343
|
-
return 0;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export async function usersQueryCommand(args, context) {
|
|
347
|
-
const { usersQueryCapability } = await loadCapabilities();
|
|
348
|
-
const options = context.parseCommandOptions(
|
|
349
|
-
args,
|
|
350
|
-
{ boolean: ["json"], value: ["name", "email", "organization", "max"] },
|
|
351
|
-
"Usage: ornn users query [--name Ada] [--email a@b.com] [--organization Acme] [--max 20]",
|
|
352
|
-
);
|
|
353
|
-
const result = await runCatalogCapability(
|
|
354
|
-
usersQueryCapability,
|
|
355
|
-
{
|
|
356
|
-
name: options.name,
|
|
357
|
-
email: options.email,
|
|
358
|
-
organization: options.organization,
|
|
359
|
-
max: options.max != null ? Number(options.max) : undefined,
|
|
360
|
-
},
|
|
361
|
-
{ ...context, staff: true, role: "admin" },
|
|
362
|
-
);
|
|
363
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
364
|
-
return 0;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export async function telemetryLatestCommand(args, context) {
|
|
368
|
-
const { telemetryLatestCapability } = await loadCapabilities();
|
|
369
|
-
const [nodeId, ...rest] = args;
|
|
370
|
-
const options = context.parseCommandOptions(
|
|
371
|
-
rest,
|
|
372
|
-
{ boolean: ["json"], value: ["start", "end", "span", "max-points"] },
|
|
373
|
-
"Usage: ornn telemetry latest <node-id> [--span 15m] [--start <iso>] [--end <iso>] [--max-points <n>]",
|
|
374
|
-
);
|
|
375
|
-
const result = await runCatalogCapability(
|
|
376
|
-
telemetryLatestCapability,
|
|
377
|
-
{
|
|
378
|
-
node_id: nodeId,
|
|
379
|
-
start: options.start,
|
|
380
|
-
end: options.end,
|
|
381
|
-
span: options.span,
|
|
382
|
-
max_points: options.maxPoints != null ? Number(options.maxPoints) : undefined,
|
|
383
|
-
},
|
|
384
|
-
{ ...context, role: context.staff ? "reviewer" : "user" },
|
|
385
|
-
);
|
|
386
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
387
|
-
return 0;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
export async function logsLatestCommand(args, context) {
|
|
391
|
-
const { logsLatestCapability } = await loadCapabilities();
|
|
392
|
-
const [nodeId, ...rest] = args;
|
|
393
|
-
const options = context.parseCommandOptions(
|
|
394
|
-
rest,
|
|
395
|
-
{ boolean: ["json"], value: ["stream", "count", "before", "cursor"] },
|
|
396
|
-
"Usage: ornn logs latest <node-id> [--stream kernel] [--count 200] [--before <iso>] [--cursor <token>]",
|
|
397
|
-
);
|
|
398
|
-
const result = await runCatalogCapability(
|
|
399
|
-
logsLatestCapability,
|
|
400
|
-
{
|
|
401
|
-
node_id: nodeId,
|
|
402
|
-
stream: options.stream,
|
|
403
|
-
count: options.count != null ? Number(options.count) : undefined,
|
|
404
|
-
before: options.before,
|
|
405
|
-
cursor: options.cursor,
|
|
406
|
-
},
|
|
407
|
-
{ ...context, role: context.staff ? "reviewer" : "user" },
|
|
408
|
-
);
|
|
409
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
410
|
-
return 0;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export async function telemetryTailCommand(args, context) {
|
|
414
|
-
const { telemetryTailCapability } = await loadCapabilities();
|
|
415
|
-
const [nodeId, ...rest] = args;
|
|
416
|
-
const options = context.parseCommandOptions(
|
|
417
|
-
rest,
|
|
418
|
-
{ boolean: ["json"], value: ["duration", "max-events", "fields"] },
|
|
419
|
-
"Usage: ornn telemetry tail <node-id> [--duration 20] [--max-events 40]",
|
|
420
|
-
);
|
|
421
|
-
const result = await runCatalogCapability(
|
|
422
|
-
telemetryTailCapability,
|
|
423
|
-
{
|
|
424
|
-
node_id: nodeId,
|
|
425
|
-
duration: options.duration != null ? Number(options.duration) : undefined,
|
|
426
|
-
max_events: options.maxEvents != null ? Number(options.maxEvents) : undefined,
|
|
427
|
-
fields: options.fields,
|
|
428
|
-
},
|
|
429
|
-
{ ...context, role: context.staff ? "reviewer" : "user" },
|
|
430
|
-
);
|
|
431
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
432
|
-
return 0;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
export async function logsTailCommand(args, context) {
|
|
436
|
-
const { logsTailCapability } = await loadCapabilities();
|
|
437
|
-
const [nodeId, ...rest] = args;
|
|
438
|
-
const options = context.parseCommandOptions(
|
|
439
|
-
rest,
|
|
440
|
-
{ boolean: ["json"], value: ["stream", "duration", "max-events"] },
|
|
441
|
-
"Usage: ornn logs tail <node-id> [--stream kernel] [--duration 20]",
|
|
442
|
-
);
|
|
443
|
-
const result = await runCatalogCapability(
|
|
444
|
-
logsTailCapability,
|
|
445
|
-
{
|
|
446
|
-
node_id: nodeId,
|
|
447
|
-
stream: options.stream,
|
|
448
|
-
duration: options.duration != null ? Number(options.duration) : undefined,
|
|
449
|
-
max_events: options.maxEvents != null ? Number(options.maxEvents) : undefined,
|
|
450
|
-
},
|
|
451
|
-
{ ...context, role: context.staff ? "reviewer" : "user" },
|
|
452
|
-
);
|
|
453
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
454
|
-
return 0;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
export async function tokensListCommand(args, context) {
|
|
458
|
-
const { tokensListCapability } = await loadCapabilities();
|
|
459
|
-
context.parseCommandOptions(args, { boolean: ["json"] }, "Usage: ornn tokens list [--json]");
|
|
460
|
-
const result = await runCatalogCapability(tokensListCapability, {}, {
|
|
461
|
-
...context,
|
|
462
|
-
staff: true,
|
|
463
|
-
role: "reviewer",
|
|
464
|
-
});
|
|
465
|
-
const rows = Array.isArray(result) ? result : (result?.tokens ?? []);
|
|
466
|
-
if (args.includes("--json")) {
|
|
467
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
468
|
-
} else if (!rows.length) {
|
|
469
|
-
context.stdout.write("No enrollment tokens found.\n");
|
|
470
|
-
} else {
|
|
471
|
-
for (const token of rows) {
|
|
472
|
-
context.stdout.write(
|
|
473
|
-
`${token?.id ?? "unknown"} ${token?.status ?? ""} operator=${token?.operator_id ?? ""} expires=${token?.expires_at ?? ""}\n`,
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return 0;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export async function tokensCreateCommand(input, context) {
|
|
481
|
-
const { tokensCreateCapability } = await loadCapabilities();
|
|
482
|
-
const preview = await runCatalogCapability(tokensCreateCapability, input, {
|
|
483
|
-
...context,
|
|
484
|
-
confirmed: false,
|
|
485
|
-
staff: true,
|
|
486
|
-
role: "admin",
|
|
487
|
-
});
|
|
488
|
-
if (preview?.ok === false) {
|
|
489
|
-
throw new Error(preview.error ?? "token create failed");
|
|
490
|
-
}
|
|
491
|
-
const confirmed = await confirmCatalogMutation({
|
|
492
|
-
stdin: context.stdin,
|
|
493
|
-
stdout: context.stdout,
|
|
494
|
-
yes: input.yes === true,
|
|
495
|
-
label: input.host || input.hosts?.length ? "Mint token and SSH enroll these hosts?" : "Mint this enrollment token?",
|
|
496
|
-
});
|
|
497
|
-
if (!confirmed) {
|
|
498
|
-
context.stdout.write("Aborted.\n");
|
|
499
|
-
return 1;
|
|
500
|
-
}
|
|
501
|
-
const result = await runCatalogCapability(tokensCreateCapability, input, {
|
|
502
|
-
...context,
|
|
503
|
-
confirmed: true,
|
|
504
|
-
staff: true,
|
|
505
|
-
role: "admin",
|
|
506
|
-
});
|
|
507
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
508
|
-
return 0;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
async function runNodeLifecycle(capabilityName, input, context, label) {
|
|
512
|
-
const caps = await loadCapabilities();
|
|
513
|
-
const capability = caps[capabilityName];
|
|
514
|
-
const preview = await runCatalogCapability(capability, input, {
|
|
515
|
-
...context,
|
|
516
|
-
confirmed: false,
|
|
517
|
-
staff: true,
|
|
518
|
-
role: "admin",
|
|
519
|
-
});
|
|
520
|
-
if (preview?.ok === false) {
|
|
521
|
-
throw new Error(preview.error ?? `${capabilityName} failed`);
|
|
522
|
-
}
|
|
523
|
-
const confirmed = await confirmCatalogMutation({
|
|
524
|
-
stdin: context.stdin,
|
|
525
|
-
stdout: context.stdout,
|
|
526
|
-
yes: input.yes === true,
|
|
527
|
-
label,
|
|
528
|
-
});
|
|
529
|
-
if (!confirmed) {
|
|
530
|
-
context.stdout.write("Aborted.\n");
|
|
531
|
-
return 1;
|
|
532
|
-
}
|
|
533
|
-
const result = await runCatalogCapability(capability, input, {
|
|
534
|
-
...context,
|
|
535
|
-
confirmed: true,
|
|
536
|
-
staff: true,
|
|
537
|
-
role: "admin",
|
|
538
|
-
});
|
|
539
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
540
|
-
return 0;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
export async function nodeOffGridCommand(args, context) {
|
|
544
|
-
const [nodeId, ...rest] = args;
|
|
545
|
-
if (!nodeId) throw new Error("Usage: ornn node off-grid <node-id> [--reason <text>] [--yes] [--json]");
|
|
546
|
-
const options = context.parseCommandOptions(
|
|
547
|
-
rest,
|
|
548
|
-
{ boolean: ["json", "yes"], value: ["reason"] },
|
|
549
|
-
"Usage: ornn node off-grid <node-id> [--reason <text>] [--yes] [--json]",
|
|
550
|
-
);
|
|
551
|
-
return runNodeLifecycle(
|
|
552
|
-
"nodeOffGridCapability",
|
|
553
|
-
{ node_id: nodeId, reason: options.reason, yes: options.yes === true },
|
|
554
|
-
context,
|
|
555
|
-
`Take node ${nodeId} off-grid?`,
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
export async function nodeOnGridCommand(args, context) {
|
|
560
|
-
const [nodeId, ...rest] = args;
|
|
561
|
-
if (!nodeId) throw new Error("Usage: ornn node on-grid <node-id> [--yes] [--json]");
|
|
562
|
-
const options = context.parseCommandOptions(rest, { boolean: ["json", "yes"] }, "Usage: ornn node on-grid <node-id> [--yes] [--json]");
|
|
563
|
-
return runNodeLifecycle("nodeOnGridCapability", { node_id: nodeId, yes: options.yes === true }, context, `Re-enroll node ${nodeId} on-grid?`);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
export async function nodeExecCommand(args, context) {
|
|
567
|
-
const [nodeId, ...rest] = args;
|
|
568
|
-
if (!nodeId) {
|
|
569
|
-
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]");
|
|
570
|
-
}
|
|
571
|
-
const options = context.parseCommandOptions(
|
|
572
|
-
rest,
|
|
573
|
-
{ boolean: ["json", "yes"], value: ["command", "timeout-seconds"] },
|
|
574
|
-
"Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]",
|
|
575
|
-
);
|
|
576
|
-
const command = String(options.command ?? "").trim();
|
|
577
|
-
const timeoutSeconds = Number(options.timeoutSeconds);
|
|
578
|
-
if (!command || !Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
579
|
-
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]");
|
|
580
|
-
}
|
|
581
|
-
const { nodeExecCapability } = await loadCapabilities();
|
|
582
|
-
const result = await runCatalogCapability(
|
|
583
|
-
nodeExecCapability,
|
|
584
|
-
{ node_id: nodeId, command, timeout_seconds: timeoutSeconds },
|
|
585
|
-
{ ...context, staff: true, role: "admin" },
|
|
586
|
-
);
|
|
587
|
-
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
588
|
-
return 0;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
export async function nodeTerminateCommand(args, context) {
|
|
592
|
-
const [nodeId, ...rest] = args;
|
|
593
|
-
if (!nodeId) throw new Error("Usage: ornn node terminate <node-id> [--reason <text>] [--force] [--yes] [--json]");
|
|
594
|
-
const options = context.parseCommandOptions(
|
|
595
|
-
rest,
|
|
596
|
-
{ boolean: ["json", "yes", "force"], value: ["reason"] },
|
|
597
|
-
"Usage: ornn node terminate <node-id> [--reason <text>] [--force] [--yes] [--json]",
|
|
598
|
-
);
|
|
599
|
-
return runNodeLifecycle(
|
|
600
|
-
"nodeTerminateCapability",
|
|
601
|
-
{ node_id: nodeId, reason: options.reason, force: options.force === true, yes: options.yes === true },
|
|
602
|
-
context,
|
|
603
|
-
`Terminate node ${nodeId}?`,
|
|
604
|
-
);
|
|
605
|
-
}
|
package/src/color.mjs
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// SGR open/close pairs for the four styles the CLI uses.
|
|
2
|
-
const CODES = { red: [31, 39], green: [32, 39], bold: [1, 22], dim: [2, 22] };
|
|
3
|
-
|
|
4
|
-
function createColors(enabled) {
|
|
5
|
-
return Object.fromEntries(
|
|
6
|
-
Object.entries(CODES).map(([name, [open, close]]) => [
|
|
7
|
-
name,
|
|
8
|
-
enabled ? (text) => `\x1b[${open}m${text}\x1b[${close}m` : (text) => text,
|
|
9
|
-
])
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const NO_COLOR_FLAG = "--no-color";
|
|
14
|
-
|
|
15
|
-
// Structured-error label prefixes that get dim/bold styling. Matched at the
|
|
16
|
-
// start of a line; the remainder of the line (the value) is left untouched.
|
|
17
|
-
const LABEL_PREFIX_RE = /^(Code|Reason|Next steps?):/;
|
|
18
|
-
|
|
19
|
-
// `--no-color` is global: strip it anywhere in argv so per-command parsing
|
|
20
|
-
// never sees it as an unknown option.
|
|
21
|
-
export function extractNoColorFlag(argv) {
|
|
22
|
-
return {
|
|
23
|
-
argv: argv.filter((arg) => arg !== NO_COLOR_FLAG),
|
|
24
|
-
noColor: argv.includes(NO_COLOR_FLAG),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function streamSupportsColor(stream, env, noColorFlag) {
|
|
29
|
-
if (noColorFlag || Object.hasOwn(env ?? {}, "NO_COLOR")) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
if (Object.hasOwn(env ?? {}, "FORCE_COLOR")) {
|
|
33
|
-
return String(env.FORCE_COLOR) !== "0";
|
|
34
|
-
}
|
|
35
|
-
return Boolean(stream.isTTY);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// stdout and stderr are evaluated independently: one can be a TTY while the
|
|
39
|
-
// other is piped.
|
|
40
|
-
export function attachStreamColors({ env, noColorFlag, stdout, stderr }) {
|
|
41
|
-
stdout.pc = createColors(streamSupportsColor(stdout, env, noColorFlag));
|
|
42
|
-
stderr.pc = createColors(streamSupportsColor(stderr, env, noColorFlag));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function writeColored(stream, variant, text) {
|
|
46
|
-
const colorize = stream.pc?.[variant];
|
|
47
|
-
stream.write(`${colorize ? colorize(text) : text}\n`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// First line is the error summary (red); later `Code:`/`Reason:`/`Next step:`
|
|
51
|
-
// label prefixes are dim/bold, their values left plain.
|
|
52
|
-
export function colorizeErrorMessage(message, stream) {
|
|
53
|
-
const pc = stream?.pc;
|
|
54
|
-
if (!pc) {
|
|
55
|
-
return message;
|
|
56
|
-
}
|
|
57
|
-
return message
|
|
58
|
-
.split("\n")
|
|
59
|
-
.map((line, index) => {
|
|
60
|
-
if (index === 0) {
|
|
61
|
-
return line ? pc.red(line) : line;
|
|
62
|
-
}
|
|
63
|
-
const match = line.match(LABEL_PREFIX_RE);
|
|
64
|
-
if (!match) {
|
|
65
|
-
return line;
|
|
66
|
-
}
|
|
67
|
-
const label = match[0];
|
|
68
|
-
return `${pc.bold(pc.dim(label))}${line.slice(label.length)}`;
|
|
69
|
-
})
|
|
70
|
-
.join("\n");
|
|
71
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare const listOperatorsCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
2
|
-
export declare const listFacilitiesCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
3
|
-
export declare const listNodesCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
4
|
-
export declare const nodeOffGridCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
5
|
-
export declare const nodeOnGridCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
6
|
-
export declare const nodeExecCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
7
|
-
export declare const nodeTerminateCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
8
|
-
export declare const fleetCapabilities: import("../capability.ts").Capability<unknown, unknown>[];
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export declare const whoamiCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
2
|
-
export declare const statusCapability: import("../capability.ts").Capability<unknown, unknown>;
|
|
3
|
-
export declare const identityCapabilities: import("../capability.ts").Capability<unknown, unknown>[];
|