@ornncompute/cli 1.0.35 → 1.0.62
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 +92 -47
- package/package.json +2 -2
- package/src/admin-capacity-reservations.mjs +65 -153
- package/src/admin-command-handlers.mjs +2 -0
- package/src/admin-credits.mjs +57 -0
- package/src/admin-node-lifecycle.mjs +48 -7
- package/src/admin-shell.mjs +113 -27
- package/src/catalog-shell.mjs +13 -21
- package/src/evidence-commands.mjs +30 -12
- package/src/kubernetes-commands.mjs +58 -16
- package/src/launch-mode-handlers.mjs +2 -2
- package/src/storage-commands.mjs +9 -1
- package/src/vm-commands.mjs +3 -3
- package/vendor/capabilities/capabilities/admin-capacity-reservations.js +55 -57
- package/vendor/capabilities/capabilities/admin-enrollment.js +3 -3
- package/vendor/capabilities/capabilities/admin.js +9 -7
- package/vendor/capabilities/capabilities/commerce-market.js +179 -104
- package/vendor/capabilities/capabilities/compute.js +4 -2
- package/vendor/capabilities/capabilities/credits.js +78 -0
- package/vendor/capabilities/capabilities/fleet.js +6 -5
- package/vendor/capabilities/capabilities/listings.js +58 -38
- package/vendor/capabilities/capabilities/market-shared.js +2 -0
- package/vendor/capabilities/capabilities/markets.js +254 -221
- package/vendor/capabilities/capabilities/native-listing.js +63 -0
- package/vendor/capabilities/capabilities/organization-invites.js +53 -0
- package/vendor/capabilities/capabilities/reservation-list.js +68 -0
- package/vendor/capabilities/capabilities/staff-shared.js +64 -0
- package/vendor/capabilities/capabilities/users.js +141 -31
- package/vendor/capabilities/cli-catalog.js +8 -2
- package/vendor/capabilities/cli-migrations.js +13 -10
- package/vendor/capabilities/software-catalog.js +52 -6
|
@@ -46,22 +46,16 @@ export const listingCreateInput = z.object({
|
|
|
46
46
|
.describe("End of the availability window. YYYY-MM-DD or RFC3339."),
|
|
47
47
|
available_from: z.string().optional().describe("Alias for available_start_at."),
|
|
48
48
|
available_to: z.string().optional().describe("Alias for available_end_at."),
|
|
49
|
-
buy_now_price_per_gpu_hour: z
|
|
50
|
-
.number()
|
|
51
|
-
.optional()
|
|
52
|
-
.describe("Price buyers pay, USD per GPU-hour. The spread over cost_per_gpu_hour is Ornn's."),
|
|
53
|
-
cost_per_gpu_hour: z
|
|
54
|
-
.number()
|
|
55
|
-
.optional()
|
|
56
|
-
.describe("Supplier price, USD per GPU-hour. Defaults to the buy-now price (spread 0)."),
|
|
49
|
+
buy_now_price_per_gpu_hour: z.number().optional().describe("Price buyers pay, USD per GPU-hour."),
|
|
57
50
|
deposit_percent: z.number().optional(),
|
|
58
51
|
down_payment_cents: z.number().int().nonnegative().optional(),
|
|
59
52
|
access_mode: z.enum(["bare-metal", "virtual-machine"]).optional(),
|
|
60
|
-
state: z.enum(["available", "
|
|
53
|
+
state: z.enum(["available", "draft"]).optional(),
|
|
61
54
|
price_omitted: z.boolean().optional(),
|
|
62
55
|
announce: z.boolean().optional(),
|
|
63
56
|
text: z.string().optional(),
|
|
64
57
|
confirm: z.boolean().optional(),
|
|
58
|
+
idempotencyKey: z.string().min(1).max(200).optional(),
|
|
65
59
|
});
|
|
66
60
|
const catalogTermsInput = z.object({ kind: z.enum(["gpu", "cpu", "node", "link"]).optional() });
|
|
67
61
|
const slackListingCreateParams = {
|
|
@@ -80,7 +74,6 @@ const slackListingCreateParams = {
|
|
|
80
74
|
available_start_at: { type: "string" },
|
|
81
75
|
available_end_at: { type: "string" },
|
|
82
76
|
buy_now_price_per_gpu_hour: { type: "number" },
|
|
83
|
-
cost_per_gpu_hour: { type: "number" },
|
|
84
77
|
deposit_percent: { type: "number" },
|
|
85
78
|
price_omitted: { type: "boolean" },
|
|
86
79
|
announce: { type: "boolean" },
|
|
@@ -166,6 +159,23 @@ export function primaryDownPaymentCents(terms) {
|
|
|
166
159
|
const totalCents = (terms.pricePerGpuHourMicroUsd * terms.nodeCount * terms.gpusPerNode * hours) / 10_000;
|
|
167
160
|
return Math.max(0, Math.round((totalCents * terms.percent) / 100));
|
|
168
161
|
}
|
|
162
|
+
/** Shared native creation body for Slack's preview and human-confirmed write. */
|
|
163
|
+
export function primaryListingBody(input, pricePerGpuHourMicroUsd, downPaymentCents) {
|
|
164
|
+
return {
|
|
165
|
+
...input,
|
|
166
|
+
kind: "primary",
|
|
167
|
+
terms: [
|
|
168
|
+
{
|
|
169
|
+
type: "reserved",
|
|
170
|
+
minDurationHours: 1 / 3600,
|
|
171
|
+
maxDurationHours: (Date.parse(input.availableUntil) - Date.parse(input.availableAt)) / 3600000,
|
|
172
|
+
renewable: false,
|
|
173
|
+
pricePerGpuHourMicroUsd,
|
|
174
|
+
downPaymentCents,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
169
179
|
/** Commerce parses listing windows as RFC3339 timestamps, not bare dates. */
|
|
170
180
|
function rfc3339(value) {
|
|
171
181
|
const trimmed = value.trim();
|
|
@@ -193,13 +203,13 @@ const LISTING_CREATE_INPUT_ONLY_KEYS = [
|
|
|
193
203
|
"site",
|
|
194
204
|
"kind",
|
|
195
205
|
"confirm",
|
|
206
|
+
"idempotencyKey",
|
|
196
207
|
"organizationId",
|
|
197
208
|
"organization_id",
|
|
198
209
|
"site_supplier",
|
|
199
210
|
"facilityId",
|
|
200
211
|
"facility_id",
|
|
201
212
|
"buy_now_price_per_gpu_hour",
|
|
202
|
-
"cost_per_gpu_hour",
|
|
203
213
|
"deposit_percent",
|
|
204
214
|
"down_payment_cents",
|
|
205
215
|
"access_mode",
|
|
@@ -208,12 +218,13 @@ const LISTING_CREATE_INPUT_ONLY_KEYS = [
|
|
|
208
218
|
];
|
|
209
219
|
export const createListingCapability = defineCapability({
|
|
210
220
|
id: "listings.create",
|
|
221
|
+
mcp: false,
|
|
211
222
|
domain: "listings",
|
|
212
223
|
roles: ["admin"],
|
|
213
224
|
description: "Create a primary marketplace listing (admin). gpu_type, cpu, node_model, and fabric_type must exact-match an orchestrator catalog display name, canonical, or alias; gpu_type becomes the listing's GPU slug. " +
|
|
214
|
-
"
|
|
225
|
+
"buy_now_price_per_gpu_hour is the customer price; deposit_percent is the upfront payment as a percentage of the full availability window. Nodes are attached afterwards. Slack never POSTs until a human replies confirm. MCP and CLI use native listing.create instead.",
|
|
215
226
|
mutation: "preview-confirm",
|
|
216
|
-
http: { method: "POST", path: "/commerce/listings" },
|
|
227
|
+
http: { method: "POST", path: "/commerce/v2/listings" },
|
|
217
228
|
slack: { parameters: slackListingCreateParams },
|
|
218
229
|
input: listingCreateInput,
|
|
219
230
|
execute: async (ctx, input) => {
|
|
@@ -244,14 +255,21 @@ export const createListingCapability = defineCapability({
|
|
|
244
255
|
const seenCursors = new Set();
|
|
245
256
|
let cursor;
|
|
246
257
|
do {
|
|
247
|
-
const response = (await ctx.fetch(queryPath("/provisioning/facilities", { cursor }), {
|
|
258
|
+
const response = (await ctx.fetch(queryPath("/provisioning/facilities", { cursor }), {
|
|
259
|
+
method: "GET",
|
|
260
|
+
}));
|
|
248
261
|
if (!Array.isArray(response.facilities))
|
|
249
262
|
throw new Error("invalid facility catalog");
|
|
250
|
-
matches.push(...response.facilities.filter((facility) => (!organizationId ||
|
|
263
|
+
matches.push(...response.facilities.filter((facility) => (!organizationId ||
|
|
264
|
+
(facility.organizationId ?? facility.organization_id) === organizationId) &&
|
|
251
265
|
(facilityId
|
|
252
266
|
? (facility.facilityId ?? facility.id) === facilityId
|
|
253
|
-
: !facilityQuery ||
|
|
254
|
-
|
|
267
|
+
: !facilityQuery ||
|
|
268
|
+
[
|
|
269
|
+
facility.facilityId ?? facility.id,
|
|
270
|
+
facility.region,
|
|
271
|
+
facility.displayName ?? facility.display_name,
|
|
272
|
+
].some((value) => value?.toLowerCase() === facilityQuery))));
|
|
255
273
|
cursor = response.nextCursor ?? undefined;
|
|
256
274
|
if (cursor) {
|
|
257
275
|
if (seenCursors.has(cursor) || seenCursors.size >= 49) {
|
|
@@ -274,12 +292,12 @@ export const createListingCapability = defineCapability({
|
|
|
274
292
|
const startsAt = rfc3339(parsed.available_start_at ??
|
|
275
293
|
parsed.available_from ??
|
|
276
294
|
parsed.starts_after ??
|
|
277
|
-
new Date().toISOString());
|
|
295
|
+
new Date(Math.floor(Date.now() / 1000) * 1000).toISOString());
|
|
278
296
|
const endsAt = rfc3339(parsed.available_end_at ??
|
|
279
297
|
parsed.available_to ??
|
|
280
298
|
parsed.ends_before ??
|
|
281
299
|
parsed.ends_after ??
|
|
282
|
-
new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString());
|
|
300
|
+
new Date(Math.floor(Date.now() / 1000) * 1000 + 30 * 24 * 60 * 60 * 1000).toISOString());
|
|
283
301
|
const nodeCount = parsed.node_count ?? parsed.min_nodes ?? 1;
|
|
284
302
|
const gpusPerNode = parsed.gpus_per_node ?? 8;
|
|
285
303
|
const boundPayload = bound.payload;
|
|
@@ -288,12 +306,7 @@ export const createListingCapability = defineCapability({
|
|
|
288
306
|
? resolveCatalogTerm(hardware.terms, "gpu", normalized.gpu_type).canonical
|
|
289
307
|
: null;
|
|
290
308
|
const buyerPrice = parsed.buy_now_price_per_gpu_hour;
|
|
291
|
-
const
|
|
292
|
-
const supplierMicro = supplierPrice != null ? microUsd(supplierPrice) : null;
|
|
293
|
-
const buyerMicro = buyerPrice != null ? microUsd(buyerPrice) : supplierMicro;
|
|
294
|
-
if (supplierMicro != null && buyerMicro != null && buyerMicro < supplierMicro) {
|
|
295
|
-
return { ok: false, error: "price_below_supplier_price" };
|
|
296
|
-
}
|
|
309
|
+
const buyerMicro = buyerPrice != null ? microUsd(buyerPrice) : null;
|
|
297
310
|
const downPaymentCents = parsed.down_payment_cents ??
|
|
298
311
|
(parsed.deposit_percent != null && buyerMicro != null
|
|
299
312
|
? primaryDownPaymentCents({
|
|
@@ -305,26 +318,23 @@ export const createListingCapability = defineCapability({
|
|
|
305
318
|
percent: parsed.deposit_percent,
|
|
306
319
|
})
|
|
307
320
|
: 0);
|
|
308
|
-
const listing = organizationId && facilityId && gpuSlug &&
|
|
309
|
-
? {
|
|
321
|
+
const listing = organizationId && facilityId && gpuSlug && buyerMicro != null
|
|
322
|
+
? primaryListingBody({
|
|
310
323
|
organizationId,
|
|
311
324
|
facilityId,
|
|
312
325
|
gpuSlug,
|
|
313
326
|
gpusPerNode,
|
|
314
|
-
|
|
315
|
-
startsAt,
|
|
316
|
-
endsAt,
|
|
327
|
+
provisioningType: parsed.access_mode ?? "virtual-machine",
|
|
328
|
+
availableAt: startsAt,
|
|
329
|
+
availableUntil: endsAt,
|
|
317
330
|
...(typeof draft.cpu === "string" ? { cpu: draft.cpu } : {}),
|
|
318
331
|
...(typeof draft.ram === "string" ? { ram: draft.ram } : {}),
|
|
319
332
|
...(typeof draft.storage === "string" ? { storage: draft.storage } : {}),
|
|
320
333
|
...(typeof draft.fabric_type === "string" ? { fabricType: draft.fabric_type } : {}),
|
|
321
334
|
...(typeof draft.internet === "string" ? { internet: draft.internet } : {}),
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
downPaymentCents,
|
|
326
|
-
...(parsed.state ? { state: parsed.state } : {}),
|
|
327
|
-
}
|
|
335
|
+
capacityNodeCount: nodeCount,
|
|
336
|
+
state: parsed.state ?? "available",
|
|
337
|
+
}, buyerMicro, downPaymentCents)
|
|
328
338
|
: null;
|
|
329
339
|
if (!ctx.confirmed) {
|
|
330
340
|
return preview("create_listing", {
|
|
@@ -337,10 +347,20 @@ export const createListingCapability = defineCapability({
|
|
|
337
347
|
if (!listing) {
|
|
338
348
|
return {
|
|
339
349
|
ok: false,
|
|
340
|
-
error: !organizationId || !facilityId
|
|
350
|
+
error: !organizationId || !facilityId
|
|
351
|
+
? "facility_required"
|
|
352
|
+
: gpuSlug
|
|
353
|
+
? "price_required"
|
|
354
|
+
: "gpu_required",
|
|
341
355
|
};
|
|
342
356
|
}
|
|
343
|
-
|
|
357
|
+
if (!parsed.idempotencyKey)
|
|
358
|
+
return { ok: false, error: "idempotency_key_required" };
|
|
359
|
+
return ctx.fetch("/commerce/v2/listings", {
|
|
360
|
+
method: "POST",
|
|
361
|
+
body: listing,
|
|
362
|
+
headers: { "Idempotency-Key": parsed.idempotencyKey },
|
|
363
|
+
});
|
|
344
364
|
},
|
|
345
365
|
});
|
|
346
366
|
export const listingCapabilities = [
|
|
@@ -2,6 +2,8 @@ import { z } from "zod";
|
|
|
2
2
|
import { preview, } from "../capability.js";
|
|
3
3
|
/** CLI semantic arguments arrive as strings; MCP and Slack send JSON values. */
|
|
4
4
|
export const positionals = z.array(z.string()).max(0).default([]);
|
|
5
|
+
/** Integer micro-USD per GPU-hour (1000000 = $1.00). */
|
|
6
|
+
export const microUsd = z.coerce.number().int().positive();
|
|
5
7
|
export const flag = z.preprocess((value) => (value === "true" ? true : value === "false" ? false : value), z.boolean());
|
|
6
8
|
export function seg(value) {
|
|
7
9
|
return encodeURIComponent(value);
|