@ornncompute/cli 0.1.5 → 0.1.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 CHANGED
@@ -56,11 +56,13 @@ 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 [--ssh-user ubuntu|admin|ornn] [--json]
60
- ornn fleet clean <failed-fleet-id> --identity-file <path> --dry-run [--json]
61
- ornn fleet clean <fleet-id> --identity-file <path> --confirm-clean <plan-hash> [--json]
59
+ ornn fleet clean <ip>... --operator <id-or-slug> --ib-island <name> --identity-file <path> --dry-run [--source-user <user>]... [--preserve-user <user>]... [--ssh-user ubuntu|admin|ornn] [--json]
60
+ ornn fleet clean <failed-fleet-id> --identity-file <path> --dry-run [--source-user <user>]... [--preserve-user <user>]... [--json]
61
+ ornn fleet clean <fleet-id> --identity-file <path> --confirm-clean <plan-hash> [--source-user <user>]... [--preserve-user <user>]... [--json]
62
62
  ornn fleet enroll <fleet-id> --identity-file <path> [--confirm-takeover <ip[,ip...]>] [--json]
63
- ornn fleet deploy <fleet-id> --tenant <contact-email> --user <email-or-id> [--commerce-reservation <id>] [--network public|private] [--json]
63
+ ornn reservations commerce list --tenant <email-or-id> [--fleet <fleet-id>] [--json]
64
+ ornn reservations commerce create --tenant <email> --listing <listing-id> --fleet <fleet-id> [--start-at <timestamp>] [--end-at <timestamp>] [--price-per-gpu-hour <rate>] [--json]
65
+ ornn fleet deploy <fleet-id> --tenant <contact-email> [--user <email-or-id>] --commerce-reservation <id> [--network public|private] [--json]
64
66
  ornn ssh <node-or-reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
65
67
  ornn metrics nodes [--json]
66
68
  ornn metrics node <node-id> [--json]
@@ -103,9 +105,9 @@ ornn storage volumes create --name <name> [--source <drive-id>] [--json]
103
105
  ornn storage volumes refresh <drive-id> [--json]
104
106
  ornn storage volumes clear <drive-id> [--json]
105
107
  ornn storage volumes delete <drive-id> [--json]
106
- ornn storage deploy <drive-id> --reservation <reservation-id> [--mount-path <path>] [--read-only|--read-write] [--json]
108
+ ornn storage files upload <drive-id> <local-file> [--destination <path>] [--content-type <type>] [--json]
109
+ ornn storage deploy <drive-id> --reservation <reservation-id> [--all-nodes] [--mount-path <path>] [--read-only|--read-write] [--json]
107
110
  ornn storage deploy status --reservation <reservation-id> [--json]
108
- ornn storage unmount --reservation <reservation-id> [--json]
109
111
  ornn storage undeploy --reservation <reservation-id> [--json]
110
112
  ornn storage buckets list [--json]
111
113
  ornn storage buckets show <drive-id> [--json]
@@ -143,8 +145,23 @@ current user's complete reservation key set on those machines. Use
143
145
  `ornn access keys push` to explicitly re-sync keys that are already attached.
144
146
 
145
147
  `ornn storage deploy status` prints reconciled object and byte progress once the
146
- managed transfer has reported progress. AWS S3 and external GCS sources use the
147
- managed transfer path. Cloudflare R2 support is coming soon.
148
+ managed transfer has reported progress, plus the number of target nodes that
149
+ have acknowledged the mount. Internal placement bucket details are not shown.
150
+ AWS S3 and external GCS sources use the managed transfer path. Cloudflare R2
151
+ buckets can be connected and verified, but deployment support is coming soon.
152
+
153
+ `ornn storage files upload` adds a file to the drive's current colocated copy.
154
+ You can upload while a direct FUSE mount is active; every mounted node sees the
155
+ file on its next lookup without a remount. A direct FUSE drive attaches to one
156
+ reservation by default; pass `--all-nodes` on `ornn storage deploy` to fan the
157
+ same mount out across every compatible node in the node group. Avoid writing
158
+ the same file path concurrently from multiple nodes because object storage is
159
+ not a POSIX shared filesystem.
160
+
161
+ For a connected bucket, deploy once before uploading. The first deploy imports
162
+ the source into an Ornn-managed colocated working copy. That copy remains the
163
+ drive's durable data when you unmount, detach, or mount it elsewhere. Ornn
164
+ does not write changes back to the original connected bucket.
148
165
 
149
166
  UI aliases are first-class in the CLI: `listings` maps to the older
150
167
  `availability` command, `exchange` maps to `bid`, and `gpus` maps to
@@ -200,7 +217,7 @@ The operator resource commands share the same reviewer auth:
200
217
  filtered to one operator.
201
218
  - `ornn tokens list` lists enrollment tokens.
202
219
  - `ornn tokens create --operator <id> [--facility <id>]
203
- [--expires-in <seconds>] [--mode bare-metal|vm] [--ip <addr>]` mints an
220
+ [--expires-in <seconds>] [--mode bare-metal|vm] [--ip <addr>]` mints an
204
221
  enrollment token and prints a ready-to-run `curl … | sudo bash` install
205
222
  command.
206
223
  - `ornn tokens revoke <token-id>` revokes an enrollment token.
@@ -212,11 +229,17 @@ Fleet handoff is an ordered, Compute-backed workflow:
212
229
  not trust a key first presented during cleanup.
213
230
  - `fleet clean ... --dry-run` tries `ubuntu`, `admin`, then `ornn`, records each
214
231
  host's hardware identity and exact deletion plan, and prints one fleet plan
215
- hash. Re-running with `--confirm-clean <plan-hash>` removes the approved tenant
232
+ hash. Repeat `--source-user` for Linux accounts owned by the departing tenant
233
+ (including an explicitly reviewed orphaned `/home/<user>` directory) and
234
+ `--preserve-user` for reviewed host accounts. Package/service-owned
235
+ accounts are protected automatically; any other account stops planning for
236
+ explicit review. Re-running with `--confirm-clean <plan-hash>` and the same
237
+ account flags removes the approved tenant
216
238
  users, keys, containers/images/volumes/networks/build cache, workload paths,
217
- mounts, services, and logs; locks preserved-account passwords, enforces
218
- key-only SSH, trims supported filesystems, pins the preserved management
219
- account to the supplied key, and stores per-node proof in Compute. A failed
239
+ mounts, services, and tenant-scoped logs. Apply refuses plans with running
240
+ containers, locks the management/root passwords, enforces key-only SSH, trims
241
+ supported filesystems, preserves existing root and management keys and host
242
+ logs, and stores per-node proof in Compute. A failed
220
243
  cleanup can be dry-run again with the same fleet ID; only failed members are
221
244
  replanned. Residual GPU
222
245
  consumers or tenant/platform listeners fail verification instead of being
@@ -226,11 +249,22 @@ Fleet handoff is an ordered, Compute-backed workflow:
226
249
  assigns the IB island, verifies the same management account still has
227
250
  key-based SSH and passwordless sudo, and records the resulting GPU node IDs
228
251
  in Compute. Generic enrollment tokens retain the legacy enrollment sanitizer.
229
- - `fleet deploy <fleet-id> --tenant <contact-email> --user <email-or-id>
230
- [--commerce-reservation <id>]` validates the exact tenant/user and, when
231
- supplied, the Commerce reservation. It succeeds only after every selected
232
- user's SSH key is installed on every running node and passwordless sudo is
233
- reported ready.
252
+ - `reservations commerce create --tenant <email> --listing <listing-id>
253
+ --fleet <fleet-id>` creates and activates the tenant's Commerce reservation.
254
+ GPU count comes from the enrolled fleet; start, end, and hourly rate default
255
+ from the listing. Start and end overrides must remain inside the listing's
256
+ allowed window; the hourly rate can also be overridden explicitly.
257
+ - `reservations commerce list --tenant <email-or-id> [--fleet <fleet-id>]`
258
+ lists active reservations in their live window; with `--fleet`, it returns
259
+ only reservations whose listing exactly matches that fleet's operator,
260
+ facility, GPU model, per-node size, and total GPU count.
261
+ - `fleet deploy <fleet-id> --tenant <contact-email>
262
+ [--user <email-or-id>] --commerce-reservation <id>` requires a live,
263
+ tenant-owned Commerce reservation whose listing exactly matches the fleet.
264
+ The user defaults to the tenant's primary administrator. Deployment succeeds
265
+ only after that user's SSH access and passwordless sudo are ready on every
266
+ node; Commerce is stamped only after the entire fleet reports deployed. A
267
+ Commerce reservation can be linked to only one fleet.
234
268
 
235
269
  Compute Postgres is authoritative; the mode-0600 files under
236
270
  `~/.config/ornn/fleets/` are only a secret-free cache. Cleanup proves “no
@@ -242,17 +276,22 @@ control-plane policy.
242
276
 
243
277
  Reservation lifecycle verbs also run under the operator surface:
244
278
 
279
+ - `ornn reservations commerce create|list` creates or discovers a validated
280
+ Commerce reservation for an enrolled fleet. These actions require a staff
281
+ `ornn login`; the direct review-secret path cannot bypass Web validation.
245
282
  - `ornn reservations withdraw <reservation-id>` withdraws a reservation's
246
283
  acceptance, returning the node(s) to tenant inventory.
247
284
  - `ornn reservations transfer <reservation-id> --target-tenant <id>
248
- [--target-user <id>] [--node <id>] [--strategy reject|park] [--confirm]`
285
+ [--target-user <id>] [--node <id>] [--strategy reject|park] [--confirm]`
249
286
  transfers a reservation to another tenant.
250
- - `ornn reservations deploy <node-id> --target-tenant <id> [--target-user <id>]
251
- [--network public|private]` deploys a node into a tenant's inventory.
287
+ - `ornn reservations deploy <node-id> --target-tenant <id>
288
+ --commerce-reservation <id> [--target-user <id>] [--network public|private]`
289
+ deploys one node only after Web verifies that the live tenant-owned Commerce
290
+ reservation and listing match that node.
252
291
 
253
292
  The tenant `ornn reservations list|show|checkout` verbs still use the tenant CLI
254
- login and `/api/cli` proxy; `withdraw|transfer|deploy` use the operator path
255
- above (staff session proxy or optional review secret).
293
+ login and `/api/cli` proxy. Staff Commerce actions use the Web operator proxy;
294
+ `withdraw|transfer|deploy` retain the operator path above.
256
295
 
257
296
  Staff happy path:
258
297
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ornncompute/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Command-line interface for Ornn compute access workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
  }