@ornncompute/cli 0.1.6 → 0.1.8
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 +98 -36
- package/package.json +1 -1
- package/src/api-client.mjs +25 -0
- package/src/cli.mjs +826 -129
package/README.md
CHANGED
|
@@ -56,11 +56,14 @@ ornn nodes keys list <node-id> [--json]
|
|
|
56
56
|
ornn node health <node-id> [--json]
|
|
57
57
|
ornn node diagnose <node-id> [--json]
|
|
58
58
|
ornn node deenroll <node-id> [--reason <text>] [--keep-record] [--json]
|
|
59
|
-
ornn fleet clean <ip>... --operator <id-or-slug> --ib-island <name> --identity-file <path> --dry-run [--
|
|
60
|
-
ornn fleet clean <
|
|
61
|
-
ornn fleet clean <fleet-id> --identity-file <path> --confirm-clean <plan-hash> [--
|
|
59
|
+
ornn fleet clean <ip>... --operator <id-or-slug> --ib-island <name> --identity-file <path> --dry-run [--policy <path>] [--policy-out <path>] [--ssh-user ubuntu|admin|ornn] [--json]
|
|
60
|
+
ornn fleet clean <fleet-id> --identity-file <path> --dry-run [--policy <path>] [--policy-out <path>] [--json]
|
|
61
|
+
ornn fleet clean <fleet-id> --identity-file <path> --confirm-clean <plan-hash> [--json]
|
|
62
62
|
ornn fleet enroll <fleet-id> --identity-file <path> [--confirm-takeover <ip[,ip...]>] [--json]
|
|
63
|
-
ornn fleet
|
|
63
|
+
ornn fleet delete <fleet-id> --confirm-delete <fleet-id> [--json]
|
|
64
|
+
ornn reservations commerce list --tenant <email-or-id> [--fleet <fleet-id>] [--json]
|
|
65
|
+
ornn reservations commerce create --tenant <email> --listing <listing-id> --fleet <fleet-id> [--start-at <timestamp>] [--end-at <timestamp>] [--price-per-gpu-hour <rate>] [--json]
|
|
66
|
+
ornn fleet deploy <fleet-id> --tenant <contact-email> [--user <email-or-id>] --commerce-reservation <id> [--network public|private] [--json]
|
|
64
67
|
ornn ssh <node-or-reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
|
|
65
68
|
ornn metrics nodes [--json]
|
|
66
69
|
ornn metrics node <node-id> [--json]
|
|
@@ -103,9 +106,9 @@ ornn storage volumes create --name <name> [--source <drive-id>] [--json]
|
|
|
103
106
|
ornn storage volumes refresh <drive-id> [--json]
|
|
104
107
|
ornn storage volumes clear <drive-id> [--json]
|
|
105
108
|
ornn storage volumes delete <drive-id> [--json]
|
|
106
|
-
ornn storage
|
|
109
|
+
ornn storage files upload <drive-id> <local-file> [--destination <path>] [--content-type <type>] [--json]
|
|
110
|
+
ornn storage deploy <drive-id> --reservation <reservation-id> [--all-nodes] [--mount-path <path>] [--read-only|--read-write] [--json]
|
|
107
111
|
ornn storage deploy status --reservation <reservation-id> [--json]
|
|
108
|
-
ornn storage unmount --reservation <reservation-id> [--json]
|
|
109
112
|
ornn storage undeploy --reservation <reservation-id> [--json]
|
|
110
113
|
ornn storage buckets list [--json]
|
|
111
114
|
ornn storage buckets show <drive-id> [--json]
|
|
@@ -143,8 +146,23 @@ current user's complete reservation key set on those machines. Use
|
|
|
143
146
|
`ornn access keys push` to explicitly re-sync keys that are already attached.
|
|
144
147
|
|
|
145
148
|
`ornn storage deploy status` prints reconciled object and byte progress once the
|
|
146
|
-
managed transfer has reported progress
|
|
147
|
-
|
|
149
|
+
managed transfer has reported progress, plus the number of target nodes that
|
|
150
|
+
have acknowledged the mount. Internal placement bucket details are not shown.
|
|
151
|
+
AWS S3 and external GCS sources use the managed transfer path. Cloudflare R2
|
|
152
|
+
buckets can be connected and verified, but deployment support is coming soon.
|
|
153
|
+
|
|
154
|
+
`ornn storage files upload` adds a file to the drive's current colocated copy.
|
|
155
|
+
You can upload while a direct FUSE mount is active; every mounted node sees the
|
|
156
|
+
file on its next lookup without a remount. A direct FUSE drive attaches to one
|
|
157
|
+
reservation by default; pass `--all-nodes` on `ornn storage deploy` to fan the
|
|
158
|
+
same mount out across every compatible node in the node group. Avoid writing
|
|
159
|
+
the same file path concurrently from multiple nodes because object storage is
|
|
160
|
+
not a POSIX shared filesystem.
|
|
161
|
+
|
|
162
|
+
For a connected bucket, deploy once before uploading. The first deploy imports
|
|
163
|
+
the source into an Ornn-managed colocated working copy. That copy remains the
|
|
164
|
+
drive's durable data when you unmount, detach, or mount it elsewhere. Ornn
|
|
165
|
+
does not write changes back to the original connected bucket.
|
|
148
166
|
|
|
149
167
|
UI aliases are first-class in the CLI: `listings` maps to the older
|
|
150
168
|
`availability` command, `exchange` maps to `bid`, and `gpus` maps to
|
|
@@ -200,7 +218,7 @@ The operator resource commands share the same reviewer auth:
|
|
|
200
218
|
filtered to one operator.
|
|
201
219
|
- `ornn tokens list` lists enrollment tokens.
|
|
202
220
|
- `ornn tokens create --operator <id> [--facility <id>]
|
|
203
|
-
|
|
221
|
+
[--expires-in <seconds>] [--mode bare-metal|vm] [--ip <addr>]` mints an
|
|
204
222
|
enrollment token and prints a ready-to-run `curl … | sudo bash` install
|
|
205
223
|
command.
|
|
206
224
|
- `ornn tokens revoke <token-id>` revokes an enrollment token.
|
|
@@ -210,33 +228,72 @@ Fleet handoff is an ordered, Compute-backed workflow:
|
|
|
210
228
|
- Before first contact, independently verify each host key and add it to the
|
|
211
229
|
operator's SSH `known_hosts`; fleet SSH uses strict host-key checking and will
|
|
212
230
|
not trust a key first presented during cleanup.
|
|
213
|
-
- `fleet clean ... --dry-run` tries `ubuntu`, `admin`, then `ornn`,
|
|
214
|
-
host's hardware identity
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
- `fleet clean ... --dry-run` tries `ubuntu`, `admin`, then `ornn`, binds the
|
|
232
|
+
inventory to the host's hardware identity, records the plan in Compute, and
|
|
233
|
+
writes an editable mode-`0600` policy beside the local fleet manifest (or to
|
|
234
|
+
`--policy-out`). Inventory covers accounts and orphan homes, authorized SSH
|
|
235
|
+
keys, tenant/log/temp/platform paths, workload and listener processes, GPU
|
|
236
|
+
consumers, custom systemd services, Docker/Podman objects and bind-mount
|
|
237
|
+
dependencies, and every block drive. Each resource advertises its own valid
|
|
238
|
+
actions. Suggestions are never applied silently: unresolved choices and
|
|
239
|
+
conflicts are recorded as plan data, so edit the policy and re-run the same
|
|
240
|
+
fleet with `--policy`; planning is not terminalized just because a choice is
|
|
241
|
+
needed. `--source-user` and `--preserve-user` remain legacy user-only
|
|
242
|
+
shortcuts and cannot be mixed with a policy file.
|
|
243
|
+
- `fleet clean <fleet-id> ... --confirm-clean <plan-hash>` is shown only after
|
|
244
|
+
every choice is resolved. Execution rechecks hardware and resource identity,
|
|
245
|
+
runs an idempotent per-resource journal, preserves the management SSH key,
|
|
246
|
+
enforces key-only SSH, and returns exact deleted, preserved, exception, and
|
|
247
|
+
drive-assurance evidence. `ignore` requires a reason and produces
|
|
248
|
+
`clean_with_exceptions`; it is never reported as residue-free deletion.
|
|
249
|
+
Data-drive actions are selectable: `logical_clean`, `discard`, `sanitize`, or
|
|
250
|
+
`reformat`. The system drive and drives without a unique serial/WWN are
|
|
251
|
+
locked to keep/ignore for in-band cleanup. `sanitize` requires NVMe plus
|
|
252
|
+
controller-reported completion. Logical cleanup, discard, and reformat are
|
|
253
|
+
not certified physical-media erasure, and the system drive requires an
|
|
254
|
+
out-of-band reimage for that assurance.
|
|
255
|
+
If the local SSH command times out while a long device operation is still in
|
|
256
|
+
progress, the recorded run remains resumable; re-run the same
|
|
257
|
+
`--confirm-clean <plan-hash>` command instead of creating a different plan.
|
|
258
|
+
Plans created by a pre-v6 runner must be replanned with `--dry-run` before
|
|
259
|
+
approval. This keeps newly discovered resources bound to a fresh reviewed
|
|
260
|
+
hash instead of adapting a legacy account-only plan during execution.
|
|
261
|
+
|
|
262
|
+
In the generated policy, change only `action`, plus `reason` for `ignore` or
|
|
263
|
+
`replacement` for `replace`; keep each generated `identity_hash` intact. `keep`
|
|
264
|
+
leaves a resource running, `stop_keep` stops but retains it, `stop` terminates a
|
|
265
|
+
process, `delete` removes it, `restart` restarts a service, and `replace`
|
|
266
|
+
recreates a container from the supplied image/name/network/command/labels/mounts
|
|
267
|
+
specification. The generated `suggested_action` is review guidance, not an
|
|
268
|
+
automatic decision. Do not put secrets in replacement commands or labels; use
|
|
269
|
+
the platform secret manager or a reviewed secret mount.
|
|
270
|
+
|
|
230
271
|
- `fleet enroll <fleet-id>` refuses nodes without successful cleanup proof,
|
|
231
272
|
issues and revokes one cleanup-bound token per host, installs the agent,
|
|
232
273
|
assigns the IB island, verifies the same management account still has
|
|
233
274
|
key-based SSH and passwordless sudo, and records the resulting GPU node IDs
|
|
234
275
|
in Compute. Generic enrollment tokens retain the legacy enrollment sanitizer.
|
|
235
|
-
- `fleet
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
276
|
+
- `fleet delete <fleet-id> --confirm-delete <fleet-id>` hard-deletes only an
|
|
277
|
+
abandoned cleanup plan or an unassigned legacy enrollment record. It saves a
|
|
278
|
+
local audit manifest first and refuses any Commerce/tenant/deployment link,
|
|
279
|
+
active enrollment, assigned GPU node, or completed/running cleanup evidence.
|
|
280
|
+
It never deletes the linked GPU-node records.
|
|
281
|
+
- `reservations commerce create --tenant <email> --listing <listing-id>
|
|
282
|
+
--fleet <fleet-id>` creates and activates the tenant's Commerce reservation.
|
|
283
|
+
GPU count comes from the enrolled fleet; start, end, and hourly rate default
|
|
284
|
+
from the listing. Start and end overrides must remain inside the listing's
|
|
285
|
+
allowed window; the hourly rate can also be overridden explicitly.
|
|
286
|
+
- `reservations commerce list --tenant <email-or-id> [--fleet <fleet-id>]`
|
|
287
|
+
lists active reservations in their live window; with `--fleet`, it returns
|
|
288
|
+
only reservations whose listing exactly matches that fleet's operator,
|
|
289
|
+
facility, GPU model, per-node size, and total GPU count.
|
|
290
|
+
- `fleet deploy <fleet-id> --tenant <contact-email>
|
|
291
|
+
[--user <email-or-id>] --commerce-reservation <id>` requires a live,
|
|
292
|
+
tenant-owned Commerce reservation whose listing exactly matches the fleet.
|
|
293
|
+
The user defaults to the tenant's primary administrator. Deployment succeeds
|
|
294
|
+
only after that user's SSH access and passwordless sudo are ready on every
|
|
295
|
+
node; Commerce is stamped only after the entire fleet reports deployed. A
|
|
296
|
+
Commerce reservation can be linked to only one fleet.
|
|
240
297
|
|
|
241
298
|
Compute Postgres is authoritative; the mode-0600 files under
|
|
242
299
|
`~/.config/ornn/fleets/` are only a secret-free cache. Cleanup proves “no
|
|
@@ -248,17 +305,22 @@ control-plane policy.
|
|
|
248
305
|
|
|
249
306
|
Reservation lifecycle verbs also run under the operator surface:
|
|
250
307
|
|
|
308
|
+
- `ornn reservations commerce create|list` creates or discovers a validated
|
|
309
|
+
Commerce reservation for an enrolled fleet. These actions require a staff
|
|
310
|
+
`ornn login`; the direct review-secret path cannot bypass Web validation.
|
|
251
311
|
- `ornn reservations withdraw <reservation-id>` withdraws a reservation's
|
|
252
312
|
acceptance, returning the node(s) to tenant inventory.
|
|
253
313
|
- `ornn reservations transfer <reservation-id> --target-tenant <id>
|
|
254
|
-
|
|
314
|
+
[--target-user <id>] [--node <id>] [--strategy reject|park] [--confirm]`
|
|
255
315
|
transfers a reservation to another tenant.
|
|
256
|
-
- `ornn reservations deploy <node-id> --target-tenant <id>
|
|
257
|
-
|
|
316
|
+
- `ornn reservations deploy <node-id> --target-tenant <id>
|
|
317
|
+
--commerce-reservation <id> [--target-user <id>] [--network public|private]`
|
|
318
|
+
deploys one node only after Web verifies that the live tenant-owned Commerce
|
|
319
|
+
reservation and listing match that node.
|
|
258
320
|
|
|
259
321
|
The tenant `ornn reservations list|show|checkout` verbs still use the tenant CLI
|
|
260
|
-
login and `/api/cli` proxy
|
|
261
|
-
|
|
322
|
+
login and `/api/cli` proxy. Staff Commerce actions use the Web operator proxy;
|
|
323
|
+
`withdraw|transfer|deploy` retain the operator path above.
|
|
262
324
|
|
|
263
325
|
Staff happy path:
|
|
264
326
|
|
package/package.json
CHANGED
package/src/api-client.mjs
CHANGED
|
@@ -271,6 +271,28 @@ export async function operatorRequest({
|
|
|
271
271
|
);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Commerce-backed operator actions must cross the Web trust boundary. Web
|
|
276
|
+
* validates Commerce state before it forwards to Compute, so direct
|
|
277
|
+
* review-secret access is intentionally unavailable for these requests.
|
|
278
|
+
*/
|
|
279
|
+
export async function webOperatorRequest({
|
|
280
|
+
body,
|
|
281
|
+
endpoint,
|
|
282
|
+
env = process.env,
|
|
283
|
+
fetchImpl = fetch,
|
|
284
|
+
method = "GET",
|
|
285
|
+
} = {}) {
|
|
286
|
+
const session = await loadAuthSession({ env });
|
|
287
|
+
if (!session?.accessToken) {
|
|
288
|
+
throw new CliApiError(
|
|
289
|
+
"Commerce-backed deployment requires Ornn staff auth through Web. Run `ornn login` first.",
|
|
290
|
+
{ status: 401 },
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
return operatorRequestViaStaffSession({ body, endpoint, env, fetchImpl, method });
|
|
294
|
+
}
|
|
295
|
+
|
|
274
296
|
export function computeEndpoint(path) {
|
|
275
297
|
const normalized = normalizeComputePath(path);
|
|
276
298
|
return `/api/cli/compute${normalized}`;
|
|
@@ -341,6 +363,9 @@ function detailMessage(detail) {
|
|
|
341
363
|
if (!detail || typeof detail !== "object") {
|
|
342
364
|
return "";
|
|
343
365
|
}
|
|
366
|
+
if (typeof detail.message === "string") {
|
|
367
|
+
return detail.message;
|
|
368
|
+
}
|
|
344
369
|
if (typeof detail.summary === "string") {
|
|
345
370
|
return detail.summary;
|
|
346
371
|
}
|