@ornncompute/cli 0.2.17 → 0.2.19

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
@@ -34,6 +34,23 @@ saved only a deployed web/auth
34
34
  origin map that known Ornn origin to its API gateway automatically; custom and
35
35
  localhost combined endpoints are left unchanged.
36
36
 
37
+ ## First machine
38
+
39
+ ```bash
40
+ ornn login
41
+ ornn listings forecast
42
+ ornn buy <listing-id>
43
+ # Complete checkout in the browser, then:
44
+ ornn gpus list
45
+ ornn nodes launch --interactive --wait
46
+ ornn nodes list
47
+ ornn ssh <machine-hostname>
48
+ ```
49
+
50
+ Use `ornn exchange create --interactive` instead of `ornn buy` to place a bid.
51
+ Guided creation is explicit and TTY-only; flag-based commands remain canonical
52
+ for scripts.
53
+
37
54
  ## Commands
38
55
 
39
56
  ```bash
@@ -42,12 +59,14 @@ ornn login [--auth-base <url>] [--no-browser] [--timeout <seconds>]
42
59
  ornn update
43
60
  ornn whoami [--json]
44
61
  ornn status [--json]
45
- ornn listings list [--gpu-type <type>] [--facility <name>] [--operator <name>] [--json]
62
+ ornn listings list [--gpu-type <type>] [--facility <name>] [--json]
63
+ ornn listings forecast [--limit <1-500>] [--cursor <opaque>] [--json]
46
64
  ornn listings show <listing-id> [--open] [--json]
47
65
  ornn listings create [--gpu-type <type>] [--node-count <n>] [--yes] [--json]
48
66
  ornn buy <listing-id> [--no-open] [--json]
49
67
  ornn exchange create <listing-id> --node-count <n> [--min-node-count <n>] --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd> [--no-open] [--json]
50
- ornn exchange list [--limit <1-500>] [--cursor <last-id>] [--json]
68
+ ornn exchange create [<listing-id>] --interactive [--node-count <n>] [--min-node-count <n>] [--start-date <yyyy-mm-dd>] [--end-date <yyyy-mm-dd>] [--price <usd>] [--no-open]
69
+ ornn exchange list [--limit <1-500>] [--cursor <cursor>] [--json]
51
70
  ornn exchange show <exchange-id> [--open] [--json]
52
71
  ornn exchange update <exchange-id> --node-count <n> --min-node-count <n> --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd>
53
72
  ornn exchange withdraw <exchange-id>
@@ -57,17 +76,18 @@ Bid create/update prefer `--node-count` and `--min-node-count`. Legacy
57
76
  listing's GPUs per node before the gateway translates the request to the
58
77
  Commerce bid contract.
59
78
 
60
- ornn gpus list [--status <status>] [--limit <1-500>] [--cursor <last-id>] [--json]
79
+ ornn gpus list [--status <status>] [--limit <1-500>] [--cursor <cursor>] [--json]
61
80
  ornn gpus show <gpu-id> [--open] [--json]
62
81
  ornn gpus checkout <gpu-id> [--no-open] [--json]
63
82
  ornn nodes list [--json]
64
- ornn nodes show <node-id> [--json]
83
+ ornn nodes show <node-id-or-hostname> [--json]
65
84
  ornn nodes launch <reservation-id> --key <path|id|label> [--mode bare-metal|vm] [--username <name>] [--network public|private] [--storage-load-drive-id <id>] [--storage-save-drive-id <id>] [--wait] [--json]
85
+ ornn nodes launch [<reservation-id>] --interactive [--key <path|id|label>] [--mode bare-metal|vm] [--username <name>] [--network public|private] [--storage-load-drive-id <id>] [--storage-save-drive-id <id>] [--wait]
66
86
  ornn nodes switch <reservation-id> --network public|private --key <path|id|label> [--mode bare-metal|vm] [--username <name>] [--wait] [--json]
67
- ornn nodes wait <node-or-reservation-id> [--timeout <seconds>] [--json]
87
+ ornn nodes wait <node-or-reservation-id-or-hostname> [--timeout <seconds>] [--json]
68
88
  ornn nodes reboot <node-id> [--json]
69
89
  ornn nodes hard-reset <node-id> [--json]
70
- ornn nodes ssh-command <node-or-reservation-id> [--json]
90
+ ornn nodes ssh-command <node-or-reservation-id-or-hostname> [--json]
71
91
  ornn nodes keys attach <node-id> --key <path|id|label> [--json]
72
92
  ornn nodes keys list <node-id> [--json]
73
93
  ornn node health <node-id> [--json]
@@ -83,15 +103,16 @@ ornn node admin-key <node-id> [--generate] [--json]
83
103
  ornn reservations commerce list --tenant <email-or-id> [--listing <listing-id>] [--json]
84
104
  ornn reservations commerce create --tenant <email> --listing <listing-id> [--node-count <n>] [--node <node-id>] [--start-at <timestamp>] [--end-at <timestamp>] [--price-per-gpu-hour <rate>] [--json]
85
105
  ornn reservations deploy <node-id> --target-tenant <id> --commerce-reservation <id> [--target-user <id>] [--network public|private] [--json]
86
- ornn ssh <node-or-reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
106
+ ornn ssh <node-or-reservation-id-or-hostname> [--print] [--identity-file <path>] [--user <name>] [--json]
87
107
  ornn metrics nodes [--json]
88
- ornn metrics node <node-id> [--json]
89
- ornn metrics history <node-id> [--start <iso>] [--end <iso>] [--max-points <n>] [--json]
90
- ornn metrics watch <node-id> [--interval <seconds>] [--count <n>] [--timeout <seconds>] [--json]
108
+ ornn metrics node <node-id-or-hostname> [--json]
109
+ ornn metrics history <node-id-or-hostname> [--start <iso>] [--end <iso>] [--max-points <n>] [--json]
110
+ ornn metrics watch <node-id-or-hostname> [--interval <seconds>] [--count <n>] [--timeout <seconds>] [--json]
91
111
  ornn clusters list [--json]
92
112
  ornn clusters reservations [--json]
93
113
  ornn clusters eligible-nodes <reservation-id> [--type kubernetes|slurm] [--network public|private] [--json]
94
114
  ornn clusters create <reservation-id> --type kubernetes|slurm [--network public|private] [--node <node-id>] [--node-count <n>] [--wait] [--json]
115
+ ornn clusters create [<reservation-id>] --interactive [--type kubernetes|slurm] [--network public|private] [--node <node-id>] [--node-count <n>] [--wait]
95
116
  ornn clusters show <reservation-id> [--type kubernetes|slurm] [--json]
96
117
  ornn clusters wait <reservation-id> [--type kubernetes|slurm] [--timeout <seconds>] [--json]
97
118
  ornn clusters credentials <reservation-id> [--type kubernetes|slurm] [--json]
@@ -100,14 +121,14 @@ ornn clusters ssh-command <reservation-id> [--identity-file <path>] [--user <nam
100
121
  ornn clusters ssh <reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
101
122
  ornn clusters add-node <reservation-id> --node <node-id> [--json]
102
123
  ornn clusters remove-node <reservation-id> --node <node-id> [--json]
103
- ornn clusters teardown <reservation-id> [--type kubernetes|slurm] [--json]
124
+ ornn clusters teardown <reservation-id> [--yes] [--type kubernetes|slurm] [--json]
104
125
  ornn slurm launch <reservation-id> [--network public|private] [--node <node-id>] [--node-count <n>] [--wait] [--wait-timeout <seconds>] [--json]
105
- ornn slurm teardown <reservation-id> [--json]
126
+ ornn slurm teardown <reservation-id> [--yes] [--json]
106
127
  ornn slurm status <reservation-id> [--json]
107
128
  ornn slurm credentials <reservation-id> [--json]
108
129
  ornn slurm ssh <reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
109
130
  ornn kubernetes launch <reservation-id> [--network public|private] [--node <node-id>] [--node-count <n>] [--wait] [--wait-timeout <seconds>] [--json]
110
- ornn kubernetes teardown <reservation-id> [--json]
131
+ ornn kubernetes teardown <reservation-id> [--yes] [--json]
111
132
  ornn kubernetes status <reservation-id> [--json]
112
133
  ornn kubernetes credentials <reservation-id> [--json]
113
134
  ornn kubernetes kubeconfig <reservation-id> [--output <path>] [--json]
@@ -115,7 +136,7 @@ ornn networks list [--json]
115
136
  ornn networks show <network-id> [--json]
116
137
  ornn networks create --name <name> [--cidr <cidr>] [--description <text>] [--json]
117
138
  ornn networks update <network-id> [--name <name>] [--description <text>] [--clear-description] [--json]
118
- ornn networks delete <network-id> [--json]
139
+ ornn networks delete <network-id> [--yes] [--json]
119
140
  ornn networks reservation <reservation-id> [--json]
120
141
  ornn networks attach <reservation-id> --network <network-id> [--json]
121
142
  ornn networks detach <reservation-id> [--json]
@@ -123,8 +144,8 @@ ornn storage volumes list [--json]
123
144
  ornn storage volumes show <drive-id> [--json]
124
145
  ornn storage volumes create --name <name> [--source <drive-id>] [--json]
125
146
  ornn storage volumes refresh <drive-id> [--json]
126
- ornn storage volumes clear <drive-id> [--json]
127
- ornn storage volumes delete <drive-id> [--json]
147
+ ornn storage volumes clear <drive-id> [--yes] [--json]
148
+ ornn storage volumes delete <drive-id> [--yes] [--json]
128
149
  ornn storage files ls <drive-id> [--prefix <path>] [--json]
129
150
  ornn storage files upload <drive-id> <local-file> [--destination <path>] [--content-type <type>] [--json]
130
151
  ornn storage files download <drive-id> <remote-path> [--output <path>] [--json]
@@ -140,7 +161,7 @@ ornn storage buckets verify <drive-id> [--json]
140
161
  ornn storage buckets disconnect <drive-id> [--json]
141
162
  ornn storage filesystem deploy --reservation <reservation-id> [--performance-tier <tier>] [--capacity-gib <gib>] [--json]
142
163
  ornn storage filesystem status --reservation <reservation-id> [--json]
143
- ornn storage filesystem delete --reservation <reservation-id> [--json]
164
+ ornn storage filesystem delete --reservation <reservation-id> [--yes] [--json]
144
165
  ornn telemetry latest <node-id> [--span 15m] [--start <iso>] [--end <iso>] [--max-points <n>] [--json]
145
166
  ornn telemetry tail <node-id> [--duration <seconds>] [--json]
146
167
  ornn logs latest <node-id> [--stream kernel] [--count <n>] [--before <iso>] [--cursor <token>] [--json]
@@ -163,7 +184,7 @@ ornn ssh-keys add --public-key <key> --label <label> [--json]
163
184
  ornn ssh-keys add --public-key-file <path> --label <label> [--json]
164
185
  ornn ssh-keys delete <key-id> [--json]
165
186
  ornn billing summary [--json]
166
- ornn billing invoices [--json]
187
+ ornn billing invoices [--limit <1-500>] [--cursor <opaque>] [--json]
167
188
  ornn billing showback --start <yyyy-mm-dd> --end <yyyy-mm-dd> [--json]
168
189
  ornn billing open [--no-open] [--json]
169
190
  ornn api <get|post|patch|put|delete> <allowlisted-path> [--data <json>] [--raw]
@@ -232,6 +253,21 @@ use `--limit`, `--json`, and the response's opaque `next_cursor` to traverse
232
253
  older history. Replay that cursor exactly; it is not an item ID. `show` and `checkout`
233
254
  use exact tenant-owned item lookups, so they are not limited to the current page.
234
255
 
256
+ `listings forecast` is a Commerce-owned view of the next 48 one-hour
257
+ availability windows. It includes capacity that is unavailable now but frees
258
+ up during the horizon. The CLI makes one gateway request; it does not probe
259
+ each hour independently.
260
+
261
+ `exchange create`, `nodes launch`, and `clusters create` accept
262
+ `--interactive`. Supplied flags prefill the guided answers. The mode requires
263
+ TTY stdin and stdout, rejects `--json`, prints the complete request, and
264
+ submits only after an explicit default-no confirmation.
265
+
266
+ Machine UUIDs remain canonical. `ornn ssh`, `nodes show`, `nodes wait`, and
267
+ tenant metrics also accept a case-insensitive exact hostname when it uniquely
268
+ matches one active machine; ambiguous hostnames fail and print the matching
269
+ full IDs.
270
+
235
271
  ## Operator commands
236
272
 
237
273
  The `ornn node` commands are for Ornn operators and reviewers, not tenants. They
@@ -271,12 +307,15 @@ path.
271
307
 
272
308
  The operator resource commands share the same reviewer auth:
273
309
 
274
- - `ornn operators list` lists enrolled operators.
310
+ - `ornn operator list [--json]` lists operators from the capabilities
311
+ catalog (staff). Distinct from `ornn operators list`.
312
+ - `ornn operators list` lists enrolled provisioning operators
313
+ (`GET /provisioning/operators`).
275
314
  - `ornn facilities list [--operator <id-or-slug>]` lists facilities, optionally
276
315
  filtered to one operator.
277
316
  - `ornn tokens list` lists enrollment tokens.
278
317
  - `ornn tokens create --operator <id> [--facility <id>]
279
- [--expires-in <seconds>] [--mode bare-metal|vm] [--ip <addr>] [--host <ip>]
318
+ [--expires-in <seconds>] [--ip <addr>] [--host <ip>]
280
319
  [--identity-file <path>] [--yes]` mints an enrollment token and prints a
281
320
  `curl … | sudo bash` install command. With `--host` and `--identity-file`,
282
321
  orchestrator SSHs and runs that curl (same as Bulk Enroll). Asks y/N unless `--yes`.
@@ -328,9 +367,15 @@ Optional installer gateway base for `ornn tokens create` install one-liners:
328
367
  Without a staff login session, operator commands fail with a clear error before
329
368
  making a network request.
330
369
 
331
- Commands print concise human-readable output by default. Use `--json` on
332
- read/show/list commands and transaction handoffs when you need structured
333
- stdout for scripts. Errors and login/browser progress go to stderr.
370
+ Commands print concise human-readable output by default. Customer list
371
+ commands use aligned tables when stdout is a TTY and retain their existing
372
+ line-oriented shape when piped or redirected. Use `--json` on
373
+ read/show/list commands and transaction handoffs for the supported structured
374
+ stdout contract. JSON never contains tables, progress, next-step hints, color,
375
+ or ANSI sequences. Errors and login/browser progress go to stderr. Under
376
+ `--json` an error is written to stderr as one object, `{"error": "<message>",
377
+ "status": <http-status>}` (`status` only when the API supplied one); stdout
378
+ stays empty and the exit code is unchanged.
334
379
 
335
380
  `ornn login` opens a browser to the Ornn auth origin, lets the user sign in
336
381
  with the normal web account flow, and then completes automatically in the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ornncompute/cli",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Command-line interface for Ornn compute access workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,11 +2,13 @@ import { clearAuthSession, configuredApiBaseUrl, loadAuthSession } from "./auth-
2
2
  import { resolveAuthBaseUrl } from "./device-auth.mjs";
3
3
 
4
4
  export class CliApiError extends Error {
5
- constructor(message, { status, detail } = {}) {
5
+ constructor(message, { status, detail, genericFallback = false } = {}) {
6
6
  super(message);
7
7
  this.name = "CliApiError";
8
8
  this.status = status;
9
9
  this.detail = detail;
10
+ // Signals `formatCliError` to append its generic actionable trailer.
11
+ this.genericFallback = genericFallback;
10
12
  }
11
13
  }
12
14
 
@@ -122,14 +124,19 @@ export async function cliRequest({
122
124
  );
123
125
  }
124
126
  const message = error instanceof Error ? error.message : String(error);
125
- throw new CliApiError(`Could not reach Ornn at ${baseUrl}: ${message}`);
127
+ throw new CliApiError(
128
+ `Could not reach Ornn at ${baseUrl} (${message}). Check your network connection and that ${baseUrl} is the correct Ornn API endpoint, then try again.`,
129
+ );
126
130
  }
127
131
 
128
132
  if (raw) {
129
133
  const text = await response.text();
130
134
  if (!response.ok) {
131
135
  await handleAuthExpiry({ env, response, hadToken: Boolean(authSession?.accessToken) });
132
- throw new CliApiError(text || response.statusText, { status: response.status });
136
+ throw new CliApiError(text || response.statusText, {
137
+ status: response.status,
138
+ genericFallback: true,
139
+ });
133
140
  }
134
141
  return text;
135
142
  }
@@ -178,10 +185,20 @@ export async function raiseForFailedResponse({
178
185
  await handleAuthExpiry({ env, response, hadToken });
179
186
  const data = text ? parseJson(text, url, response) : null;
180
187
  const detail = data?.detail ?? data?.error ?? response.statusText;
181
- const message = detailMessage(detail) || fallbackMessage || response.statusText;
188
+ const detailText = detailMessage(detail);
189
+ const message = detailText || fallbackMessage || response.statusText;
190
+ // A generic-trailer candidate for `formatCliError`: either the API
191
+ // actually sent a `detail`/`error` value (even if the CLI doesn't
192
+ // recognize its shape), or nothing usable came back at all and this call
193
+ // site had no hand-authored `fallbackMessage` of its own to fall back to
194
+ // — i.e. the rendered message is nothing but the bare HTTP status text.
195
+ // A call site that supplies its own `fallbackMessage` has already made
196
+ // that fallback self-contained and actionable; leave it alone.
197
+ const genericFallback = Boolean(detailText) || !fallbackMessage;
182
198
  throw new CliApiError(formatMessage(message), {
183
199
  detail: data,
184
200
  status: response.status,
201
+ genericFallback,
185
202
  });
186
203
  }
187
204
 
@@ -274,6 +291,7 @@ function parseJson(text, url, response) {
274
291
  const hint = isHtml ? " The Ornn host may not have this CLI endpoint deployed yet." : "";
275
292
  throw new CliApiError(`Ornn returned a non-JSON response from ${url}.${hint}`, {
276
293
  status: response?.status,
294
+ genericFallback: response?.ok === false,
277
295
  });
278
296
  }
279
297
  }