@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 +68 -23
- package/package.json +1 -1
- package/src/api-client.mjs +22 -4
- package/src/cli.mjs +1936 -408
- package/src/color.mjs +71 -0
- package/vendor/capabilities/capabilities/listings.d.ts +1 -0
- package/vendor/capabilities/capabilities/listings.js +30 -0
- package/vendor/capabilities/capability.d.ts +1 -1
- package/vendor/capabilities/capability.js +10 -5
- package/vendor/capabilities/index.d.ts +1 -1
- package/vendor/capabilities/index.js +1 -1
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>] [--
|
|
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
|
|
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 <
|
|
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
|
|
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>] [--
|
|
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.
|
|
332
|
-
|
|
333
|
-
|
|
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
package/src/api-client.mjs
CHANGED
|
@@ -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(
|
|
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, {
|
|
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
|
|
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
|
}
|