@ornncompute/cli 0.2.5 → 0.2.7
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 +19 -10
- package/package.json +1 -1
- package/src/catalog-dispatch.mjs +10 -8
- package/src/cli.mjs +2 -2
- package/vendor/capabilities/capabilities/clusters.js +3 -3
- package/vendor/capabilities/capabilities/fleet.js +10 -11
- package/vendor/capabilities/capabilities/listings.js +2 -2
- package/vendor/capabilities/capabilities/tokens.js +2 -2
- package/vendor/capabilities/capability.d.ts +18 -0
- package/vendor/capabilities/capability.js +12 -0
- package/vendor/capabilities/index.d.ts +1 -1
- package/vendor/capabilities/index.js +1 -1
package/README.md
CHANGED
|
@@ -72,12 +72,12 @@ ornn nodes keys attach <node-id> --key <path|id|label> [--json]
|
|
|
72
72
|
ornn nodes keys list <node-id> [--json]
|
|
73
73
|
ornn node health <node-id> [--json]
|
|
74
74
|
ornn node diagnose <node-id> [--json]
|
|
75
|
-
ornn node list [--operator <id-or-slug>] [--facility <id>] [--json]
|
|
75
|
+
ornn node list [--operator <id-or-slug>] [--facility <id>] [--email <email>] [--json]
|
|
76
76
|
ornn node reboot <node-id> [--json]
|
|
77
77
|
ornn node hard-reset <node-id> [--json]
|
|
78
78
|
ornn node off-grid <node-id> [--reason <text>] [--yes] [--json]
|
|
79
79
|
ornn node on-grid <node-id> [--yes] [--json]
|
|
80
|
-
ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
80
|
+
ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]
|
|
81
81
|
ornn node terminate <node-id> [--reason <text>] [--force] [--yes] [--json]
|
|
82
82
|
ornn node admin-key <node-id> [--generate] [--json]
|
|
83
83
|
ornn reservations commerce list --tenant <email-or-id> [--listing <listing-id>] [--json]
|
|
@@ -138,6 +138,14 @@ ornn storage buckets connect gcs|s3|r2 --bucket <bucket>|--url <url> [--name <na
|
|
|
138
138
|
ornn storage buckets update-credentials <drive-id> --access-key-id <id> --secret-access-key-file <path> [--json]
|
|
139
139
|
ornn storage buckets verify <drive-id> [--json]
|
|
140
140
|
ornn storage buckets disconnect <drive-id> [--json]
|
|
141
|
+
ornn storage filesystem deploy --reservation <reservation-id> [--performance-tier <tier>] [--capacity-gib <gib>] [--json]
|
|
142
|
+
ornn storage filesystem status --reservation <reservation-id> [--json]
|
|
143
|
+
ornn storage filesystem delete --reservation <reservation-id> [--json]
|
|
144
|
+
ornn telemetry latest <node-id> [--span 15m] [--start <iso>] [--end <iso>] [--max-points <n>] [--json]
|
|
145
|
+
ornn telemetry tail <node-id> [--duration <seconds>] [--json]
|
|
146
|
+
ornn logs latest <node-id> [--stream kernel] [--count <n>] [--before <iso>] [--cursor <token>] [--json]
|
|
147
|
+
ornn logs tail <node-id> [--stream kernel] [--duration <seconds>] [--json]
|
|
148
|
+
ornn users query [--name <name>] [--email <email>] [--organization <name>] [--max <n>]
|
|
141
149
|
ornn keys list [--json]
|
|
142
150
|
ornn keys add [<public-key-file>] --label <label> [--public-key <key>] [--json]
|
|
143
151
|
ornn keys delete <key-id> [--json]
|
|
@@ -236,21 +244,22 @@ path.
|
|
|
236
244
|
- `ornn node health <node-id>` shows launch readiness (ready/blocked plus
|
|
237
245
|
reasons), passive fault reasons, and the last validation status.
|
|
238
246
|
- `ornn node diagnose <node-id>` queues a manual node validation test.
|
|
239
|
-
- `ornn node list [--operator <id-or-slug>] [--facility <id>]` lists GPU nodes
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
- `ornn node reboot <node-id>` queues an OS reboot of the
|
|
243
|
-
(
|
|
247
|
+
- `ornn node list [--operator <id-or-slug>] [--facility <id>] [--email <email>]` lists GPU nodes.
|
|
248
|
+
Orchestrator applies facility id and an operator UUID. Non-UUID `--operator`
|
|
249
|
+
slugs and `--email` are forwarded today and do not filter the fleet.
|
|
250
|
+
- `ornn node reboot <node-id>` queues an OS reboot of the node's resolved
|
|
251
|
+
instance (its host for bare-metal, its VM for a virtual-machine instance id;
|
|
252
|
+
storage and SSH keys preserved).
|
|
244
253
|
- `ornn node hard-reset <node-id>` wipes tenant storage/users and reboots, then
|
|
245
254
|
re-pushes the tenant's keys on reconnect. The tenant keeps ownership of the
|
|
246
|
-
|
|
255
|
+
instance.
|
|
247
256
|
- `ornn node off-grid <node-id> [--reason <text>] [--yes]` takes a node off-grid
|
|
248
257
|
(remove Ornn agent management, leave the host powered on). Asks y/N unless `--yes`.
|
|
249
258
|
- `ornn node on-grid <node-id> [--yes]` re-enrolls an off-grid host: orchestrator
|
|
250
259
|
SSHs with the stored admin key and re-runs the installer. Asks y/N unless `--yes`.
|
|
251
|
-
- `ornn node exec <node-id> --command <shell> --timeout-seconds <n
|
|
260
|
+
- `ornn node exec <node-id> --command <shell> --timeout-seconds <n>` runs
|
|
252
261
|
one unrestricted command over orchestrator SSH with the stored admin key. Not
|
|
253
|
-
an interactive shell (`ornn ssh`).
|
|
262
|
+
an interactive shell (`ornn ssh`). Runs immediately; no y/N.
|
|
254
263
|
- `ornn node terminate <node-id> [--reason <text>] [--force] [--yes]` best-effort
|
|
255
264
|
cleans the host, removes the Ornn agent, and hard-deletes the node record.
|
|
256
265
|
It does not return the host to a tenant. Asks y/N unless `--yes`. There is no
|
package/package.json
CHANGED
package/src/catalog-dispatch.mjs
CHANGED
|
@@ -468,24 +468,26 @@ export async function nodeOnGridCommand(args, context) {
|
|
|
468
468
|
export async function nodeExecCommand(args, context) {
|
|
469
469
|
const [nodeId, ...rest] = args;
|
|
470
470
|
if (!nodeId) {
|
|
471
|
-
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
471
|
+
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]");
|
|
472
472
|
}
|
|
473
473
|
const options = context.parseCommandOptions(
|
|
474
474
|
rest,
|
|
475
475
|
{ boolean: ["json", "yes"], value: ["command", "timeout-seconds"] },
|
|
476
|
-
"Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
476
|
+
"Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]",
|
|
477
477
|
);
|
|
478
478
|
const command = String(options.command ?? "").trim();
|
|
479
479
|
const timeoutSeconds = Number(options.timeoutSeconds);
|
|
480
480
|
if (!command || !Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
481
|
-
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
481
|
+
throw new Error("Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]");
|
|
482
482
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
483
|
+
const { nodeExecCapability } = await loadCapabilities();
|
|
484
|
+
const result = await runCatalogCapability(
|
|
485
|
+
nodeExecCapability,
|
|
486
|
+
{ node_id: nodeId, command, timeout_seconds: timeoutSeconds },
|
|
487
|
+
{ ...context, staff: true, role: "admin" },
|
|
488
488
|
);
|
|
489
|
+
context.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
490
|
+
return 0;
|
|
489
491
|
}
|
|
490
492
|
|
|
491
493
|
export async function nodeTerminateCommand(args, context) {
|
package/src/cli.mjs
CHANGED
|
@@ -159,7 +159,7 @@ Usage:
|
|
|
159
159
|
ornn node hard-reset <node-id> [--json]
|
|
160
160
|
ornn node off-grid <node-id> [--reason <text>] [--yes] [--json]
|
|
161
161
|
ornn node on-grid <node-id> [--yes] [--json]
|
|
162
|
-
ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
162
|
+
ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]
|
|
163
163
|
ornn node terminate <node-id> [--reason <text>] [--force] [--yes] [--json]
|
|
164
164
|
ornn node admin-key <node-id> [--generate] [--json]
|
|
165
165
|
ornn operators list [--json]
|
|
@@ -2195,7 +2195,7 @@ function nodeOpUsage(subcommand) {
|
|
|
2195
2195
|
return "Usage: ornn node on-grid <node-id> [--yes] [--json]";
|
|
2196
2196
|
}
|
|
2197
2197
|
if (subcommand === "exec") {
|
|
2198
|
-
return "Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--
|
|
2198
|
+
return "Usage: ornn node exec <node-id> --command <shell> --timeout-seconds <n> [--json]";
|
|
2199
2199
|
}
|
|
2200
2200
|
if (subcommand === "terminate") {
|
|
2201
2201
|
return "Usage: ornn node terminate <node-id> [--reason <text>] [--force] [--yes] [--json]";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { defineCapability } from "../capability.js";
|
|
2
|
+
import { defineCapability, preview } from "../capability.js";
|
|
3
3
|
const reservationInput = z.object({
|
|
4
4
|
reservation_id: z.string().min(1),
|
|
5
5
|
reservationId: z.string().optional(),
|
|
@@ -51,7 +51,7 @@ function clusterPair(kind, domain) {
|
|
|
51
51
|
return { ok: false, error: "other_cluster", reason: "other_cluster" };
|
|
52
52
|
}
|
|
53
53
|
if (!ctx.confirmed) {
|
|
54
|
-
return
|
|
54
|
+
return preview(`${kind}.launch`, { reservation_id: id });
|
|
55
55
|
}
|
|
56
56
|
return ctx.fetch(clusterPath(kind, id, "cluster/launch"), {
|
|
57
57
|
method: "POST",
|
|
@@ -75,7 +75,7 @@ function clusterPair(kind, domain) {
|
|
|
75
75
|
const parsed = reservationInput.parse(input);
|
|
76
76
|
const id = reservationId(parsed);
|
|
77
77
|
if (!ctx.confirmed) {
|
|
78
|
-
return
|
|
78
|
+
return preview(`${kind}.teardown`, { reservation_id: id });
|
|
79
79
|
}
|
|
80
80
|
return ctx.fetch(clusterPath(kind, id, "cluster/teardown"), { method: "POST" });
|
|
81
81
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { defineCapability } from "../capability.js";
|
|
2
|
+
import { defineCapability, preview } from "../capability.js";
|
|
3
3
|
function queryPath(path, params) {
|
|
4
4
|
const parts = [];
|
|
5
5
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -138,7 +138,7 @@ export const nodeOffGridCapability = defineCapability({
|
|
|
138
138
|
const id = nodeIdOf(parsed);
|
|
139
139
|
const body = { ...(parsed.reason ? { reason: parsed.reason } : {}) };
|
|
140
140
|
if (!ctx.confirmed) {
|
|
141
|
-
return
|
|
141
|
+
return preview("node.off-grid", { node_id: id, ...body });
|
|
142
142
|
}
|
|
143
143
|
return ctx.fetch(`/internal/nodes/${encodeURIComponent(id)}/off-grid`, {
|
|
144
144
|
method: "POST",
|
|
@@ -167,7 +167,7 @@ export const nodeOnGridCapability = defineCapability({
|
|
|
167
167
|
const parsed = nodeLifecycleInput.parse(input);
|
|
168
168
|
const id = nodeIdOf(parsed);
|
|
169
169
|
if (!ctx.confirmed) {
|
|
170
|
-
return
|
|
170
|
+
return preview("node.on-grid", { node_id: id });
|
|
171
171
|
}
|
|
172
172
|
return ctx.fetch(`/internal/nodes/${encodeURIComponent(id)}/on-grid`, {
|
|
173
173
|
method: "POST",
|
|
@@ -179,8 +179,8 @@ export const nodeExecCapability = defineCapability({
|
|
|
179
179
|
id: "node.exec",
|
|
180
180
|
domain: "fleet",
|
|
181
181
|
roles: ["admin"],
|
|
182
|
-
description: "Admin-only. Run one unrestricted command on an enrolled node over orchestrator SSH with the stored admin key. Always pass timeout_seconds. Not an interactive shell. On NVIDIA hosts use nvidia-smi; on AMD Instinct use amd-smi.
|
|
183
|
-
mutation: "
|
|
182
|
+
description: "Admin-only. Run one unrestricted command on an enrolled node over orchestrator SSH with the stored admin key. Always pass timeout_seconds. Not an interactive shell. On NVIDIA hosts use nvidia-smi; on AMD Instinct use amd-smi. Runs on the tool call — no confirm, no y/N.",
|
|
183
|
+
mutation: "read",
|
|
184
184
|
http: { method: "POST", path: "/internal/remote" },
|
|
185
185
|
slack: {
|
|
186
186
|
parameters: {
|
|
@@ -201,11 +201,10 @@ export const nodeExecCapability = defineCapability({
|
|
|
201
201
|
const parsed = nodeExecInput.parse(input);
|
|
202
202
|
const id = nodeIdOf(parsed);
|
|
203
203
|
const timeoutSeconds = parsed.timeout_seconds ?? parsed.timeoutSeconds;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
return ctx.fetch("/internal/remote", { method: "POST", body: payload });
|
|
204
|
+
return ctx.fetch("/internal/remote", {
|
|
205
|
+
method: "POST",
|
|
206
|
+
body: { node_id: id, command: parsed.command, timeout_seconds: timeoutSeconds },
|
|
207
|
+
});
|
|
209
208
|
},
|
|
210
209
|
});
|
|
211
210
|
export const nodeTerminateCapability = defineCapability({
|
|
@@ -235,7 +234,7 @@ export const nodeTerminateCapability = defineCapability({
|
|
|
235
234
|
...(parsed.force !== undefined ? { force: parsed.force } : {}),
|
|
236
235
|
};
|
|
237
236
|
if (!ctx.confirmed) {
|
|
238
|
-
return
|
|
237
|
+
return preview("node.terminate", { node_id: id, ...body });
|
|
239
238
|
}
|
|
240
239
|
return ctx.fetch(`/internal/nodes/${encodeURIComponent(id)}/terminate`, {
|
|
241
240
|
method: "POST",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { defineCapability } from "../capability.js";
|
|
2
|
+
import { defineCapability, preview } from "../capability.js";
|
|
3
3
|
import { bindHardwareFields, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, projectCatalogTerms, } from "../spec-catalog.js";
|
|
4
4
|
export const listingCreateInput = z.object({
|
|
5
5
|
kind: z.enum(["gpu", "cpu", "node", "link"]).optional(),
|
|
@@ -147,7 +147,7 @@ export const createListingCapability = defineCapability({
|
|
|
147
147
|
return bound;
|
|
148
148
|
const payload = { backing_mode: "forward", ...bound.payload };
|
|
149
149
|
if (!ctx.confirmed) {
|
|
150
|
-
return
|
|
150
|
+
return preview("create_listing", payload);
|
|
151
151
|
}
|
|
152
152
|
return ctx.fetch("/inventory", { method: "POST", body: payload });
|
|
153
153
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { defineCapability } from "../capability.js";
|
|
2
|
+
import { defineCapability, preview } from "../capability.js";
|
|
3
3
|
const optionalText = z.string().nullish();
|
|
4
4
|
const tokenCreateInput = z.object({
|
|
5
5
|
operator_id: z.string().min(1),
|
|
@@ -107,7 +107,7 @@ export const tokensCreateCapability = defineCapability({
|
|
|
107
107
|
remote: hosts.length > 0 && privateKey.length > 0,
|
|
108
108
|
};
|
|
109
109
|
if (!ctx.confirmed) {
|
|
110
|
-
return
|
|
110
|
+
return preview("tokens.create", { ...payload, private_key: privateKey ? "[redacted]" : "" });
|
|
111
111
|
}
|
|
112
112
|
const minted = (await ctx.fetch("/internal/enrollment-tokens", {
|
|
113
113
|
method: "POST",
|
|
@@ -19,6 +19,24 @@ export type CapabilityContext = {
|
|
|
19
19
|
/** Human (or MCP) confirmed a preview-confirm mutation. Execute never POSTs without this. */
|
|
20
20
|
confirmed?: boolean;
|
|
21
21
|
};
|
|
22
|
+
/** The un-executed result of a `preview-confirm` capability. */
|
|
23
|
+
export type PreviewResult = {
|
|
24
|
+
status: "preview";
|
|
25
|
+
requiresConfirm: true;
|
|
26
|
+
confirmed: false;
|
|
27
|
+
action: string;
|
|
28
|
+
payload: unknown;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Build a preview-confirm capability's un-executed result.
|
|
32
|
+
*
|
|
33
|
+
* Previews used to return `{ ok: true, action, payload }`, which is
|
|
34
|
+
* indistinguishable from a completed mutation to anyone skimming the JSON --
|
|
35
|
+
* a human, or an LLM deciding whether its call took effect. ORN-3417 recorded
|
|
36
|
+
* a live `node.off-grid` preview as a confirm-gate failure for exactly that
|
|
37
|
+
* reason. `status: "preview"` states it outright, and carries no `ok`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function preview(action: string, payload: unknown): PreviewResult;
|
|
22
40
|
export type JsonSchema = {
|
|
23
41
|
type: "object";
|
|
24
42
|
properties?: Record<string, unknown>;
|
|
@@ -16,6 +16,18 @@ export const DOMAINS = [
|
|
|
16
16
|
"billing",
|
|
17
17
|
"observability",
|
|
18
18
|
];
|
|
19
|
+
/**
|
|
20
|
+
* Build a preview-confirm capability's un-executed result.
|
|
21
|
+
*
|
|
22
|
+
* Previews used to return `{ ok: true, action, payload }`, which is
|
|
23
|
+
* indistinguishable from a completed mutation to anyone skimming the JSON --
|
|
24
|
+
* a human, or an LLM deciding whether its call took effect. ORN-3417 recorded
|
|
25
|
+
* a live `node.off-grid` preview as a confirm-gate failure for exactly that
|
|
26
|
+
* reason. `status: "preview"` states it outright, and carries no `ok`.
|
|
27
|
+
*/
|
|
28
|
+
export function preview(action, payload) {
|
|
29
|
+
return { status: "preview", requiresConfirm: true, confirmed: false, action, payload };
|
|
30
|
+
}
|
|
19
31
|
/** Fill CLI/MCP/Slack names from `id`. Slack parameters stay optional. */
|
|
20
32
|
export function defineCapability(spec) {
|
|
21
33
|
const names = namesFromId(spec.id);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { byCliCommand, byMcpName, bySlackName, defineCapability, forRole, slackStaffVisible, visibleToClient, type Capability, type CapabilityBindings, type CapabilityContext, type CapabilitySpec, type JsonSchema, type CapabilityTransport, type Domain, type HttpSpec, type Mutation, } from "./capability.ts";
|
|
1
|
+
export { byCliCommand, byMcpName, bySlackName, defineCapability, forRole, preview, slackStaffVisible, visibleToClient, type Capability, type CapabilityBindings, type CapabilityContext, type CapabilitySpec, type JsonSchema, type CapabilityTransport, type Domain, type HttpSpec, type Mutation, type PreviewResult, } from "./capability.ts";
|
|
2
2
|
export { namesFromId } from "./names.ts";
|
|
3
3
|
export { catalog } from "./catalog.ts";
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.ts";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { byCliCommand, byMcpName, bySlackName, defineCapability, forRole, slackStaffVisible, visibleToClient, } from "./capability.js";
|
|
1
|
+
export { byCliCommand, byMcpName, bySlackName, defineCapability, forRole, preview, slackStaffVisible, visibleToClient, } from "./capability.js";
|
|
2
2
|
export { namesFromId } from "./names.js";
|
|
3
3
|
export { catalog } from "./catalog.js";
|
|
4
4
|
export { fleetCapabilities, listFacilitiesCapability, listNodesCapability, listOperatorsCapability, nodeExecCapability, nodeOffGridCapability, nodeOnGridCapability, nodeTerminateCapability, } from "./capabilities/fleet.js";
|