@ornncompute/cli 1.0.25 → 1.0.34

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.
Files changed (35) hide show
  1. package/README.md +76 -23
  2. package/package.json +1 -1
  3. package/src/admin-capacity-reservations.mjs +68 -43
  4. package/src/admin-enrollment.mjs +14 -13
  5. package/src/admin-node-lifecycle.mjs +10 -13
  6. package/src/admin-shell.mjs +146 -12
  7. package/src/catalog-shell.mjs +58 -45
  8. package/src/device-auth.mjs +1 -1
  9. package/src/evidence-commands.mjs +25 -11
  10. package/src/gpu-catalog.mjs +21 -0
  11. package/src/identity-file.mjs +28 -0
  12. package/src/kubernetes-commands.mjs +12 -6
  13. package/src/machine-reference.mjs +10 -2
  14. package/src/mount-commands.mjs +4 -2
  15. package/src/slurm-commands.mjs +11 -0
  16. package/src/storage-commands.mjs +27 -7
  17. package/src/vm-commands.mjs +42 -7
  18. package/vendor/capabilities/capabilities/admin-capacity-reservations.js +31 -61
  19. package/vendor/capabilities/capabilities/admin-enrollment.js +9 -9
  20. package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +6 -6
  21. package/vendor/capabilities/capabilities/admin.js +7 -7
  22. package/vendor/capabilities/capabilities/commerce-market.js +330 -0
  23. package/vendor/capabilities/capabilities/fleet.js +57 -21
  24. package/vendor/capabilities/capabilities/listings.js +137 -146
  25. package/vendor/capabilities/capabilities/market-shared.js +44 -0
  26. package/vendor/capabilities/capabilities/markets.js +449 -0
  27. package/vendor/capabilities/capabilities/networks.js +7 -0
  28. package/vendor/capabilities/capabilities/tokens.js +12 -9
  29. package/vendor/capabilities/capability.js +2 -1
  30. package/vendor/capabilities/cli-catalog.js +4 -2
  31. package/vendor/capabilities/cli-migrations.js +16 -43
  32. package/vendor/capabilities/cli.js +1 -1
  33. package/vendor/capabilities/spec-catalog.js +26 -3
  34. package/vendor/capabilities/supplier-directory.js +20 -0
  35. package/vendor/capabilities/capabilities/capacity-offers.js +0 -368
@@ -0,0 +1,449 @@
1
+ import { z } from "zod";
2
+ import { defineCapability } from "../capability.js";
3
+ import { gpuDisplay, loadHardwareCatalog } from "../spec-catalog.js";
4
+ import { consent, idempotencyHeaders, microUsdLabel, positionals, query, seg, } from "./market-shared.js";
5
+ // Markets verbs: one capability per `/v1/markets` route. Spot order books are
6
+ // per GPU slug; schedules are per listing. Prices are integer micro-USD per
7
+ // GPU-hour, exactly as Markets reads and writes them.
8
+ const BASE = "/v1/markets";
9
+ function help(command, outcome, human, automation, related, order, acceptedReferences = []) {
10
+ return {
11
+ arguments: "semantic",
12
+ audience: "customer",
13
+ category: "resource",
14
+ command,
15
+ outcome,
16
+ human,
17
+ automation,
18
+ acceptedReferences,
19
+ related,
20
+ order,
21
+ };
22
+ }
23
+ const microUsd = z.coerce.number().int().positive();
24
+ export const spotBooksInput = z.object({ $positionals: positionals });
25
+ export const spotBooksCapability = defineCapability({
26
+ id: "spot.books",
27
+ domain: "markets",
28
+ roles: ["user"],
29
+ description: "List the spot order books, one per GPU type, with the best bid and ask in micro-USD per GPU-hour.",
30
+ mutation: "read",
31
+ http: { method: "GET", path: `${BASE}/spot/books` },
32
+ input: spotBooksInput,
33
+ cli: {
34
+ command: ["spot", "books"],
35
+ vnext: help(["spot", "books"], "List spot order books by GPU type with their best bid and ask.", "ornn spot books", "ornn spot books --format json", ["spot book", "spot bid"], 30),
36
+ },
37
+ slack: false,
38
+ execute: async (ctx) => ctx.fetch(`${BASE}/spot/books`, { method: "GET" }),
39
+ });
40
+ export const spotBookInput = z.object({
41
+ $positionals: positionals,
42
+ gpuSlug: z.string().min(1).describe("Catalog GPU slug, for example nvidia_h100_sxm."),
43
+ });
44
+ export const spotBookCapability = defineCapability({
45
+ id: "spot.book",
46
+ domain: "markets",
47
+ roles: ["user"],
48
+ description: "Show one spot order book's depth: GPUs and order counts at each bid and ask price level.",
49
+ mutation: "read",
50
+ http: { method: "GET", path: `${BASE}/spot/books/{gpuSlug}` },
51
+ input: spotBookInput,
52
+ cli: {
53
+ command: ["spot", "book"],
54
+ vnext: help(["spot", "book"], "Show bid and ask depth for one GPU type.", "ornn spot book --gpu-slug <slug>", "ornn spot book --gpu-slug <slug> --format json", ["spot books", "spot trades", "spot bid"], 31, ["catalog GPU slug"]),
55
+ },
56
+ slack: false,
57
+ execute: async (ctx, input) => {
58
+ const parsed = spotBookInput.parse(input);
59
+ return ctx.fetch(`${BASE}/spot/books/${seg(parsed.gpuSlug)}`, { method: "GET" });
60
+ },
61
+ });
62
+ export const spotTradesCapability = defineCapability({
63
+ id: "spot.trades",
64
+ domain: "markets",
65
+ roles: ["user"],
66
+ description: "List recent spot trades for one GPU type: price, GPUs and time, with no identities.",
67
+ mutation: "read",
68
+ http: { method: "GET", path: `${BASE}/spot/books/{gpuSlug}/trades` },
69
+ input: spotBookInput,
70
+ cli: {
71
+ command: ["spot", "trades"],
72
+ vnext: help(["spot", "trades"], "List recent spot fills for one GPU type.", "ornn spot trades --gpu-slug <slug>", "ornn spot trades --gpu-slug <slug> --format json", ["spot book"], 32, ["catalog GPU slug"]),
73
+ },
74
+ slack: false,
75
+ execute: async (ctx, input) => {
76
+ const parsed = spotBookInput.parse(input);
77
+ return ctx.fetch(`${BASE}/spot/books/${seg(parsed.gpuSlug)}/trades`, { method: "GET" });
78
+ },
79
+ });
80
+ export const spotBidInput = z
81
+ .object({
82
+ $positionals: positionals,
83
+ gpuSlug: z.string().min(1).describe("Catalog GPU slug, for example nvidia_h100_sxm."),
84
+ gpus: z.coerce
85
+ .number()
86
+ .int()
87
+ .min(1)
88
+ .max(8)
89
+ .describe("GPUs on one node, 1-8. To buy N whole nodes, place N bids of 8 GPUs."),
90
+ orderType: z
91
+ .enum(["limit", "market"])
92
+ .default("limit")
93
+ .describe("A limit bid rests until filled; a market bid fills now or is cancelled."),
94
+ pricePerGpuHourMicroUsd: microUsd
95
+ .optional()
96
+ .describe("Limit price in micro-USD per GPU-hour (1000000 = $1.00). Required for limit bids."),
97
+ idempotencyKey: z.string().min(1).max(200).optional(),
98
+ confirm: z.boolean().optional(),
99
+ })
100
+ .refine((bid) => (bid.orderType === "limit") === (bid.pricePerGpuHourMicroUsd !== undefined), {
101
+ message: "A limit bid needs pricePerGpuHourMicroUsd; a market bid takes no price.",
102
+ path: ["pricePerGpuHourMicroUsd"],
103
+ });
104
+ export const spotBidCapability = defineCapability({
105
+ id: "spot.bid",
106
+ domain: "markets",
107
+ roles: ["user"],
108
+ description: "Place a spot bid for 1-8 GPUs on one node. A filled bid becomes a running spot reservation, " +
109
+ "prepaid hourly from Ornn credits first and then the saved card; the side that takes a resting order pays the transaction fee. Requires confirm: true.",
110
+ mutation: "preview-confirm",
111
+ http: { method: "POST", path: `${BASE}/spot/orders` },
112
+ input: spotBidInput,
113
+ cli: {
114
+ command: ["spot", "bid"],
115
+ vnext: help(["spot", "bid"], "Place one spot bid for 1-8 GPUs on a single node.", "ornn spot bid --gpu-slug <slug> --gpus <1-8> [--order-type limit|market] [--price-per-gpu-hour-micro-usd <n>]", "ornn spot bid --gpu-slug <slug> --gpus 8 --price-per-gpu-hour-micro-usd 2500000 --yes --format json", ["spot orders", "spot cancel"], 33, ["catalog GPU slug"]),
116
+ },
117
+ slack: false,
118
+ execute: async (ctx, input) => {
119
+ const parsed = spotBidInput.parse(input);
120
+ const body = {
121
+ gpuSlug: parsed.gpuSlug,
122
+ gpus: parsed.gpus,
123
+ orderType: parsed.orderType,
124
+ ...(parsed.pricePerGpuHourMicroUsd !== undefined
125
+ ? { pricePerGpuHourMicroUsd: parsed.pricePerGpuHourMicroUsd }
126
+ : {}),
127
+ };
128
+ // Only the CLI review renders terms; MCP and Slack preview the payload.
129
+ const hardware = ctx.cli?.interaction
130
+ ? await loadHardwareCatalog(ctx.fetch).catch(() => null)
131
+ : null;
132
+ const gate = await consent(ctx, "spot.bid", "Place spot bid", [
133
+ { label: "GPU type", value: gpuDisplay(hardware?.terms ?? [], parsed.gpuSlug) },
134
+ { label: "GPUs", value: String(parsed.gpus) },
135
+ { label: "Order", value: parsed.orderType },
136
+ { label: "Limit price", value: microUsdLabel(parsed.pricePerGpuHourMicroUsd) },
137
+ ], body);
138
+ if (gate)
139
+ return gate;
140
+ return ctx.fetch(`${BASE}/spot/orders`, {
141
+ method: "POST",
142
+ body,
143
+ headers: idempotencyHeaders(ctx, parsed.idempotencyKey),
144
+ });
145
+ },
146
+ });
147
+ export const spotOrdersInput = z.object({ $positionals: positionals });
148
+ export const spotOrdersCapability = defineCapability({
149
+ id: "spot.orders",
150
+ domain: "markets",
151
+ roles: ["user"],
152
+ description: "List this organization's spot bids (resting, running and ended) and the asks its listings have on the books.",
153
+ mutation: "read",
154
+ http: { method: "GET", path: `${BASE}/spot/orders` },
155
+ input: spotOrdersInput,
156
+ cli: {
157
+ command: ["spot", "orders"],
158
+ vnext: help(["spot", "orders"], "List your organization's spot bids and listing asks.", "ornn spot orders", "ornn spot orders --format json", ["spot bid", "spot cancel"], 34),
159
+ },
160
+ slack: false,
161
+ execute: async (ctx) => ctx.fetch(`${BASE}/spot/orders`, { method: "GET" }),
162
+ });
163
+ export const spotCancelInput = z.object({
164
+ $positionals: positionals,
165
+ orderId: z
166
+ .string()
167
+ .uuid()
168
+ .describe("A spot bid ID; for a running bid this is also its reservation ID."),
169
+ confirm: z.boolean().optional(),
170
+ });
171
+ export const spotCancelCapability = defineCapability({
172
+ id: "spot.cancel",
173
+ domain: "markets",
174
+ roles: ["user"],
175
+ description: "Cancel a resting spot bid, or end a running spot reservation (the buyer or the seller may end it; the GPUs go back on the book). Requires confirm: true.",
176
+ mutation: "preview-confirm",
177
+ http: { method: "DELETE", path: `${BASE}/spot/orders/{orderId}` },
178
+ input: spotCancelInput,
179
+ cli: {
180
+ command: ["spot", "cancel"],
181
+ vnext: help(["spot", "cancel"], "Cancel a resting bid or end a running spot reservation.", "ornn spot cancel --order-id <id>", "ornn spot cancel --order-id <id> --yes --format json", ["spot orders"], 35, ["spot order ID", "spot reservation ID"]),
182
+ },
183
+ slack: false,
184
+ execute: async (ctx, input) => {
185
+ const parsed = spotCancelInput.parse(input);
186
+ const gate = await consent(ctx, "spot.cancel", "Cancel spot order", [
187
+ { label: "Order", value: parsed.orderId },
188
+ {
189
+ label: "Consequence",
190
+ value: "A resting bid is cancelled; a running spot reservation ends and its machine stops.",
191
+ },
192
+ ], { orderId: parsed.orderId });
193
+ if (gate)
194
+ return gate;
195
+ return ctx.fetch(`${BASE}/spot/orders/${seg(parsed.orderId)}`, { method: "DELETE" });
196
+ },
197
+ });
198
+ export const schedulesListInput = z.object({
199
+ $positionals: positionals,
200
+ listingKind: z.enum(["primary", "secondary"]).optional(),
201
+ gpuSlug: z.string().min(1).optional(),
202
+ from: z.string().min(1).optional().describe("RFC 3339 lower bound for the schedule window."),
203
+ to: z.string().min(1).optional().describe("RFC 3339 upper bound for the schedule window."),
204
+ });
205
+ export const schedulesListCapability = defineCapability({
206
+ id: "schedules.list",
207
+ domain: "markets",
208
+ roles: ["user"],
209
+ description: "List reservable schedules with their available windows (sold windows removed). Primary schedules are shown only to approved organizations; secondary (relisted) schedules are open to every signed-in organization.",
210
+ mutation: "read",
211
+ http: { method: "GET", path: `${BASE}/schedules` },
212
+ input: schedulesListInput,
213
+ cli: {
214
+ command: ["schedules", "list"],
215
+ vnext: help(["schedules", "list"], "List primary and secondary schedules with their open windows.", "ornn schedules list [--listing-kind primary|secondary] [--gpu-slug <slug>] [--from <time>] [--to <time>]", "ornn schedules list --listing-kind secondary --format json", ["schedules show", "schedules buy", "offers post"], 40),
216
+ },
217
+ slack: false,
218
+ execute: async (ctx, input) => {
219
+ const parsed = schedulesListInput.parse(input ?? {});
220
+ return ctx.fetch(`${BASE}/schedules${query({
221
+ listingKind: parsed.listingKind,
222
+ gpuSlug: parsed.gpuSlug,
223
+ from: parsed.from,
224
+ to: parsed.to,
225
+ })}`, { method: "GET" });
226
+ },
227
+ });
228
+ export const scheduleShowInput = z.object({
229
+ $positionals: positionals,
230
+ listingId: z.string().uuid(),
231
+ });
232
+ export const scheduleShowCapability = defineCapability({
233
+ id: "schedules.show",
234
+ domain: "markets",
235
+ roles: ["user"],
236
+ description: "Show one listing's schedule: a primary's price, down payment and open offer book, or a secondary's asking price, and its sold windows.",
237
+ mutation: "read",
238
+ http: { method: "GET", path: `${BASE}/schedules/{listingId}` },
239
+ input: scheduleShowInput,
240
+ cli: {
241
+ command: ["schedules", "show"],
242
+ vnext: help(["schedules", "show"], "Show one schedule with its sold windows and offers.", "ornn schedules show --listing-id <id>", "ornn schedules show --listing-id <id> --format json", ["schedules list", "schedules buy", "offers post", "offers accept"], 41, ["listing ID"]),
243
+ },
244
+ slack: false,
245
+ execute: async (ctx, input) => {
246
+ const parsed = scheduleShowInput.parse(input);
247
+ return ctx.fetch(`${BASE}/schedules/${seg(parsed.listingId)}`, { method: "GET" });
248
+ },
249
+ });
250
+ export const schedulePurchaseInput = z.object({
251
+ $positionals: positionals,
252
+ listingId: z.string().uuid().describe("A secondary (relisted) listing."),
253
+ startsAt: z
254
+ .string()
255
+ .min(1)
256
+ .describe("RFC 3339 start of the window: inside the listing, now or later, on the hour."),
257
+ endsAt: z
258
+ .string()
259
+ .min(1)
260
+ .describe("RFC 3339 end of the window, at least an hour after the start."),
261
+ askPricePerGpuHourMicroUsd: microUsd.describe("The listing's current asking price in micro-USD per GPU-hour, from schedules show. A changed price refuses the purchase."),
262
+ idempotencyKey: z.string().min(1).max(200).optional(),
263
+ confirm: z.boolean().optional(),
264
+ });
265
+ export const schedulePurchaseCapability = defineCapability({
266
+ id: "schedules.purchase",
267
+ domain: "markets",
268
+ roles: ["user"],
269
+ description: "Buy a window of a secondary (relisted) listing at its asking price, for every node on it. There is no offer: the window is yours once it is paid. You pay the asking price plus the Ornn trading fee up front, from Ornn credits and then your saved card; $10,000 or more is invoiced by wire. Requires confirm: true.",
270
+ mutation: "preview-confirm",
271
+ http: { method: "POST", path: `${BASE}/schedules/purchases` },
272
+ input: schedulePurchaseInput,
273
+ cli: {
274
+ command: ["schedules", "buy"],
275
+ vnext: help(["schedules", "buy"], "Buy a window of relisted capacity at its asking price.", "ornn schedules buy --listing-id <id> --starts-at <time> --ends-at <time> --ask-price-per-gpu-hour-micro-usd <n>", "ornn schedules buy --listing-id <id> --starts-at <time> --ends-at <time> --ask-price-per-gpu-hour-micro-usd 4000000 --yes --format json", ["schedules show", "reservations show"], 42, ["listing ID"]),
276
+ },
277
+ slack: false,
278
+ execute: async (ctx, input) => {
279
+ const parsed = schedulePurchaseInput.parse(input);
280
+ const body = {
281
+ legs: [
282
+ {
283
+ listingId: parsed.listingId,
284
+ startsAt: parsed.startsAt,
285
+ endsAt: parsed.endsAt,
286
+ askPricePerGpuHourMicroUsd: parsed.askPricePerGpuHourMicroUsd,
287
+ },
288
+ ],
289
+ };
290
+ const gate = await consent(ctx, "schedules.purchase", "Buy relisted window", [
291
+ { label: "Listing", value: parsed.listingId },
292
+ { label: "Window", value: `${parsed.startsAt} to ${parsed.endsAt}` },
293
+ { label: "Asking price", value: microUsdLabel(parsed.askPricePerGpuHourMicroUsd) },
294
+ {
295
+ label: "Payment",
296
+ value: "Up front, with the Ornn trading fee on top: Ornn credits, then your saved card.",
297
+ },
298
+ ], body);
299
+ if (gate)
300
+ return gate;
301
+ return ctx.fetch(`${BASE}/schedules/purchases`, {
302
+ method: "POST",
303
+ body,
304
+ headers: idempotencyHeaders(ctx, parsed.idempotencyKey),
305
+ });
306
+ },
307
+ });
308
+ export const offersListInput = z.object({ $positionals: positionals });
309
+ export const offersListCapability = defineCapability({
310
+ id: "offers.list",
311
+ domain: "markets",
312
+ roles: ["user"],
313
+ description: "List the offers this organization has posted on schedules, with their status.",
314
+ mutation: "read",
315
+ http: { method: "GET", path: `${BASE}/offers` },
316
+ input: offersListInput,
317
+ cli: {
318
+ command: ["offers", "list"],
319
+ vnext: help(["offers", "list"], "List the offers your organization has posted.", "ornn offers list", "ornn offers list --format json", ["offers post", "offers cancel"], 42),
320
+ },
321
+ slack: false,
322
+ execute: async (ctx) => ctx.fetch(`${BASE}/offers`, { method: "GET" }),
323
+ });
324
+ export const offerPostInput = z.object({
325
+ $positionals: positionals,
326
+ listingId: z.string().uuid(),
327
+ pricePerGpuHourMicroUsd: microUsd.describe("Offer price in micro-USD per GPU-hour (1000000 = $1.00). Covers every node for the rest of the listing."),
328
+ downPaymentCents: z.coerce
329
+ .number()
330
+ .int()
331
+ .min(0)
332
+ .optional()
333
+ .describe("Primary listings only: the down payment you offer, in cents."),
334
+ idempotencyKey: z.string().min(1).max(200).optional(),
335
+ confirm: z.boolean().optional(),
336
+ });
337
+ export const offerPostCapability = defineCapability({
338
+ id: "offers.post",
339
+ domain: "markets",
340
+ roles: ["user"],
341
+ description: "Offer a price for a whole primary listing: every node, from the later of the listing's start and now to its end. The listing owner accepts it to create your reservation. Relisted (secondary) capacity takes no offers: buy it with schedules purchase. Requires confirm: true.",
342
+ mutation: "preview-confirm",
343
+ http: { method: "POST", path: `${BASE}/schedules/{listingId}/offers` },
344
+ input: offerPostInput,
345
+ cli: {
346
+ command: ["offers", "post"],
347
+ vnext: help(["offers", "post"], "Offer a price for a whole primary listing.", "ornn offers post --listing-id <id> --price-per-gpu-hour-micro-usd <n> [--down-payment-cents <n>]", "ornn offers post --listing-id <id> --price-per-gpu-hour-micro-usd 2500000 --yes --format json", ["schedules show", "offers list", "offers cancel"], 43, ["listing ID"]),
348
+ },
349
+ slack: false,
350
+ execute: async (ctx, input) => {
351
+ const parsed = offerPostInput.parse(input);
352
+ const body = {
353
+ pricePerGpuHourMicroUsd: parsed.pricePerGpuHourMicroUsd,
354
+ ...(parsed.downPaymentCents !== undefined
355
+ ? { downPaymentCents: parsed.downPaymentCents }
356
+ : {}),
357
+ };
358
+ const gate = await consent(ctx, "offers.post", "Post schedule offer", [
359
+ { label: "Listing", value: parsed.listingId },
360
+ { label: "Window", value: "The rest of the listing" },
361
+ { label: "Price", value: microUsdLabel(parsed.pricePerGpuHourMicroUsd) },
362
+ ...(parsed.downPaymentCents !== undefined
363
+ ? [{ label: "Down payment", value: `$${(parsed.downPaymentCents / 100).toFixed(2)}` }]
364
+ : []),
365
+ ], { listingId: parsed.listingId, ...body });
366
+ if (gate)
367
+ return gate;
368
+ return ctx.fetch(`${BASE}/schedules/${seg(parsed.listingId)}/offers`, {
369
+ method: "POST",
370
+ body,
371
+ headers: idempotencyHeaders(ctx, parsed.idempotencyKey),
372
+ });
373
+ },
374
+ });
375
+ export const offerDecisionInput = z.object({
376
+ $positionals: positionals,
377
+ listingId: z.string().uuid(),
378
+ offerId: z.string().uuid(),
379
+ confirm: z.boolean().optional(),
380
+ });
381
+ export const offerAcceptCapability = defineCapability({
382
+ id: "offers.accept",
383
+ domain: "markets",
384
+ roles: ["user"],
385
+ description: "Accept an offer on a primary listing you own. The accepted window becomes the buyer's reservation and overlapping offers and relists from the same holding are cancelled. Requires confirm: true.",
386
+ mutation: "preview-confirm",
387
+ http: { method: "POST", path: `${BASE}/schedules/{listingId}/offers/{offerId}/accept` },
388
+ input: offerDecisionInput,
389
+ cli: {
390
+ command: ["offers", "accept"],
391
+ vnext: help(["offers", "accept"], "Accept one offer on a listing you own.", "ornn offers accept --listing-id <id> --offer-id <id>", "ornn offers accept --listing-id <id> --offer-id <id> --yes --format json", ["schedules show"], 44, ["listing ID", "offer ID"]),
392
+ },
393
+ slack: false,
394
+ execute: async (ctx, input) => {
395
+ const parsed = offerDecisionInput.parse(input);
396
+ const gate = await consent(ctx, "offers.accept", "Accept schedule offer", [
397
+ { label: "Listing", value: parsed.listingId },
398
+ { label: "Offer", value: parsed.offerId },
399
+ {
400
+ label: "Consequence",
401
+ value: "The window is sold; overlapping open offers and conflicting relists from this holding are cancelled.",
402
+ },
403
+ ], { listingId: parsed.listingId, offerId: parsed.offerId });
404
+ if (gate)
405
+ return gate;
406
+ return ctx.fetch(`${BASE}/schedules/${seg(parsed.listingId)}/offers/${seg(parsed.offerId)}/accept`, { method: "POST", body: {} });
407
+ },
408
+ });
409
+ export const offerCancelCapability = defineCapability({
410
+ id: "offers.cancel",
411
+ domain: "markets",
412
+ roles: ["user"],
413
+ description: "Cancel an open offer you posted, or one on a listing you own. Requires confirm: true.",
414
+ mutation: "preview-confirm",
415
+ http: { method: "DELETE", path: `${BASE}/schedules/{listingId}/offers/{offerId}` },
416
+ input: offerDecisionInput,
417
+ cli: {
418
+ command: ["offers", "cancel"],
419
+ vnext: help(["offers", "cancel"], "Cancel one open offer.", "ornn offers cancel --listing-id <id> --offer-id <id>", "ornn offers cancel --listing-id <id> --offer-id <id> --yes --format json", ["offers list"], 45, ["listing ID", "offer ID"]),
420
+ },
421
+ slack: false,
422
+ execute: async (ctx, input) => {
423
+ const parsed = offerDecisionInput.parse(input);
424
+ const gate = await consent(ctx, "offers.cancel", "Cancel schedule offer", [
425
+ { label: "Listing", value: parsed.listingId },
426
+ { label: "Offer", value: parsed.offerId },
427
+ ], { listingId: parsed.listingId, offerId: parsed.offerId });
428
+ if (gate)
429
+ return gate;
430
+ return ctx.fetch(`${BASE}/schedules/${seg(parsed.listingId)}/offers/${seg(parsed.offerId)}`, {
431
+ method: "DELETE",
432
+ });
433
+ },
434
+ });
435
+ export const marketCapabilities = [
436
+ spotBooksCapability,
437
+ spotBookCapability,
438
+ spotTradesCapability,
439
+ spotBidCapability,
440
+ spotOrdersCapability,
441
+ spotCancelCapability,
442
+ schedulesListCapability,
443
+ scheduleShowCapability,
444
+ schedulePurchaseCapability,
445
+ offersListCapability,
446
+ offerPostCapability,
447
+ offerAcceptCapability,
448
+ offerCancelCapability,
449
+ ];
@@ -904,6 +904,13 @@ async function connectPrivateTarget(context, input) {
904
904
  field: "machine",
905
905
  });
906
906
  }
907
+ // A UUID and an organization network do not prove that this target exists.
908
+ // Only a resolved native target may continue into private transport setup.
909
+ if (!resolved) {
910
+ runtime.error("No accessible machine, cluster, or capacity reservation matches this target.", {
911
+ code: "connect_target_not_found",
912
+ });
913
+ }
907
914
  const shared = record(await context.fetch(`${NETWORKS_PATH}/shared?overlay=true`));
908
915
  const privateNetwork = projectNetwork({
909
916
  ...record(shared.network),
@@ -2,8 +2,8 @@ import { z } from "zod";
2
2
  import { defineCapability, preview } from "../capability.js";
3
3
  const optionalText = z.string().nullish();
4
4
  const tokenCreateInput = z.object({
5
- operator_id: z.string().min(1),
6
- operatorId: optionalText,
5
+ organization_id: z.string().min(1),
6
+ organizationId: optionalText,
7
7
  facility_id: optionalText,
8
8
  facilityId: optionalText,
9
9
  expires_in_seconds: z.number().int().positive().nullish(),
@@ -51,7 +51,7 @@ export const tokensListCapability = defineCapability({
51
51
  id: "tokens.list",
52
52
  domain: "fleet",
53
53
  roles: ["admin"],
54
- description: "List issued enrollment tokens (id, operator, status, expiry). Does not return plaintext secrets.",
54
+ description: "List issued enrollment tokens (id, supplier organization_id, status, expiry). Does not return plaintext secrets.",
55
55
  mutation: "read",
56
56
  http: { method: "GET", path: "/enrollment-tokens" },
57
57
  slack: { parameters: { type: "object", properties: {} } },
@@ -69,7 +69,10 @@ export const tokensCreateCapability = defineCapability({
69
69
  parameters: {
70
70
  type: "object",
71
71
  properties: {
72
- operator_id: { type: "string", description: "Commerce operator UUID from operator.list." },
72
+ organization_id: {
73
+ type: "string",
74
+ description: "Supplier organization UUID from supplier.list.",
75
+ },
73
76
  facility_id: { type: "string", description: "Optional facility UUID." },
74
77
  expires_in_seconds: { type: "integer", description: "Token TTL in seconds." },
75
78
  filter: {
@@ -90,20 +93,20 @@ export const tokensCreateCapability = defineCapability({
90
93
  description: "PEM private key. Required to enroll over SSH.",
91
94
  },
92
95
  },
93
- required: ["operator_id"],
96
+ required: ["organization_id"],
94
97
  },
95
98
  },
96
99
  input: tokenCreateInput,
97
100
  execute: async (ctx, input) => {
98
101
  const parsed = tokenCreateInput.parse(input);
99
- const operatorId = (parsed.operator_id || parsed.operatorId || "").trim();
102
+ const organizationId = (parsed.organization_id || parsed.organizationId || "").trim();
100
103
  const hosts = [
101
104
  ...(parsed.host?.trim() ? [parsed.host.trim()] : []),
102
105
  ...(parsed.hosts ?? []).map((host) => host.trim()).filter(Boolean),
103
106
  ];
104
107
  const privateKey = parsed.private_key?.trim() || parsed.privateKey?.trim() || "";
105
108
  const payload = {
106
- operator_id: operatorId,
109
+ organization_id: organizationId,
107
110
  facility_id: parsed.facility_id ?? parsed.facilityId,
108
111
  expires_in_seconds: parsed.expires_in_seconds ?? parsed.expiresInSeconds,
109
112
  filter: parsed.filter,
@@ -119,7 +122,7 @@ export const tokensCreateCapability = defineCapability({
119
122
  const minted = (await ctx.fetch("/enrollment-tokens", {
120
123
  method: "POST",
121
124
  body: {
122
- operator_id: operatorId,
125
+ organization_id: organizationId,
123
126
  ...(payload.facility_id ? { facility_id: payload.facility_id } : {}),
124
127
  ...(payload.expires_in_seconds ? { expires_in_seconds: payload.expires_in_seconds } : {}),
125
128
  },
@@ -153,7 +156,7 @@ export const tokensCreateCapability = defineCapability({
153
156
  }
154
157
  return {
155
158
  id: minted.id,
156
- operator_id: minted.operator_id ?? operatorId,
159
+ organization_id: minted.organization_id ?? organizationId,
157
160
  expires_at: minted.expires_at,
158
161
  hosts: results,
159
162
  };
@@ -6,7 +6,7 @@ export const DOMAINS = [
6
6
  "fleet",
7
7
  "users",
8
8
  "reservations",
9
- "bids",
9
+ "markets",
10
10
  "access",
11
11
  "clusters",
12
12
  "slurm",
@@ -15,6 +15,7 @@ export const DOMAINS = [
15
15
  "storage",
16
16
  "vpn",
17
17
  "billing",
18
+ "downtime",
18
19
  "observability",
19
20
  "gtm",
20
21
  ];
@@ -1,6 +1,7 @@
1
1
  import { adminCapabilities, bindAdminCapabilities } from "./capabilities/admin.js";
2
- import { capacityOfferCapabilities } from "./capabilities/capacity-offers.js";
3
2
  import { adminCapacityReservationCapabilities } from "./capabilities/admin-capacity-reservations.js";
3
+ import { commerceMarketCapabilities } from "./capabilities/commerce-market.js";
4
+ import { marketCapabilities } from "./capabilities/markets.js";
4
5
  import { adminEnrollmentCapabilities, bindAdminEnrollmentCapabilities, } from "./capabilities/admin-enrollment.js";
5
6
  import { adminNodeDiagnosticCapabilities } from "./capabilities/admin-node-diagnostics.js";
6
7
  import { adminNodeLifecycleCapabilities } from "./capabilities/admin-nodes-lifecycle.js";
@@ -41,7 +42,8 @@ export const catalog = [
41
42
  ...cliShellCapabilities,
42
43
  ...identityCapabilities,
43
44
  ...listingCapabilities,
44
- ...capacityOfferCapabilities,
45
+ ...commerceMarketCapabilities,
46
+ ...marketCapabilities,
45
47
  ...computeCapabilities,
46
48
  ...fleetCapabilities.filter((capability) => capability.id !== "node.exec"),
47
49
  ...networkCapabilities,