@ornncompute/cli 0.1.1 → 0.1.3

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
@@ -1,11 +1,13 @@
1
- # Ornn Fabric CLI
1
+ # Ornn CLI
2
2
 
3
- Command-line access for Ornn Fabric bare-metal compute workflows: find
4
- available capacity, buy or bid, manage reservations, activate SSH access,
5
- resell capacity, and open billing.
3
+ Command-line access for Ornn compute workflows: browse Listings, buy capacity,
4
+ bid in Exchange, manage GPU reservations, launch VM or Bare Metal machines,
5
+ create Kubernetes or Slurm clusters, manage private networks and storage
6
+ volumes, attach SSH keys, SSH into launched machines, watch machine health
7
+ metrics, and view billing readouts.
6
8
 
7
9
  For the end-user install, browser-login, command, and troubleshooting guide, see
8
- the root [`CLI.md`](../../CLI.md).
10
+ the [`docs/cli.md`](../../docs/cli.md) guide.
9
11
 
10
12
  ## Install
11
13
 
@@ -14,9 +16,9 @@ curl -fsSL https://compute.ornn.com/cli/install | sh
14
16
  ```
15
17
 
16
18
  The installer requires Node.js 20 or newer and npm. It installs `@ornncompute/cli`
17
- globally, exposes both `fabric` and `ornn` binaries, adds the npm global bin
18
- directory to the user's shell profile when needed, and writes
19
- `ORNN_AUTH_BASE_URL` for the Fabric host that served the installer. The
19
+ globally, exposes the `ornn` binary, adds the npm global bin directory to the
20
+ user's shell profile when needed, and writes
21
+ `ORNN_AUTH_BASE_URL` for the Ornn host that served the installer. The
20
22
  installer prints a short Ornn Compute welcome banner and login prompt; set
21
23
  `ORNN_INSTALL_BANNER=0` to skip it or `ORNN_INSTALL_ANIMATION=0` to keep it
22
24
  static.
@@ -24,46 +26,100 @@ static.
24
26
  ## Commands
25
27
 
26
28
  ```bash
27
- fabric help [command]
28
- fabric login [--auth-base <url>] [--no-browser] [--timeout <seconds>]
29
- fabric whoami [--json]
30
- fabric status [--json]
31
- fabric availability list [--gpu-type <type>] [--facility <name>] [--operator <name>] [--json]
32
- fabric availability show <listing-id> [--open] [--json]
33
- fabric buy <listing-id> [--no-open] [--json]
34
- fabric bid create <listing-id> --gpu-count <n> --min-gpu-count <n> --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd> [--no-open] [--json]
35
- fabric bid list [--json]
36
- fabric bid show <bid-id> [--open] [--json]
37
- fabric bid update <bid-id> --gpu-count <n> --min-gpu-count <n> --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd>
38
- fabric bid withdraw <bid-id>
39
- fabric reservations list [--status <status>] [--json]
40
- fabric reservations show <reservation-id> [--open] [--json]
41
- fabric reservations checkout <reservation-id> [--no-open] [--json]
42
- fabric access show <reservation-id> [--json]
43
- fabric access activate <reservation-id> --ssh-key-id <id> [--username <name>] [--no-open] [--json]
44
- fabric access push-keys <reservation-id> --ssh-key-id <id> [--json]
45
- fabric resale browse [--json]
46
- fabric resale list <reservation-id> --ask-price <usd> [--no-open] [--json]
47
- fabric resale update <reservation-id> --ask-price <usd> [--no-open] [--json]
48
- fabric resale delist <reservation-id> [--no-open] [--json]
49
- fabric ssh-keys list [--json]
50
- fabric ssh-keys add --public-key <key> [--label <label>] [--json]
51
- fabric ssh-keys add --public-key-file <path> [--label <label>] [--json]
52
- fabric ssh-keys delete <key-id> [--json]
53
- fabric billing open [--no-open] [--json]
54
- fabric api <get|post|patch|put|delete> <compute-path> [--data <json>] [--raw]
55
- fabric logout
29
+ ornn help [command]
30
+ ornn login [--auth-base <url>] [--no-browser] [--timeout <seconds>]
31
+ ornn whoami [--json]
32
+ ornn status [--json]
33
+ ornn listings list [--gpu-type <type>] [--facility <name>] [--operator <name>] [--json]
34
+ ornn listings show <listing-id> [--open] [--json]
35
+ ornn buy <listing-id> [--no-open] [--json]
36
+ ornn exchange create <listing-id> --gpu-count <n> --min-gpu-count <n> --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd> [--no-open] [--json]
37
+ ornn exchange list [--json]
38
+ ornn exchange show <bid-id> [--open] [--json]
39
+ ornn exchange update <bid-id> --gpu-count <n> --min-gpu-count <n> --start-date <yyyy-mm-dd> --end-date <yyyy-mm-dd> --price <usd>
40
+ ornn exchange withdraw <bid-id>
41
+ ornn gpus list [--status <status>] [--json]
42
+ ornn gpus show <reservation-id> [--open] [--json]
43
+ ornn gpus checkout <reservation-id> [--no-open] [--json]
44
+ ornn nodes list [--json]
45
+ ornn nodes show <node-id> [--json]
46
+ 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]
47
+ ornn nodes wait <node-or-reservation-id> [--timeout <seconds>] [--json]
48
+ ornn nodes start <node-id> [--json]
49
+ ornn nodes stop <node-id> [--json]
50
+ ornn nodes teardown <node-id> [--json]
51
+ ornn nodes revoke <node-id> [--json]
52
+ ornn nodes ssh-command <node-or-reservation-id> [--json]
53
+ ornn nodes keys attach <node-id> --key <path|id|label> [--json]
54
+ ornn nodes keys list <node-id> [--json]
55
+ ornn ssh <node-or-reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
56
+ ornn metrics nodes [--json]
57
+ ornn metrics node <node-id> [--json]
58
+ ornn metrics history <node-id> [--start <iso>] [--end <iso>] [--max-points <n>] [--json]
59
+ ornn metrics watch <node-id> [--interval <seconds>] [--count <n>] [--timeout <seconds>] [--json]
60
+ ornn clusters list [--json]
61
+ ornn clusters reservations [--json]
62
+ ornn clusters eligible-nodes <reservation-id> [--type kubernetes|slurm] [--network public|private] [--json]
63
+ ornn clusters create <reservation-id> --type kubernetes|slurm [--network public|private] [--node <node-id>] [--node-count <n>] [--wait] [--json]
64
+ ornn clusters show <reservation-id> [--type kubernetes|slurm] [--json]
65
+ ornn clusters wait <reservation-id> [--type kubernetes|slurm] [--timeout <seconds>] [--json]
66
+ ornn clusters credentials <reservation-id> [--type kubernetes|slurm] [--json]
67
+ ornn clusters kubeconfig <reservation-id> [--output <path>] [--json]
68
+ ornn clusters ssh-command <reservation-id> [--identity-file <path>] [--user <name>] [--json]
69
+ ornn clusters ssh <reservation-id> [--print] [--identity-file <path>] [--user <name>] [--json]
70
+ ornn clusters add-node <reservation-id> --node <node-id> [--json]
71
+ ornn clusters remove-node <reservation-id> --node <node-id> [--json]
72
+ ornn clusters teardown <reservation-id> [--type kubernetes|slurm] [--json]
73
+ ornn networks list [--json]
74
+ ornn networks show <network-id> [--json]
75
+ ornn networks create --name <name> [--cidr <cidr>] [--description <text>] [--json]
76
+ ornn networks update <network-id> [--name <name>] [--description <text>] [--clear-description] [--json]
77
+ ornn networks delete <network-id> [--json]
78
+ ornn networks reservation <reservation-id> [--json]
79
+ ornn networks attach <reservation-id> --network <network-id> [--json]
80
+ ornn networks detach <reservation-id> [--json]
81
+ ornn storage volumes list [--json]
82
+ ornn storage volumes show <drive-id> [--json]
83
+ ornn storage volumes create --name <name> [--source <drive-id>] [--json]
84
+ ornn storage volumes refresh <drive-id> [--json]
85
+ ornn storage volumes clear <drive-id> [--json]
86
+ ornn storage volumes delete <drive-id> [--json]
87
+ ornn keys list [--json]
88
+ ornn keys add [<public-key-file>] [--public-key <key>] [--label <label>] [--json]
89
+ ornn keys delete <key-id> [--json]
90
+ ornn access show <reservation-id> [--json]
91
+ ornn access activate <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] [--no-open] [--json]
92
+ ornn access push-keys <reservation-id> --ssh-key-id <id> [--json]
93
+ ornn access keys list <reservation-id> [--json]
94
+ ornn access keys add <reservation-id> --public-key <key> [--label <label>] [--json]
95
+ ornn access keys add <reservation-id> --public-key-file <path> [--label <label>] [--json]
96
+ ornn access keys push <reservation-id> --ssh-key-id <id> [--json]
97
+ ornn access keys status <reservation-id> [--json]
98
+ ornn ssh-keys list [--json]
99
+ ornn ssh-keys add --public-key <key> [--label <label>] [--json]
100
+ ornn ssh-keys add --public-key-file <path> [--label <label>] [--json]
101
+ ornn ssh-keys delete <key-id> [--json]
102
+ ornn billing summary [--json]
103
+ ornn billing invoices [--json]
104
+ ornn billing showback --start <yyyy-mm-dd> --end <yyyy-mm-dd> [--json]
105
+ ornn billing open [--no-open] [--json]
106
+ ornn api <get|post|patch|put|delete> <compute-path> [--data <json>] [--raw]
107
+ ornn logout
56
108
  ```
57
109
 
110
+ UI aliases are first-class in the CLI: `listings` maps to the older
111
+ `availability` command, `exchange` maps to `bid`, and `gpus` maps to
112
+ `reservations`. The older command names remain available for scripts.
113
+
58
114
  Commands print concise human-readable output by default. Use `--json` on
59
115
  read/show/list commands and transaction handoffs when you need structured
60
116
  stdout for scripts. Errors and login/browser progress go to stderr.
61
117
 
62
- `fabric login` opens a browser to the Fabric auth origin, lets the user sign in
118
+ `ornn login` opens a browser to the Ornn auth origin, lets the user sign in
63
119
  with the normal web account flow, and then completes automatically in the
64
120
  terminal once the browser session approves the CLI device request. If the CLI
65
121
  cannot open a browser, copy the printed URL into any browser and sign in there;
66
122
  the terminal keeps polling until the login is approved or expires.
67
123
 
68
- Run `fabric --help` for the full command list and `fabric api` for the
124
+ Run `ornn --help` for the full command list and `ornn api` for the
69
125
  allowlisted compute API escape hatch.
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@ornncompute/cli",
3
- "version": "0.1.1",
4
- "description": "Command-line interface for Ornn Fabric bare-metal compute workflows.",
3
+ "version": "0.1.3",
4
+ "description": "Command-line interface for Ornn compute access workflows.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "fabric": "bin/ornn.js",
8
7
  "ornn": "bin/ornn.js"
9
8
  },
10
9
  "files": [
@@ -1,4 +1,4 @@
1
- import { loadAuthSession } from "./auth-store.mjs";
1
+ import { clearAuthSession, loadAuthSession } from "./auth-store.mjs";
2
2
  import { resolveAuthBaseUrl } from "./device-auth.mjs";
3
3
 
4
4
  export class CliApiError extends Error {
@@ -23,7 +23,7 @@ export function resolveApiBaseUrl({ env = process.env, explicit, session } = {})
23
23
  export async function loadRequiredSession({ env = process.env } = {}) {
24
24
  const session = await loadAuthSession({ env });
25
25
  if (!session?.accessToken) {
26
- throw new CliApiError("Not logged in. Run `fabric login` first.", { status: 401 });
26
+ throw new CliApiError("Not logged in. Run `ornn login` first.", { status: 401 });
27
27
  }
28
28
  return session;
29
29
  }
@@ -64,23 +64,29 @@ export async function cliRequest({
64
64
  });
65
65
  } catch (error) {
66
66
  const message = error instanceof Error ? error.message : String(error);
67
- throw new CliApiError(`Could not reach Fabric at ${baseUrl}: ${message}`);
67
+ throw new CliApiError(`Could not reach Ornn at ${baseUrl}: ${message}`);
68
68
  }
69
69
 
70
70
  if (raw) {
71
71
  const text = await response.text();
72
72
  if (!response.ok) {
73
+ await handleAuthExpiry({ env, response, hadToken: Boolean(authSession?.accessToken) });
73
74
  throw new CliApiError(text || response.statusText, { status: response.status });
74
75
  }
75
76
  return text;
76
77
  }
77
78
 
78
79
  const text = await response.text();
80
+ if (!response.ok) {
81
+ // Clear a dead token before parsing, so a 401 with a non-JSON body
82
+ // (e.g. a gateway/WAF error page) still triggers the re-login flow.
83
+ await handleAuthExpiry({ env, response, hadToken: Boolean(authSession?.accessToken) });
84
+ }
79
85
  const data = text ? parseJson(text, url.toString(), response) : null;
80
86
  if (!response.ok) {
81
87
  const detail = data?.detail ?? data?.error ?? response.statusText;
82
88
  const message = detailMessage(detail) || response.statusText;
83
- throw new CliApiError(`Fabric request failed: ${message}`, {
89
+ throw new CliApiError(`Ornn request failed: ${message}`, {
84
90
  detail: data,
85
91
  status: response.status,
86
92
  });
@@ -111,14 +117,24 @@ function parseJson(text, url, response) {
111
117
  const contentType = response?.headers?.get?.("content-type") || "";
112
118
  const isHtml = /html/i.test(contentType) || /^\s*</.test(text);
113
119
  const hint = isHtml
114
- ? " The Fabric host may not have this CLI endpoint deployed yet."
120
+ ? " The Ornn host may not have this CLI endpoint deployed yet."
115
121
  : "";
116
- throw new CliApiError(`Fabric returned a non-JSON response from ${url}.${hint}`, {
122
+ throw new CliApiError(`Ornn returned a non-JSON response from ${url}.${hint}`, {
117
123
  status: response?.status,
118
124
  });
119
125
  }
120
126
  }
121
127
 
128
+ async function handleAuthExpiry({ env, response, hadToken }) {
129
+ if (response.status !== 401 || !hadToken) {
130
+ return;
131
+ }
132
+ await clearAuthSession({ env });
133
+ throw new CliApiError("Your Ornn session has expired or was revoked. Run `ornn login` to sign in again.", {
134
+ status: 401,
135
+ });
136
+ }
137
+
122
138
  function detailMessage(detail) {
123
139
  if (typeof detail === "string") {
124
140
  return detail;