@ornncompute/cli 1.0.67 → 1.0.75
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 +35 -27
- package/package.json +1 -1
- package/src/admin-capacity-reservations.mjs +2 -12
- package/src/admin-command-handlers.mjs +0 -1
- package/src/admin-credits.mjs +1 -2
- package/src/admin-enrollment.mjs +22 -30
- package/src/admin-mutations.mjs +6 -56
- package/src/admin-node-diagnostics.mjs +2 -36
- package/src/admin-node-lifecycle.mjs +25 -43
- package/src/admin-shell.mjs +39 -32
- package/src/catalog-shell.mjs +4 -18
- package/src/external-bucket-commands.mjs +5 -73
- package/src/interaction-runtime.mjs +2 -37
- package/src/kubernetes-commands.mjs +11 -102
- package/src/launch-mode-handlers.mjs +0 -1
- package/src/mount-commands.mjs +25 -131
- package/src/storage-commands.mjs +2 -87
- package/src/vm-commands.mjs +11 -44
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +12 -5
- package/vendor/capabilities/capabilities/admin-enrollment.js +18 -17
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +3 -101
- package/vendor/capabilities/capabilities/admin.js +3 -3
- package/vendor/capabilities/capabilities/clusters.js +3 -3
- package/vendor/capabilities/capabilities/commerce-market.js +9 -9
- package/vendor/capabilities/capabilities/compute.js +7 -7
- package/vendor/capabilities/capabilities/credits.js +7 -2
- package/vendor/capabilities/capabilities/external-buckets.js +4 -4
- package/vendor/capabilities/capabilities/fleet.js +109 -29
- package/vendor/capabilities/capabilities/listings.js +44 -16
- package/vendor/capabilities/capabilities/locality.js +42 -0
- package/vendor/capabilities/capabilities/market-shared.js +1 -1
- package/vendor/capabilities/capabilities/markets.js +14 -14
- package/vendor/capabilities/capabilities/mounts.js +4 -4
- package/vendor/capabilities/capabilities/native-listing.js +51 -15
- package/vendor/capabilities/capabilities/networks.js +5 -5
- package/vendor/capabilities/capabilities/observability.js +49 -14
- package/vendor/capabilities/capabilities/tokens.js +5 -9
- package/vendor/capabilities/capabilities/users.js +8 -10
- package/vendor/capabilities/capabilities/volumes.js +5 -5
package/src/vm-commands.mjs
CHANGED
|
@@ -303,7 +303,6 @@ function failedOperationRecoveryCommand(value, interaction) {
|
|
|
303
303
|
`--mode ${accessMode}`,
|
|
304
304
|
`--network ${networkMode}`,
|
|
305
305
|
accessMode !== sourceMode ? "--erase-local-data" : null,
|
|
306
|
-
"--yes",
|
|
307
306
|
`--idempotency-key ${requestId}`,
|
|
308
307
|
]
|
|
309
308
|
.filter(Boolean)
|
|
@@ -317,7 +316,6 @@ function failedOperationRecoveryCommand(value, interaction) {
|
|
|
317
316
|
`ornn machines ${command}`,
|
|
318
317
|
shellQuote(id),
|
|
319
318
|
command === "reset" ? "--erase-local-data" : null,
|
|
320
|
-
"--yes",
|
|
321
319
|
`--idempotency-key ${requestId}`,
|
|
322
320
|
]
|
|
323
321
|
.filter(Boolean)
|
|
@@ -392,7 +390,7 @@ function positional(input, expected, usage, interaction) {
|
|
|
392
390
|
return values;
|
|
393
391
|
}
|
|
394
392
|
|
|
395
|
-
function machineMutationPreflight(input, interaction
|
|
393
|
+
function machineMutationPreflight(input, interaction) {
|
|
396
394
|
if (!interaction.machine) return;
|
|
397
395
|
if (!text(input.idempotencyKey)) {
|
|
398
396
|
interaction.usage("--idempotency-key is required for automation.", {
|
|
@@ -400,12 +398,6 @@ function machineMutationPreflight(input, interaction, { requireYes = true } = {}
|
|
|
400
398
|
field: "idempotencyKey",
|
|
401
399
|
});
|
|
402
400
|
}
|
|
403
|
-
if (requireYes && input.yes !== true) {
|
|
404
|
-
interaction.usage("--yes is required for automation after reviewing the exact intent.", {
|
|
405
|
-
code: "consent_required",
|
|
406
|
-
field: "yes",
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
401
|
}
|
|
410
402
|
|
|
411
403
|
async function selectedOrganization(context, interaction, expectedId) {
|
|
@@ -415,8 +407,8 @@ async function selectedOrganization(context, interaction, expectedId) {
|
|
|
415
407
|
const selected = record(record(result.data).selected_organization);
|
|
416
408
|
const id = text(selected.id);
|
|
417
409
|
const name = text(selected.name);
|
|
418
|
-
if (!id || !name
|
|
419
|
-
interaction.usage("
|
|
410
|
+
if (!id || !name) {
|
|
411
|
+
interaction.usage("Join an organization before launching compute.", {
|
|
420
412
|
code: "organization_required",
|
|
421
413
|
field: "organization",
|
|
422
414
|
});
|
|
@@ -821,12 +813,6 @@ function launchInputPreflight(input, interaction) {
|
|
|
821
813
|
field: "label",
|
|
822
814
|
});
|
|
823
815
|
}
|
|
824
|
-
if (input.yes !== true) {
|
|
825
|
-
interaction.usage("--yes is required for automation after reviewing the launch intent.", {
|
|
826
|
-
code: "consent_required",
|
|
827
|
-
field: "yes",
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
816
|
if (!text(input.idempotencyKey)) {
|
|
831
817
|
interaction.usage("--idempotency-key is required for automation.", {
|
|
832
818
|
code: "idempotency_key_required",
|
|
@@ -1199,7 +1185,7 @@ export const vmLaunchModeHandler = {
|
|
|
1199
1185
|
resourceId(key),
|
|
1200
1186
|
"--idempotency-key",
|
|
1201
1187
|
idempotencyKey,
|
|
1202
|
-
...(interaction.machine ? ["--
|
|
1188
|
+
...(interaction.machine ? ["--no-wait", "--format", "json"] : []),
|
|
1203
1189
|
].join(" ");
|
|
1204
1190
|
return uncertainMutationFailure(interaction, error, {
|
|
1205
1191
|
data: {
|
|
@@ -1559,7 +1545,6 @@ function durableWorkspaceAllocation(reservation, interaction) {
|
|
|
1559
1545
|
function workspaceStepContext(context, interaction, signal) {
|
|
1560
1546
|
const stepInteraction = Object.create(interaction);
|
|
1561
1547
|
stepInteraction.review = async () => true;
|
|
1562
|
-
stepInteraction.options = { ...interaction.options, yes: true };
|
|
1563
1548
|
return {
|
|
1564
1549
|
...context,
|
|
1565
1550
|
fetch: (path, init = {}) =>
|
|
@@ -1681,7 +1666,7 @@ function workspaceResumeCommand(input, goal, facts, id, interaction, localResume
|
|
|
1681
1666
|
if (goal.username) parts.push("--username", goal.username);
|
|
1682
1667
|
if (input.noWait) parts.push("--no-wait");
|
|
1683
1668
|
if (input.wait) parts.push("--wait", "--timeout", text(input.timeout));
|
|
1684
|
-
if (interaction.machine) parts.push("--
|
|
1669
|
+
if (interaction.machine) parts.push("--format", interaction.format);
|
|
1685
1670
|
return parts.map(shellQuote).join(" ");
|
|
1686
1671
|
}
|
|
1687
1672
|
|
|
@@ -1952,7 +1937,6 @@ async function launchDurableBareMetalWorkspace({
|
|
|
1952
1937
|
$positionals: [reservationId],
|
|
1953
1938
|
key: resourceId(key),
|
|
1954
1939
|
idempotencyKey: `${id}:ssh-key`,
|
|
1955
|
-
yes: true,
|
|
1956
1940
|
}
|
|
1957
1941
|
);
|
|
1958
1942
|
states.ssh_key_assignment = {
|
|
@@ -1988,7 +1972,6 @@ async function launchDurableBareMetalWorkspace({
|
|
|
1988
1972
|
idempotencyKey: `${id}:volume-mount`,
|
|
1989
1973
|
noWait: true,
|
|
1990
1974
|
volume: facts.volume.id,
|
|
1991
|
-
yes: true,
|
|
1992
1975
|
}
|
|
1993
1976
|
);
|
|
1994
1977
|
facts.volumeMount = mount.data.volume_mount;
|
|
@@ -2464,7 +2447,7 @@ export const bareMetalLaunchModeHandler = {
|
|
|
2464
2447
|
resourceId(key),
|
|
2465
2448
|
"--idempotency-key",
|
|
2466
2449
|
idempotencyKey,
|
|
2467
|
-
...(interaction.machine ? ["--
|
|
2450
|
+
...(interaction.machine ? ["--no-wait", "--format", "json"] : []),
|
|
2468
2451
|
].join(" ");
|
|
2469
2452
|
return uncertainMutationFailure(interaction, error, {
|
|
2470
2453
|
data: {
|
|
@@ -3002,24 +2985,12 @@ function validateWaitInput(input, interaction, { waitCommand = false } = {}) {
|
|
|
3002
2985
|
function requireMachineMutationSafety(input, interaction, { destructive = false } = {}) {
|
|
3003
2986
|
validateWaitInput(input, interaction);
|
|
3004
2987
|
if (destructive && input.eraseLocalData !== true) {
|
|
3005
|
-
interaction.usage(
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
field: "eraseLocalData",
|
|
3010
|
-
}
|
|
3011
|
-
);
|
|
2988
|
+
interaction.usage("machines reset requires --erase-local-data.", {
|
|
2989
|
+
code: "erase_local_data_required",
|
|
2990
|
+
field: "eraseLocalData",
|
|
2991
|
+
});
|
|
3012
2992
|
}
|
|
3013
2993
|
if (interaction.machine || !interaction.eligibleHuman) {
|
|
3014
|
-
if (input.yes !== true) {
|
|
3015
|
-
interaction.usage(
|
|
3016
|
-
"--yes is required outside an eligible terminal after reviewing the exact intent.",
|
|
3017
|
-
{
|
|
3018
|
-
code: "consent_required",
|
|
3019
|
-
field: "yes",
|
|
3020
|
-
}
|
|
3021
|
-
);
|
|
3022
|
-
}
|
|
3023
2994
|
if (!text(input.idempotencyKey)) {
|
|
3024
2995
|
interaction.usage("--idempotency-key is required outside an eligible terminal.", {
|
|
3025
2996
|
code: "idempotency_key_required",
|
|
@@ -3452,7 +3423,7 @@ async function switchMachine(input, context, interaction) {
|
|
|
3452
3423
|
const erasesLocalData = targetMode !== currentMode;
|
|
3453
3424
|
if (erasesLocalData && input.eraseLocalData !== true) {
|
|
3454
3425
|
interaction.usage(
|
|
3455
|
-
"Changing between VM and Bare Metal permanently erases source-local data and requires --erase-local-data;
|
|
3426
|
+
"Changing between VM and Bare Metal permanently erases source-local data and requires --erase-local-data; cannot infer destructive scope.",
|
|
3456
3427
|
{ code: "erase_local_data_required", field: "eraseLocalData" }
|
|
3457
3428
|
);
|
|
3458
3429
|
}
|
|
@@ -3561,7 +3532,6 @@ async function switchMachine(input, context, interaction) {
|
|
|
3561
3532
|
requestedMode ? `--mode ${requestedMode}` : null,
|
|
3562
3533
|
requestedNetwork ? `--network ${requestedNetwork}` : null,
|
|
3563
3534
|
erasesLocalData ? "--erase-local-data" : null,
|
|
3564
|
-
"--yes",
|
|
3565
3535
|
"--no-wait",
|
|
3566
3536
|
`--idempotency-key ${shellQuote(idempotencyKey)}`,
|
|
3567
3537
|
]
|
|
@@ -3663,7 +3633,6 @@ async function switchMachine(input, context, interaction) {
|
|
|
3663
3633
|
`--mode ${targetMode}`,
|
|
3664
3634
|
`--network ${targetNetwork}`,
|
|
3665
3635
|
erasesLocalData ? "--erase-local-data" : null,
|
|
3666
|
-
"--yes",
|
|
3667
3636
|
`--idempotency-key ${shellQuote(interaction.idempotencyKey())}`,
|
|
3668
3637
|
]
|
|
3669
3638
|
.filter(Boolean)
|
|
@@ -3781,7 +3750,6 @@ async function maintainMachine(command, input, context, interaction) {
|
|
|
3781
3750
|
`ornn machines ${command}`,
|
|
3782
3751
|
shellQuote(machineId(machine)),
|
|
3783
3752
|
destructive ? "--erase-local-data" : null,
|
|
3784
|
-
"--yes",
|
|
3785
3753
|
"--no-wait",
|
|
3786
3754
|
`--idempotency-key ${shellQuote(idempotencyKey)}`,
|
|
3787
3755
|
]
|
|
@@ -3874,7 +3842,6 @@ async function maintainMachine(command, input, context, interaction) {
|
|
|
3874
3842
|
`ornn machines ${command}`,
|
|
3875
3843
|
shellQuote(machineId(waitOutcome.value)),
|
|
3876
3844
|
destructive ? "--erase-local-data" : null,
|
|
3877
|
-
"--yes",
|
|
3878
3845
|
`--idempotency-key ${shellQuote(interaction.idempotencyKey())}`,
|
|
3879
3846
|
]
|
|
3880
3847
|
.filter(Boolean)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineCapability, preview } from "../capability.js";
|
|
3
|
+
import { ensureFacility, locateRegion, unplacedLocation } from "./locality.js";
|
|
3
4
|
import { nativeListingCreateInput } from "./native-listing.js";
|
|
4
5
|
import { idempotencyHeaders, microUsd } from "./market-shared.js";
|
|
5
6
|
function adminHelp(command, outcome, human, automation, review) {
|
|
@@ -18,22 +19,28 @@ export const adminCapacityOffersCreateCapability = defineCapability({
|
|
|
18
19
|
id: "listing.create",
|
|
19
20
|
domain: "listings",
|
|
20
21
|
roles: ["admin"],
|
|
21
|
-
description: "Create native capacity with
|
|
22
|
+
description: "Create native capacity with an organization, a facilityId or a location, hardware, availability and advertised terms. A location is any state, city, address, or catalog region (us-west-3); Ornn places it in a catalog region and uses the organization's facility there. Primary capacity needs daysToRfs (0 when ready now) or both availableAt and availableUntil. nodeIds attach nodes at creation. Returns {listing, terms}. Requires an idempotencyKey reused on retries and confirm: true.",
|
|
22
23
|
mutation: "preview-confirm",
|
|
23
24
|
http: { method: "POST", path: "/commerce/v2/listings" },
|
|
24
25
|
input: nativeListingCreateInput,
|
|
25
26
|
slack: false,
|
|
26
27
|
cli: {
|
|
27
28
|
command: ["admin", "capacity-offers", "create"],
|
|
28
|
-
vnext: adminHelp(["admin", "capacity-offers", "create"], "Create native capacity and its advertised financial terms.", "ornn admin capacity-offers create --gpu-slug GPU --
|
|
29
|
+
vnext: adminHelp(["admin", "capacity-offers", "create"], "Create native capacity and its advertised financial terms.", "ornn admin capacity-offers create --gpu-slug GPU --gpus-per-node COUNT (--facility-id UUID | --location PLACE) --provisioning-type bare-metal|virtual-machine --state draft|available --terms JSON --idempotency-key KEY (--days-to-rfs DAYS | --available-at TIME --available-until TIME) [--capacity-node-count COUNT] [--node-ids JSON] [--organization-id UUID]", "Requires an eligible human terminal; --location also needs --organization-id; reuse --idempotency-key on retries.", "Review the native parent fields and each financial term before default-no consent."),
|
|
29
30
|
},
|
|
30
31
|
execute: async (ctx, input) => {
|
|
31
|
-
const { confirm: _confirm, idempotencyKey, ...body } = nativeListingCreateInput.parse(input);
|
|
32
|
+
const { confirm: _confirm, idempotencyKey, location, ...body } = nativeListingCreateInput.parse(input);
|
|
33
|
+
const located = location ? await locateRegion(ctx.fetch, location) : null;
|
|
34
|
+
if (location && !located)
|
|
35
|
+
throw new Error(unplacedLocation(location));
|
|
32
36
|
if (!ctx.confirmed)
|
|
33
|
-
return preview("listing.create", body);
|
|
37
|
+
return preview("listing.create", located ? { ...body, located } : body);
|
|
38
|
+
const facilityId = located
|
|
39
|
+
? (await ensureFacility(ctx.fetch, body.organizationId, located.region)).facilityId
|
|
40
|
+
: body.facilityId;
|
|
34
41
|
return ctx.fetch("/commerce/v2/listings", {
|
|
35
42
|
method: "POST",
|
|
36
|
-
body,
|
|
43
|
+
body: { ...body, facilityId },
|
|
37
44
|
headers: idempotencyHeaders(ctx, idempotencyKey),
|
|
38
45
|
});
|
|
39
46
|
},
|
|
@@ -44,11 +44,13 @@ export const adminEnrollmentTokensListCapability = {
|
|
|
44
44
|
};
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
|
-
export const adminEnrollmentTokenCreateCapability = withVnextBinding(tokensCreateCapability, adminHelp(["admin", "enrollment-tokens", "create"], "Create one expiring enrollment token through an explicit secret export.", "ornn admin enrollment-tokens create --supplier UUID --expires-in SECONDS (--print | --output PATH) [--overwrite] [
|
|
47
|
+
export const adminEnrollmentTokenCreateCapability = withVnextBinding(tokensCreateCapability, adminHelp(["admin", "enrollment-tokens", "create"], "Create one expiring enrollment token through an explicit secret export.", "ornn admin enrollment-tokens create --supplier UUID --expires-in SECONDS (--print | --output PATH) [--overwrite] []", "Unsupported: token creation requires an eligible human terminal.", ["canonical supplier organization UUID"], "Review the supplier, expiry, and secret destination before minting the token."));
|
|
48
48
|
const tokenRevokeInput = z.object({
|
|
49
|
-
|
|
49
|
+
tokenId: z.string().min(1),
|
|
50
|
+
token_id: UUID.optional(),
|
|
50
51
|
expected_created_at: nonEmpty.optional(),
|
|
51
52
|
idempotency_key: nonEmpty.optional(),
|
|
53
|
+
confirm: z.boolean().optional(),
|
|
52
54
|
});
|
|
53
55
|
function rowsFromTokenList(payload) {
|
|
54
56
|
if (Array.isArray(payload))
|
|
@@ -104,22 +106,23 @@ async function readExactToken(ctx, tokenId, expectedCreatedAt) {
|
|
|
104
106
|
return { disposition: "apply", target };
|
|
105
107
|
}
|
|
106
108
|
export const adminEnrollmentTokenRevokeCapability = defineCapability({
|
|
107
|
-
id: "
|
|
109
|
+
id: "enrollment.token.revoke",
|
|
108
110
|
domain: "fleet",
|
|
109
111
|
roles: ["admin"],
|
|
110
|
-
description: "
|
|
112
|
+
description: "Internal staff only: revoke one exact enrollment-token generation after reconciling its durable goal.",
|
|
111
113
|
mutation: "preview-confirm",
|
|
112
|
-
http: { method: "DELETE", path: "/enrollment-tokens/{
|
|
114
|
+
http: { method: "DELETE", path: "/enrollment-tokens/{tokenId}" },
|
|
113
115
|
cli: {
|
|
114
116
|
command: ["admin", "enrollment-tokens", "revoke"],
|
|
115
|
-
vnext: adminHelp(["admin", "enrollment-tokens", "revoke"], "Revoke one exact enrollment-token generation.", "ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] [
|
|
117
|
+
vnext: adminHelp(["admin", "enrollment-tokens", "revoke"], "Revoke one exact enrollment-token generation.", "ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] []", "ornn admin enrollment-tokens revoke <token-id> --expected-created-at TIME --idempotency-key KEY --format json", ["canonical token UUID", "token created_at generation"], "Review the exact token generation and the loss of future enrollment access."),
|
|
116
118
|
},
|
|
117
|
-
mcp: false,
|
|
118
119
|
slack: false,
|
|
120
|
+
mcp: { name: "enrollment_token_revoke" },
|
|
119
121
|
input: tokenRevokeInput,
|
|
120
122
|
execute: async (ctx, input) => {
|
|
121
123
|
const parsed = tokenRevokeInput.parse(input);
|
|
122
|
-
const
|
|
124
|
+
const tokenId = parsed.tokenId ?? parsed.token_id;
|
|
125
|
+
const current = await readExactToken(ctx, tokenId, parsed.expected_created_at);
|
|
123
126
|
if (!ctx.confirmed)
|
|
124
127
|
return current;
|
|
125
128
|
if (current.disposition === "conflict") {
|
|
@@ -127,23 +130,21 @@ export const adminEnrollmentTokenRevokeCapability = defineCapability({
|
|
|
127
130
|
}
|
|
128
131
|
if (current.disposition === "satisfied") {
|
|
129
132
|
return {
|
|
130
|
-
id:
|
|
133
|
+
id: tokenId,
|
|
131
134
|
created_at: parsed.expected_created_at ?? current.target?.created_at ?? null,
|
|
132
135
|
status: "revoked",
|
|
133
136
|
reconciled: true,
|
|
134
137
|
};
|
|
135
138
|
}
|
|
136
139
|
if (!current.target.created_at) {
|
|
137
|
-
throw codedError("token_identity_unavailable", `Enrollment token ${
|
|
140
|
+
throw codedError("token_identity_unavailable", `Enrollment token ${tokenId} has no stable creation identity and cannot be revoked safely.`);
|
|
138
141
|
}
|
|
139
|
-
const revoked = (await ctx.fetch(`/enrollment-tokens/${encodeURIComponent(
|
|
142
|
+
const revoked = (await ctx.fetch(`/enrollment-tokens/${encodeURIComponent(tokenId)}`, {
|
|
140
143
|
method: "DELETE",
|
|
141
|
-
...(parsed.idempotency_key
|
|
142
|
-
? { headers: { "Idempotency-Key": parsed.idempotency_key } }
|
|
143
|
-
: {}),
|
|
144
|
+
...(parsed.idempotency_key ? { headers: { "Idempotency-Key": parsed.idempotency_key } } : {}),
|
|
144
145
|
}));
|
|
145
146
|
return {
|
|
146
|
-
id:
|
|
147
|
+
id: tokenId,
|
|
147
148
|
created_at: current.target.created_at,
|
|
148
149
|
organization_id: current.target.organization_id,
|
|
149
150
|
expires_at: current.target.expires_at,
|
|
@@ -190,7 +191,7 @@ export const adminNodeEnrollCapability = defineCapability({
|
|
|
190
191
|
http: { method: "POST", path: "/remote" },
|
|
191
192
|
cli: {
|
|
192
193
|
command: ["admin", "nodes", "enroll"],
|
|
193
|
-
vnext: adminHelp(["admin", "nodes", "enroll"], "Install Ornn management on one explicitly assigned node.", "ornn admin nodes enroll <node-id> --host HOST --supplier UUID --facility UUID [--filter COMPARTMENT_IDS] --identity-file PATH --token-ttl SECONDS --replace-existing-management [--verbose] [
|
|
194
|
+
vnext: adminHelp(["admin", "nodes", "enroll"], "Install Ornn management on one explicitly assigned node.", "ornn admin nodes enroll <node-id> --host HOST --supplier UUID --facility UUID [--filter COMPARTMENT_IDS] --identity-file PATH --token-ttl SECONDS --replace-existing-management [--verbose] []", "Unsupported: remote enrollment requires an eligible human terminal.", ["canonical node UUID", "canonical supplier organization UUID", "canonical facility UUID"], "Review node identity, SSH target, assignment, compartment filter, and replacement of existing management."),
|
|
194
195
|
},
|
|
195
196
|
mcp: false,
|
|
196
197
|
slack: false,
|
|
@@ -291,7 +292,7 @@ export const adminNodeRotateKeyCapability = defineCapability({
|
|
|
291
292
|
http: { method: "POST", path: "/enrollment/admin-ssh-key" },
|
|
292
293
|
cli: {
|
|
293
294
|
command: ["admin", "nodes", "rotate-key"],
|
|
294
|
-
vnext: adminHelp(["admin", "nodes", "rotate-key"], "Rotate one node admin key and export the replacement deliberately.", "ornn admin nodes rotate-key <node-id> (--print | --output PATH) [--overwrite] [
|
|
295
|
+
vnext: adminHelp(["admin", "nodes", "rotate-key"], "Rotate one node admin key and export the replacement deliberately.", "ornn admin nodes rotate-key <node-id> (--print | --output PATH) [--overwrite] []", "Unsupported: key rotation requires an eligible human terminal.", ["canonical node UUID"], "Review the node and loss of access through the previous private key."),
|
|
295
296
|
},
|
|
296
297
|
mcp: false,
|
|
297
298
|
slack: false,
|
|
@@ -18,16 +18,10 @@ const rebootInput = z.object({
|
|
|
18
18
|
const hardResetInput = rebootInput.extend({
|
|
19
19
|
erase_local_data: z.literal(true),
|
|
20
20
|
});
|
|
21
|
-
const terminationInput = z.object({
|
|
22
|
-
...commonInput,
|
|
23
|
-
delete_node_record: z.literal(true),
|
|
24
|
-
skip_cleanup: z.boolean().optional(),
|
|
25
|
-
});
|
|
26
21
|
const assignmentInput = z.object({
|
|
27
22
|
...commonInput,
|
|
28
23
|
reservation_id: z.string().trim().min(1),
|
|
29
24
|
});
|
|
30
|
-
const managementInput = z.object(commonInput);
|
|
31
25
|
function adminLifecycleHelp(command, outcome, human, automation, review) {
|
|
32
26
|
return {
|
|
33
27
|
audience: "admin",
|
|
@@ -83,7 +77,7 @@ export const adminNodeRebootCapability = defineCapability({
|
|
|
83
77
|
http: { method: "POST", path: "/nodes/{id}/reboot" },
|
|
84
78
|
cli: {
|
|
85
79
|
command: ["admin", "nodes", "reboot"],
|
|
86
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "reboot"], "Reboot one exact live machine while preserving local data and access configuration.", "ornn admin nodes reboot <node-ref> [
|
|
80
|
+
vnext: adminLifecycleHelp(["admin", "nodes", "reboot"], "Reboot one exact live machine while preserving local data and access configuration.", "ornn admin nodes reboot <node-ref> [] [--no-wait] [--timeout SECONDS]", "Unsupported: reboot requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, and preserved access."),
|
|
87
81
|
},
|
|
88
82
|
mcp: false,
|
|
89
83
|
slack: false,
|
|
@@ -99,102 +93,13 @@ export const adminNodeHardResetCapability = defineCapability({
|
|
|
99
93
|
http: { method: "POST", path: "/nodes/{id}/reboot" },
|
|
100
94
|
cli: {
|
|
101
95
|
command: ["admin", "nodes", "hard-reset"],
|
|
102
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "hard-reset"], "Erase local tenant data and hard reset one exact live machine.", "ornn admin nodes hard-reset <node-ref> --erase-local-data [
|
|
96
|
+
vnext: adminLifecycleHelp(["admin", "nodes", "hard-reset"], "Erase local tenant data and hard reset one exact live machine.", "ornn admin nodes hard-reset <node-ref> --erase-local-data [] [--no-wait] [--timeout SECONDS]", "Unsupported: hard reset requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, erased local data, and restored keys."),
|
|
103
97
|
},
|
|
104
98
|
mcp: false,
|
|
105
99
|
slack: false,
|
|
106
100
|
input: hardResetInput,
|
|
107
101
|
execute: (ctx, input) => reboot(ctx, input, "hard_reset"),
|
|
108
102
|
});
|
|
109
|
-
export const adminNodeSuspendManagementCapability = defineCapability({
|
|
110
|
-
id: "nodes.suspend_management",
|
|
111
|
-
domain: "fleet",
|
|
112
|
-
roles: ["admin"],
|
|
113
|
-
description: "Suspend Ornn management while leaving the exact host powered on.",
|
|
114
|
-
mutation: "preview-confirm",
|
|
115
|
-
http: { method: "POST", path: "/nodes/{id}/off-grid" },
|
|
116
|
-
cli: {
|
|
117
|
-
command: ["admin", "nodes", "suspend-management"],
|
|
118
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "suspend-management"], "Remove Ornn management and managed access from one powered-on node.", "ornn admin nodes suspend-management <node-ref> [--reason TEXT] [--yes] [--no-wait]", "ornn admin nodes suspend-management <node-id> --yes --idempotency-key KEY --format json", "Node, supplier, facility, capacity assignment, management removal, and managed-access loss."),
|
|
119
|
-
},
|
|
120
|
-
mcp: false,
|
|
121
|
-
slack: false,
|
|
122
|
-
input: managementInput,
|
|
123
|
-
execute: async (ctx, input) => {
|
|
124
|
-
const parsed = managementInput.parse(input);
|
|
125
|
-
const id = nodeId(parsed);
|
|
126
|
-
const body = {
|
|
127
|
-
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
128
|
-
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
129
|
-
};
|
|
130
|
-
if (!ctx.confirmed)
|
|
131
|
-
return preview("nodes.suspend_management", { node_id: id, ...body });
|
|
132
|
-
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/off-grid`, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
body,
|
|
135
|
-
headers: requestHeaders(parsed.idempotency_key),
|
|
136
|
-
});
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
export const adminNodeResumeManagementCapability = defineCapability({
|
|
140
|
-
id: "nodes.resume_management",
|
|
141
|
-
domain: "fleet",
|
|
142
|
-
roles: ["admin"],
|
|
143
|
-
description: "Resume Ornn management for one exact off-grid node.",
|
|
144
|
-
mutation: "preview-confirm",
|
|
145
|
-
http: { method: "POST", path: "/nodes/{id}/on-grid" },
|
|
146
|
-
cli: {
|
|
147
|
-
command: ["admin", "nodes", "resume-management"],
|
|
148
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "resume-management"], "Restore Ornn management and managed access on one off-grid node.", "ornn admin nodes resume-management <node-ref> [--yes] [--no-wait]", "ornn admin nodes resume-management <node-id> --yes --idempotency-key KEY --format json", "Node, supplier, facility, capacity assignment, remote re-enrollment, and restored managed access."),
|
|
149
|
-
},
|
|
150
|
-
mcp: false,
|
|
151
|
-
slack: false,
|
|
152
|
-
input: managementInput,
|
|
153
|
-
execute: async (ctx, input) => {
|
|
154
|
-
const parsed = managementInput.parse(input);
|
|
155
|
-
const id = nodeId(parsed);
|
|
156
|
-
if (!ctx.confirmed)
|
|
157
|
-
return preview("nodes.resume_management", { node_id: id });
|
|
158
|
-
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/on-grid`, {
|
|
159
|
-
method: "POST",
|
|
160
|
-
body: {
|
|
161
|
-
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
162
|
-
},
|
|
163
|
-
headers: requestHeaders(parsed.idempotency_key),
|
|
164
|
-
});
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
export const adminNodeTerminateCapability = defineCapability({
|
|
168
|
-
id: "nodes.terminate",
|
|
169
|
-
domain: "fleet",
|
|
170
|
-
roles: ["admin"],
|
|
171
|
-
description: "Delete one exact node record with explicit cleanup scope and consequence review.",
|
|
172
|
-
mutation: "preview-confirm",
|
|
173
|
-
http: { method: "POST", path: "/nodes/{id}/terminate" },
|
|
174
|
-
cli: {
|
|
175
|
-
command: ["admin", "nodes", "terminate"],
|
|
176
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "terminate"], "End managed access and permanently delete one exact node record.", "ornn admin nodes terminate <node-ref> --delete-node-record [--skip-cleanup] [--reason TEXT] [--yes]", "ornn admin nodes terminate <node-id> --delete-node-record --yes --idempotency-key KEY --format json", "Node, supplier, facility, reservation/capacity, host cleanup, node-record deletion, and access loss."),
|
|
177
|
-
},
|
|
178
|
-
mcp: false,
|
|
179
|
-
slack: false,
|
|
180
|
-
input: terminationInput,
|
|
181
|
-
execute: async (ctx, input) => {
|
|
182
|
-
const parsed = terminationInput.parse(input);
|
|
183
|
-
const id = nodeId(parsed);
|
|
184
|
-
const body = {
|
|
185
|
-
force: parsed.skip_cleanup === true,
|
|
186
|
-
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
187
|
-
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
188
|
-
};
|
|
189
|
-
if (!ctx.confirmed)
|
|
190
|
-
return preview("nodes.terminate", { node_id: id, ...body });
|
|
191
|
-
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/terminate`, {
|
|
192
|
-
method: "POST",
|
|
193
|
-
body,
|
|
194
|
-
headers: requestHeaders(parsed.idempotency_key),
|
|
195
|
-
});
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
103
|
export const adminNodeAssignCapability = defineCapability({
|
|
199
104
|
id: "nodes.assign",
|
|
200
105
|
domain: "fleet",
|
|
@@ -204,7 +109,7 @@ export const adminNodeAssignCapability = defineCapability({
|
|
|
204
109
|
http: { method: "POST", path: "/nodes/{id}/assign" },
|
|
205
110
|
cli: {
|
|
206
111
|
command: ["admin", "nodes", "assign"],
|
|
207
|
-
vnext: adminLifecycleHelp(["admin", "nodes", "assign"], "Assign one exact node to one exact capacity reservation.", "ornn admin nodes assign <node-ref> --reservation <reservation-id> [
|
|
112
|
+
vnext: adminLifecycleHelp(["admin", "nodes", "assign"], "Assign one exact node to one exact capacity reservation.", "ornn admin nodes assign <node-ref> --reservation <reservation-id> [] [--no-wait]", "ornn admin nodes assign <node-id> --reservation <reservation-id> --idempotency-key KEY --format json", "Node, supplier, facility, previous and requested capacity assignments, and tenant access change."),
|
|
208
113
|
},
|
|
209
114
|
mcp: false,
|
|
210
115
|
slack: false,
|
|
@@ -228,8 +133,5 @@ export const adminNodeAssignCapability = defineCapability({
|
|
|
228
133
|
export const adminNodeLifecycleCapabilities = [
|
|
229
134
|
adminNodeRebootCapability,
|
|
230
135
|
adminNodeHardResetCapability,
|
|
231
|
-
adminNodeSuspendManagementCapability,
|
|
232
|
-
adminNodeResumeManagementCapability,
|
|
233
|
-
adminNodeTerminateCapability,
|
|
234
136
|
adminNodeAssignCapability,
|
|
235
137
|
];
|
|
@@ -36,9 +36,9 @@ function withAdminBinding(capability, help) {
|
|
|
36
36
|
},
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
export const adminUsersListCapability = withAdminBinding(usersListCapability, adminHelp(["admin", "users", "list"], "List users
|
|
40
|
-
export const adminOrganizationsListCapability = withAdminBinding(organizationListCapability, adminHelp(["admin", "organizations", "list"], "List organizations in the Authorization directory.", "ornn admin organizations list [--query QUERY] [--role consumer|supplier] [--approved true|false] [--ids UUID,...] [--limit 25] [--offset OFFSET]
|
|
41
|
-
export const adminSuppliersListCapability = withAdminBinding(listSuppliersCapability, adminHelp(["admin", "suppliers", "list"], "List supplier organizations visible to staff.", "ornn admin suppliers list [--limit 50] [--
|
|
39
|
+
export const adminUsersListCapability = withAdminBinding(usersListCapability, adminHelp(["admin", "users", "list"], "List users in the Authorization directory; pass next_offset as --offset to continue.", "ornn admin users list [--query QUERY] [--organization UUID] [--limit 25] [--offset OFFSET]", "ornn admin users list --query QUERY --limit 25 [--offset OFFSET] --format json"));
|
|
40
|
+
export const adminOrganizationsListCapability = withAdminBinding(organizationListCapability, adminHelp(["admin", "organizations", "list"], "List organizations in the Authorization directory; pass next_offset as --offset to continue.", "ornn admin organizations list [--query QUERY] [--role consumer|supplier] [--approved true|false] [--ids UUID,...] [--limit 25] [--offset OFFSET]", "ornn admin organizations list --query QUERY --limit 25 [--offset OFFSET] --format json"));
|
|
41
|
+
export const adminSuppliersListCapability = withAdminBinding(listSuppliersCapability, adminHelp(["admin", "suppliers", "list"], "List supplier organizations visible to staff; pass next_offset as --offset to continue.", "ornn admin suppliers list [--limit 50] [--offset OFFSET]", "ornn admin suppliers list --limit 50 --offset OFFSET --format json"));
|
|
42
42
|
export const adminFacilitiesListCapability = withAdminBinding(listFacilitiesCapability, adminHelp(["admin", "facilities", "list"], "List facilities visible to staff.", "ornn admin facilities list [--supplier UUID] [--limit 50] [--cursor CURSOR]", "ornn admin facilities list --limit 50 --format json"));
|
|
43
43
|
export const adminNodesListCapability = withAdminBinding(listNodesCapability, adminHelp(["admin", "nodes", "list"], "List enrolled, ghost, and off-grid nodes.", "ornn admin nodes list [--supplier UUID] [--facility FACILITY] [--status STATUS] [--reservation UUID] [--limit 50] [--cursor CURSOR]", "ornn admin nodes list --limit 50 [--cursor CURSOR] --format json"));
|
|
44
44
|
const nodeInput = z.object({ node_id: z.string().min(1) });
|
|
@@ -204,7 +204,7 @@ export const clusterCliCapabilities = [
|
|
|
204
204
|
mutation: "preview-confirm",
|
|
205
205
|
outcome: "Add one currently eligible machine, or rediscover that exact membership goal.",
|
|
206
206
|
human: "ornn clusters add-node [<cluster-id>] [<machine-id-or-hostname>] [--guided] [--no-wait]",
|
|
207
|
-
automation: "ornn clusters add-node <cluster-id> <machine-id>
|
|
207
|
+
automation: "ornn clusters add-node <cluster-id> <machine-id> --idempotency-key <key> --format json",
|
|
208
208
|
acceptedReferences: [
|
|
209
209
|
"canonical cluster ID",
|
|
210
210
|
"canonical machine ID",
|
|
@@ -221,7 +221,7 @@ export const clusterCliCapabilities = [
|
|
|
221
221
|
mutation: "preview-confirm",
|
|
222
222
|
outcome: "Remove one exact member, or rediscover that exact membership goal.",
|
|
223
223
|
human: "ornn clusters remove-node [<cluster-id>] [<machine-id-or-hostname>] [--guided] [--no-wait]",
|
|
224
|
-
automation: "ornn clusters remove-node <cluster-id> <machine-id>
|
|
224
|
+
automation: "ornn clusters remove-node <cluster-id> <machine-id> --idempotency-key <key> --format json",
|
|
225
225
|
acceptedReferences: [
|
|
226
226
|
"canonical cluster ID",
|
|
227
227
|
"canonical machine ID",
|
|
@@ -238,7 +238,7 @@ export const clusterCliCapabilities = [
|
|
|
238
238
|
mutation: "preview-confirm",
|
|
239
239
|
outcome: "End cluster lifecycle while reporting partial member failures and continuing reservation charges.",
|
|
240
240
|
human: "ornn clusters teardown [<cluster-id>] [--guided] [--no-wait]",
|
|
241
|
-
automation: "ornn clusters teardown <cluster-id>
|
|
241
|
+
automation: "ornn clusters teardown <cluster-id> --idempotency-key <key> --format json",
|
|
242
242
|
acceptedReferences: ["canonical cluster ID", "invocation-local guided choice"],
|
|
243
243
|
review: "The cluster and native access end; the capacity reservation and its charges remain.",
|
|
244
244
|
related: ["clusters show", "reservations show"],
|
|
@@ -33,7 +33,7 @@ export const listingsListCapability = defineCapability({
|
|
|
33
33
|
id: "listings.list",
|
|
34
34
|
domain: "listings",
|
|
35
35
|
roles: ["user"],
|
|
36
|
-
description: "List native capacity listings. Primary listings require organization approval. Returns capacityListingId, hardware, capacity and availability;
|
|
36
|
+
description: "List native capacity listings. Primary listings require organization approval. Returns capacityListingId, hardware, capacity and availability; when nextCursor is present, pass it back as cursor for the next page. Read listings.terms separately for prices and durations.",
|
|
37
37
|
mutation: "read",
|
|
38
38
|
http: { method: "GET", path: "/commerce/v2/listings" },
|
|
39
39
|
input: listingsListInput,
|
|
@@ -142,13 +142,13 @@ export const listingRelistCapability = defineCapability({
|
|
|
142
142
|
id: "listings.relist",
|
|
143
143
|
domain: "listings",
|
|
144
144
|
roles: ["user"],
|
|
145
|
-
description: "Relist whole nodes of a reservation's capacity using a native availability window, nodeCount and financial terms. Buyers run on nodes Ornn assigns when their rental starts
|
|
145
|
+
description: "Relist whole nodes of a reservation's capacity using a native availability window, nodeCount and financial terms. Buyers run on nodes Ornn assigns when their rental starts..",
|
|
146
146
|
mutation: "preview-confirm",
|
|
147
147
|
http: { method: "POST", path: "/commerce/reservations/{reservationId}/relist" },
|
|
148
148
|
input: listingRelistInput,
|
|
149
149
|
cli: {
|
|
150
150
|
command: ["capacity-offers", "relist"],
|
|
151
|
-
vnext: help(["capacity-offers", "relist"], "Relist reserved capacity with explicit financial terms.", "ornn capacity-offers relist --reservation-id UUID --node-count COUNT --starts-at TIME --ends-at TIME --terms JSON", "ornn capacity-offers relist --reservation-id UUID --node-count 1 --starts-at TIME --ends-at TIME --terms JSON --idempotency-key KEY
|
|
151
|
+
vnext: help(["capacity-offers", "relist"], "Relist reserved capacity with explicit financial terms.", "ornn capacity-offers relist --reservation-id UUID --node-count COUNT --starts-at TIME --ends-at TIME --terms JSON", "ornn capacity-offers relist --reservation-id UUID --node-count 1 --starts-at TIME --ends-at TIME --terms JSON --idempotency-key KEY --format json", ["capacity-offers terms", "capacity-offers delist"], 24, ["reservation ID"]),
|
|
152
152
|
},
|
|
153
153
|
slack: false,
|
|
154
154
|
execute: async (ctx, input) => {
|
|
@@ -179,7 +179,7 @@ export const listingUpdateCapability = defineCapability({
|
|
|
179
179
|
id: "listings.update",
|
|
180
180
|
domain: "listings",
|
|
181
181
|
roles: ["admin"],
|
|
182
|
-
description: "Staff only: edit one advertised native financial term with its complete financial fields. Reuse idempotencyKey on retries
|
|
182
|
+
description: "Staff only: edit one advertised native financial term with its complete financial fields. Reuse idempotencyKey on retries..",
|
|
183
183
|
mutation: "preview-confirm",
|
|
184
184
|
http: { method: "PATCH", path: "/commerce/v2/listings/{listingId}/terms/{listingTermsId}" },
|
|
185
185
|
input: listingUpdateInput,
|
|
@@ -212,13 +212,13 @@ export const listingDelistCapability = defineCapability({
|
|
|
212
212
|
id: "listings.delist",
|
|
213
213
|
domain: "listings",
|
|
214
214
|
roles: ["user"],
|
|
215
|
-
description: "Delist a listing you own. Its unsold time leaves the market, its spot asks are cancelled and running spot reservations on it end. Windows already bought stay sold
|
|
215
|
+
description: "Delist a listing you own. Its unsold time leaves the market, its spot asks are cancelled and running spot reservations on it end. Windows already bought stay sold..",
|
|
216
216
|
mutation: "preview-confirm",
|
|
217
217
|
http: { method: "POST", path: "/commerce/v2/listings/{listingId}/delist" },
|
|
218
218
|
input: listingDelistInput,
|
|
219
219
|
cli: {
|
|
220
220
|
command: ["capacity-offers", "delist"],
|
|
221
|
-
vnext: help(["capacity-offers", "delist"], "Delist a listing you relisted.", "ornn capacity-offers delist --listing-id <id>", "ornn capacity-offers delist --listing-id <id>
|
|
221
|
+
vnext: help(["capacity-offers", "delist"], "Delist a listing you relisted.", "ornn capacity-offers delist --listing-id <id>", "ornn capacity-offers delist --listing-id <id> --format json", ["capacity-offers relist"], 26, ["listing ID"]),
|
|
222
222
|
},
|
|
223
223
|
slack: false,
|
|
224
224
|
execute: async (ctx, input) => {
|
|
@@ -256,7 +256,7 @@ export const reservationsListCapability = defineCapability({
|
|
|
256
256
|
id: "reservations.list",
|
|
257
257
|
domain: "reservations",
|
|
258
258
|
roles: ["user"],
|
|
259
|
-
description: "List this organization's reservations, schedule and spot alike (a running spot reservation has no end time). With listingId, list the trades on a listing you own.",
|
|
259
|
+
description: "List this organization's reservations, schedule and spot alike (a running spot reservation has no end time). With listingId, list the trades on a listing you own. When nextCursor is present, pass it back as cursor for the next page.",
|
|
260
260
|
mutation: "read",
|
|
261
261
|
http: { method: "GET", path: "/commerce/reservations" },
|
|
262
262
|
input: reservationsListInput,
|
|
@@ -348,13 +348,13 @@ export const reservationPurchaseCapability = defineCapability({
|
|
|
348
348
|
id: "reservations.purchase",
|
|
349
349
|
domain: "reservations",
|
|
350
350
|
roles: ["user"],
|
|
351
|
-
description: "Book a window of a relisted (secondary) listing on one of its terms at the term's price. The GPUs are held for you until the window starts; then it becomes your reservation, with the booking's ID. Nothing is charged when you book. Primary listings take offers instead (offers.post)
|
|
351
|
+
description: "Book a window of a relisted (secondary) listing on one of its terms at the term's price. The GPUs are held for you until the window starts; then it becomes your reservation, with the booking's ID. Nothing is charged when you book. Primary listings take offers instead (offers.post)..",
|
|
352
352
|
mutation: "preview-confirm",
|
|
353
353
|
http: { method: "POST", path: "/v1/markets/bookings" },
|
|
354
354
|
input: reservationPurchaseInput,
|
|
355
355
|
cli: {
|
|
356
356
|
command: ["reservations", "buy"],
|
|
357
|
-
vnext: help(["reservations", "buy"], "Book a window of relisted capacity at its term's price.", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd <n> [--node-count <n>]", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd 4000000 --idempotency-key <key>
|
|
357
|
+
vnext: help(["reservations", "buy"], "Book a window of relisted capacity at its term's price.", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd <n> [--node-count <n>]", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd 4000000 --idempotency-key <key> --format json", ["capacity-offers availability", "capacity-offers terms", "reservations show"], 30, ["listingTermsId from capacity-offers terms"]),
|
|
358
358
|
},
|
|
359
359
|
slack: false,
|
|
360
360
|
execute: async (ctx, input) => {
|