@ornncompute/cli 1.0.48 → 1.0.63
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 +65 -47
- package/package.json +2 -2
- package/src/admin-capacity-reservations.mjs +36 -54
- package/src/admin-command-handlers.mjs +2 -0
- package/src/admin-credits.mjs +57 -0
- package/src/admin-enrollment.mjs +1 -1
- package/src/admin-node-diagnostics.mjs +1 -1
- package/src/admin-node-lifecycle.mjs +1 -1
- package/src/admin-shell.mjs +13 -3
- package/src/catalog-shell.mjs +4 -7
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +32 -15
- package/vendor/capabilities/capabilities/admin-enrollment.js +5 -5
- package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +8 -8
- package/vendor/capabilities/capabilities/admin.js +3 -3
- package/vendor/capabilities/capabilities/commerce-market.js +122 -22
- package/vendor/capabilities/capabilities/credits.js +78 -0
- package/vendor/capabilities/capabilities/fleet.js +14 -13
- package/vendor/capabilities/capabilities/listings.js +1 -1
- package/vendor/capabilities/capabilities/market-shared.js +2 -0
- package/vendor/capabilities/capabilities/markets.js +255 -222
- package/vendor/capabilities/capabilities/native-listing.js +1 -1
- package/vendor/capabilities/capabilities/reservation-list.js +68 -0
- package/vendor/capabilities/capabilities/staff-shared.js +64 -0
- package/vendor/capabilities/capabilities/tokens.js +1 -1
- package/vendor/capabilities/cli-catalog.js +4 -0
- package/vendor/capabilities/cli-migrations.js +12 -8
|
@@ -53,7 +53,7 @@ export const nativeListingUpdateFields = {
|
|
|
53
53
|
facilityId: z.string().uuid().optional(),
|
|
54
54
|
provisioningType: z.enum(["bare-metal", "virtual-machine"]).optional(),
|
|
55
55
|
availableAt: z.iso.datetime({ offset: true }).optional(),
|
|
56
|
-
availableUntil: z.iso.datetime({ offset: true }).optional(),
|
|
56
|
+
availableUntil: z.iso.datetime({ offset: true }).nullable().optional(),
|
|
57
57
|
daysToRfs: z.coerce.number().int().nonnegative().optional(),
|
|
58
58
|
cpu: z.string().optional(),
|
|
59
59
|
ram: z.string().optional(),
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineCapability } from "../capability.js";
|
|
3
|
+
import { queryString } from "./staff-shared.js";
|
|
4
|
+
export const reservationListInput = z.object({
|
|
5
|
+
customerId: z.string().uuid().optional(),
|
|
6
|
+
listingId: z.string().uuid().optional(),
|
|
7
|
+
listingTermId: z.string().uuid().optional(),
|
|
8
|
+
status: z.enum(["confirmed", "active", "completed", "cancelled"]).optional(),
|
|
9
|
+
cursor: z.string().optional(),
|
|
10
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
11
|
+
});
|
|
12
|
+
// Staff on every surface: Slack and the CLI resolve a customer's reservations
|
|
13
|
+
// with this, then nodes.list reservation_id for that reservation's machines.
|
|
14
|
+
export const reservationListCapability = defineCapability({
|
|
15
|
+
id: "reservation.list",
|
|
16
|
+
domain: "reservations",
|
|
17
|
+
roles: ["admin"],
|
|
18
|
+
description: "Internal staff only: list Commerce reservations across organizations, optionally filtered by customer organization (customerId), listing, listing term, and derived status. Resolve a customer's organization with users.list or organization.list first; pass each reservationId to nodes.list reservation_id for its machines. Page with nextCursor as cursor.",
|
|
19
|
+
mutation: "read",
|
|
20
|
+
http: { method: "GET", path: "/commerce/reservations" },
|
|
21
|
+
cli: {
|
|
22
|
+
command: ["admin", "reservations", "list"],
|
|
23
|
+
vnext: {
|
|
24
|
+
audience: "admin",
|
|
25
|
+
category: "resource",
|
|
26
|
+
command: ["admin", "reservations", "list"],
|
|
27
|
+
outcome: "List reservations across organizations.",
|
|
28
|
+
human: "ornn admin reservations list [--customer UUID] [--listing UUID] [--listing-term UUID] [--status confirmed|active|completed|cancelled] [--limit 50] [--cursor CURSOR]",
|
|
29
|
+
automation: "ornn admin reservations list --customer UUID --limit 50 [--cursor CURSOR] --format json",
|
|
30
|
+
acceptedReferences: [],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
slack: {
|
|
34
|
+
parameters: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
customerId: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Optional customer organization UUID from users_list or organization_list.",
|
|
40
|
+
},
|
|
41
|
+
listingId: { type: "string", description: "Optional listing UUID." },
|
|
42
|
+
listingTermId: { type: "string", description: "Optional listing term UUID." },
|
|
43
|
+
status: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["confirmed", "active", "completed", "cancelled"],
|
|
46
|
+
description: "Optional derived reservation status.",
|
|
47
|
+
},
|
|
48
|
+
cursor: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Optional. Pass the previous response's nextCursor to fetch the next page.",
|
|
51
|
+
},
|
|
52
|
+
limit: { type: "integer", description: "Optional page size (Slack returns at most 10)." },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
input: reservationListInput,
|
|
57
|
+
execute: async (ctx, input) => {
|
|
58
|
+
const parsed = reservationListInput.parse(input ?? {});
|
|
59
|
+
return ctx.fetch(`/commerce/reservations${queryString({
|
|
60
|
+
customerId: parsed.customerId,
|
|
61
|
+
listingId: parsed.listingId,
|
|
62
|
+
status: parsed.status,
|
|
63
|
+
listingTermId: parsed.listingTermId,
|
|
64
|
+
cursor: parsed.cursor,
|
|
65
|
+
limit: parsed.limit,
|
|
66
|
+
})}`, { method: "GET" });
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Helpers shared by staff capabilities. */
|
|
2
|
+
/**
|
|
3
|
+
* Build a request body from the fields that were actually supplied.
|
|
4
|
+
*
|
|
5
|
+
* Staff writes are almost all "PATCH the fields the caller named": `undefined`
|
|
6
|
+
* means leave alone, and `null` is a real value that clears the field. So the
|
|
7
|
+
* check is `!== undefined`, never truthiness.
|
|
8
|
+
*/
|
|
9
|
+
export function mapBody(input, mapping) {
|
|
10
|
+
const body = {};
|
|
11
|
+
for (const [field, wire] of Object.entries(mapping)) {
|
|
12
|
+
if (input[field] !== undefined)
|
|
13
|
+
body[wire] = input[field];
|
|
14
|
+
}
|
|
15
|
+
return body;
|
|
16
|
+
}
|
|
17
|
+
/** Path segment escape. */
|
|
18
|
+
export function seg(value) {
|
|
19
|
+
return encodeURIComponent(value);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A transport failure's HTTP status, when it carries one.
|
|
23
|
+
*
|
|
24
|
+
* Capabilities cannot import a surface's error class (`apps/mcp`'s
|
|
25
|
+
* `WebApiError`, say), so read the status structurally.
|
|
26
|
+
*/
|
|
27
|
+
export function statusOf(error) {
|
|
28
|
+
if (error && typeof error === "object" && "status" in error) {
|
|
29
|
+
const status = error.status;
|
|
30
|
+
if (typeof status === "number")
|
|
31
|
+
return status;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read a resource's current optimistic-concurrency version.
|
|
37
|
+
*
|
|
38
|
+
* Commerce rejects a PATCH or lifecycle action whose `version` is absent or
|
|
39
|
+
* non-positive ("missing version" / "version must be positive"), but no staff
|
|
40
|
+
* caller has that number to hand. Read it back when it was not supplied, and
|
|
41
|
+
* only once confirmed so a preview stays a pure dry run.
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveVersion(ctx, path, provided) {
|
|
44
|
+
if (provided && provided > 0)
|
|
45
|
+
return provided;
|
|
46
|
+
if (!ctx.confirmed)
|
|
47
|
+
return 1;
|
|
48
|
+
try {
|
|
49
|
+
const current = (await ctx.fetch(path, { method: "GET" }));
|
|
50
|
+
if (typeof current?.version === "number" && current.version > 0)
|
|
51
|
+
return current.version;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Fall through: send 1 and let commerce report the conflict, which names
|
|
55
|
+
// the expected version, rather than failing on a lookup the caller can't see.
|
|
56
|
+
}
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
export function queryString(params) {
|
|
60
|
+
const parts = Object.entries(params)
|
|
61
|
+
.filter((entry) => entry[1] !== undefined && entry[1] !== "")
|
|
62
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
63
|
+
return parts.length > 0 ? `?${parts.join("&")}` : "";
|
|
64
|
+
}
|
|
@@ -142,7 +142,7 @@ export const tokensCreateCapability = defineCapability({
|
|
|
142
142
|
}
|
|
143
143
|
const results = [];
|
|
144
144
|
for (const host of hosts) {
|
|
145
|
-
results.push(await ctx.fetch("/
|
|
145
|
+
results.push(await ctx.fetch("/remote", {
|
|
146
146
|
method: "POST",
|
|
147
147
|
body: {
|
|
148
148
|
host,
|
|
@@ -6,6 +6,7 @@ import { adminEnrollmentCapabilities, bindAdminEnrollmentCapabilities, } from ".
|
|
|
6
6
|
import { adminNodeDiagnosticCapabilities } from "./capabilities/admin-node-diagnostics.js";
|
|
7
7
|
import { adminNodeLifecycleCapabilities } from "./capabilities/admin-nodes-lifecycle.js";
|
|
8
8
|
import { computeCapabilities } from "./capabilities/compute.js";
|
|
9
|
+
import { creditGrantCapability } from "./capabilities/credits.js";
|
|
9
10
|
import { cliShellCapabilities } from "./capabilities/cli.js";
|
|
10
11
|
import { clusterCliCapabilities, kubernetesCapabilities, slurmCapabilities, } from "./capabilities/clusters.js";
|
|
11
12
|
import { externalBucketCapabilities } from "./capabilities/external-buckets.js";
|
|
@@ -18,6 +19,7 @@ import { listingCapabilities } from "./capabilities/listings.js";
|
|
|
18
19
|
import { observabilityCapabilities } from "./capabilities/observability.js";
|
|
19
20
|
import { organizationInviteCapability } from "./capabilities/organization-invites.js";
|
|
20
21
|
import { networkCapabilities } from "./capabilities/networks.js";
|
|
22
|
+
import { reservationListCapability } from "./capabilities/reservation-list.js";
|
|
21
23
|
import { volumeCapabilities } from "./capabilities/volumes.js";
|
|
22
24
|
import { userCapabilities } from "./capabilities/users.js";
|
|
23
25
|
function canonicalCliCatalog(capabilities) {
|
|
@@ -62,8 +64,10 @@ export const catalog = [
|
|
|
62
64
|
])),
|
|
63
65
|
...adminCapabilities.filter((capability) => capability.id === "nodes.health"),
|
|
64
66
|
...adminCapacityReservationCapabilities,
|
|
67
|
+
reservationListCapability,
|
|
65
68
|
...adminNodeDiagnosticCapabilities,
|
|
66
69
|
...adminEnrollmentCapabilities,
|
|
67
70
|
organizationInviteCapability,
|
|
71
|
+
creditGrantCapability,
|
|
68
72
|
]),
|
|
69
73
|
];
|
|
@@ -9,21 +9,25 @@ export const LEGACY_CLI_MIGRATIONS = [
|
|
|
9
9
|
{ legacy: ["catalog", "search"], replacement: "ornn capacity-offers list" },
|
|
10
10
|
{ legacy: ["gpu-types"], replacement: "ornn capacity-offers list" },
|
|
11
11
|
{ legacy: ["availability", "list"], replacement: "ornn capacity-offers list" },
|
|
12
|
-
{ legacy: ["availability", "forecast"], replacement: "ornn
|
|
12
|
+
{ legacy: ["availability", "forecast"], replacement: "ornn capacity-offers availability" },
|
|
13
13
|
{ legacy: ["availability", "show"], replacement: "ornn capacity-offers show" },
|
|
14
14
|
{ legacy: ["listing", "list"], replacement: "ornn capacity-offers list" },
|
|
15
|
-
{ legacy: ["listing", "forecast"], replacement: "ornn
|
|
15
|
+
{ legacy: ["listing", "forecast"], replacement: "ornn capacity-offers availability" },
|
|
16
16
|
{ legacy: ["listing", "show"], replacement: "ornn capacity-offers show" },
|
|
17
17
|
{ legacy: ["listings", "list"], replacement: "ornn capacity-offers list" },
|
|
18
|
-
{ legacy: ["listings", "forecast"], replacement: "ornn
|
|
18
|
+
{ legacy: ["listings", "forecast"], replacement: "ornn capacity-offers availability" },
|
|
19
19
|
{ legacy: ["listings", "show"], replacement: "ornn capacity-offers show" },
|
|
20
20
|
{ legacy: ["listings", "create"], replacement: "ornn admin capacity-offers create" },
|
|
21
21
|
{ legacy: ["buy"], replacement: "ornn offers post" },
|
|
22
|
-
{ legacy: ["
|
|
23
|
-
{ legacy: ["
|
|
24
|
-
{ legacy: ["
|
|
25
|
-
{ legacy: ["
|
|
26
|
-
{ legacy: ["bid", "
|
|
22
|
+
{ legacy: ["schedules", "list"], replacement: "ornn capacity-offers list --kind secondary" },
|
|
23
|
+
{ legacy: ["schedules", "show"], replacement: "ornn capacity-offers availability" },
|
|
24
|
+
{ legacy: ["schedules", "buy"], replacement: "ornn reservations buy" },
|
|
25
|
+
{ legacy: ["schedules"], replacement: "ornn capacity-offers list" },
|
|
26
|
+
{ legacy: ["bid", "create"], replacement: "ornn market bid" },
|
|
27
|
+
{ legacy: ["bid", "list"], replacement: "ornn market orders" },
|
|
28
|
+
{ legacy: ["bid", "show"], replacement: "ornn market orders" },
|
|
29
|
+
{ legacy: ["bid", "update"], replacement: "ornn market bid" },
|
|
30
|
+
{ legacy: ["bid", "withdraw"], replacement: "ornn market cancel" },
|
|
27
31
|
{
|
|
28
32
|
legacy: ["exchange", "create"],
|
|
29
33
|
replacement: "ornn capacity-offers list (use the Ornn web app for commercial workflows)",
|