@ornncompute/cli 0.2.16 → 0.2.18
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 +5 -2
- package/package.json +1 -1
- package/src/cli.mjs +3 -4
- package/src/update.mjs +13 -8
- package/vendor/capabilities/capabilities/staff-access.js +15 -1
package/README.md
CHANGED
|
@@ -271,12 +271,15 @@ path.
|
|
|
271
271
|
|
|
272
272
|
The operator resource commands share the same reviewer auth:
|
|
273
273
|
|
|
274
|
-
- `ornn
|
|
274
|
+
- `ornn operator list [--json]` lists operators from the capabilities
|
|
275
|
+
catalog (staff). Distinct from `ornn operators list`.
|
|
276
|
+
- `ornn operators list` lists enrolled provisioning operators
|
|
277
|
+
(`GET /provisioning/operators`).
|
|
275
278
|
- `ornn facilities list [--operator <id-or-slug>]` lists facilities, optionally
|
|
276
279
|
filtered to one operator.
|
|
277
280
|
- `ornn tokens list` lists enrollment tokens.
|
|
278
281
|
- `ornn tokens create --operator <id> [--facility <id>]
|
|
279
|
-
[--expires-in <seconds>] [--
|
|
282
|
+
[--expires-in <seconds>] [--ip <addr>] [--host <ip>]
|
|
280
283
|
[--identity-file <path>] [--yes]` mints an enrollment token and prints a
|
|
281
284
|
`curl … | sudo bash` install command. With `--host` and `--identity-file`,
|
|
282
285
|
orchestrator SSHs and runs that curl (same as Bulk Enroll). Asks y/N unless `--yes`.
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -165,7 +165,7 @@ Usage:
|
|
|
165
165
|
ornn operators list [--json]
|
|
166
166
|
ornn facilities list [--operator <id-or-slug>] [--json]
|
|
167
167
|
ornn tokens list [--json]
|
|
168
|
-
ornn tokens create --operator <id> [--facility <id>] [--expires-in <seconds>] [--
|
|
168
|
+
ornn tokens create --operator <id> [--facility <id>] [--expires-in <seconds>] [--ip <addr>] [--host <ip>] [--identity-file <path>] [--yes] [--json]
|
|
169
169
|
ornn tokens revoke <token-id> [--json]
|
|
170
170
|
ornn reservations transfer <reservation-id> --target-tenant <id> [--target-user <id>] [--node <id>] [--strategy reject|park] [--confirm] [--json]
|
|
171
171
|
ornn reservations commerce list --tenant <email-or-id> [--listing <listing-id>] [--json]
|
|
@@ -2467,12 +2467,12 @@ async function tokensCommand(args, context) {
|
|
|
2467
2467
|
|
|
2468
2468
|
if (subcommand === "create") {
|
|
2469
2469
|
const createUsage =
|
|
2470
|
-
"Usage: ornn tokens create --operator <id-or-slug> [--facility <id>] [--expires-in <seconds>] [--
|
|
2470
|
+
"Usage: ornn tokens create --operator <id-or-slug> [--facility <id>] [--expires-in <seconds>] [--ip <addr>] [--host <ip>] [--identity-file <path>] [--yes] [--json]";
|
|
2471
2471
|
const options = parseCommandOptions(
|
|
2472
2472
|
rest,
|
|
2473
2473
|
{
|
|
2474
2474
|
boolean: ["json", "force", "yes"],
|
|
2475
|
-
value: ["operator", "facility", "expires-in", "
|
|
2475
|
+
value: ["operator", "facility", "expires-in", "ip", "host", "identity-file"],
|
|
2476
2476
|
},
|
|
2477
2477
|
createUsage
|
|
2478
2478
|
);
|
|
@@ -2495,7 +2495,6 @@ async function tokensCommand(args, context) {
|
|
|
2495
2495
|
...(optionalStringOption(options.expiresIn)
|
|
2496
2496
|
? { expires_in_seconds: Number.parseInt(optionalStringOption(options.expiresIn), 10) }
|
|
2497
2497
|
: {}),
|
|
2498
|
-
...(optionalStringOption(options.mode) ? { node_mode: optionalStringOption(options.mode) } : {}),
|
|
2499
2498
|
...(optionalStringOption(options.ip) ? { ip: optionalStringOption(options.ip) } : {}),
|
|
2500
2499
|
...(optionalStringOption(options.host) ? { host: optionalStringOption(options.host) } : {}),
|
|
2501
2500
|
...(privateKey ? { private_key: privateKey } : {}),
|
package/src/update.mjs
CHANGED
|
@@ -125,7 +125,7 @@ export function detectInstallContext(binPath, env = process.env) {
|
|
|
125
125
|
if (isNpmGlobalInstall(lower, env)) {
|
|
126
126
|
return { kind: "npm", binPath: resolved };
|
|
127
127
|
}
|
|
128
|
-
if (isProjectInstall(resolved
|
|
128
|
+
if (isProjectInstall(resolved)) {
|
|
129
129
|
return { kind: "project", binPath: resolved };
|
|
130
130
|
}
|
|
131
131
|
if (has(".bun") || isUnderDir(lower, env.BUN_INSTALL)) {
|
|
@@ -140,14 +140,15 @@ export function detectInstallContext(binPath, env = process.env) {
|
|
|
140
140
|
return { kind: "npm", binPath: resolved };
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
function isProjectInstall(resolvedPath
|
|
143
|
+
function isProjectInstall(resolvedPath) {
|
|
144
144
|
// Global layouts have no package.json above node_modules; projects do.
|
|
145
|
-
const
|
|
146
|
-
|
|
145
|
+
const parts = resolvedPath.replaceAll("\\", "/").split("/");
|
|
146
|
+
const index = parts.findIndex((segment) => segment.toLowerCase() === "node_modules");
|
|
147
|
+
if (index <= 0) {
|
|
147
148
|
return false;
|
|
148
149
|
}
|
|
149
150
|
try {
|
|
150
|
-
return existsSync(join(
|
|
151
|
+
return existsSync(join(parts.slice(0, index).join("/"), "package.json"));
|
|
151
152
|
} catch {
|
|
152
153
|
return false;
|
|
153
154
|
}
|
|
@@ -212,9 +213,13 @@ function stableInstallRoot(binPath) {
|
|
|
212
213
|
try {
|
|
213
214
|
resolved = realpathSync(binPath);
|
|
214
215
|
} catch {}
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
|
|
216
|
+
const parts = resolved.replaceAll("\\", "/").split("/");
|
|
217
|
+
const index = parts.findIndex((segment) => segment.toLowerCase() === "node_modules");
|
|
218
|
+
if (index <= 0) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const prefix = parts.slice(0, index).join("/");
|
|
222
|
+
return prefix.length > 0 ? prefix : null;
|
|
218
223
|
}
|
|
219
224
|
|
|
220
225
|
function pathIdentity(path) {
|
|
@@ -200,7 +200,21 @@ export const sshKeyAddCapability = defineStaffMcpCapability({
|
|
|
200
200
|
// (gateway `add_reservation_key`), which this tool previously bypassed by
|
|
201
201
|
// posting `public_key` into a body that only reads `sshKeyId`/`userId`.
|
|
202
202
|
let sshKeyId = parsed.sshKeyId;
|
|
203
|
-
if (
|
|
203
|
+
if (sshKeyId) {
|
|
204
|
+
// Commerce stores the id as a soft key without checking authorization,
|
|
205
|
+
// so gate it here the way gateway `add_reservation_key` does.
|
|
206
|
+
const listed = (await ctx.fetch(`/v1/organizations/${seg(parsed.tenantId)}/ssh-keys`, {
|
|
207
|
+
method: "GET",
|
|
208
|
+
}));
|
|
209
|
+
const key = (listed.ssh_keys ?? []).find((candidate) => candidate.id === sshKeyId);
|
|
210
|
+
if (!key) {
|
|
211
|
+
throw new Error("SSH key not found.");
|
|
212
|
+
}
|
|
213
|
+
if (key.status !== "active" || key.revoked_at) {
|
|
214
|
+
throw new Error("SSH key is not active.");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
204
218
|
const registered = (await ctx.fetch(`/v1/organizations/${seg(parsed.tenantId)}/ssh-keys`, {
|
|
205
219
|
method: "POST",
|
|
206
220
|
body: { public_key: parsed.publicKey, ...mapBody(parsed, { label: "label" }) },
|