@ornncompute/cli 1.0.72 → 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 +24 -20
- 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/external-bucket-commands.mjs +5 -73
- package/src/interaction-runtime.mjs +2 -37
- package/src/kubernetes-commands.mjs +9 -100
- 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 +9 -42
- 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/market-shared.js +1 -1
- package/vendor/capabilities/capabilities/markets.js +12 -12
- package/vendor/capabilities/capabilities/mounts.js +4 -4
- 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
|
@@ -17,44 +17,41 @@ const suppliersInput = z.object({
|
|
|
17
17
|
});
|
|
18
18
|
const facilitiesInput = z.object({
|
|
19
19
|
organization_id: z.string().optional(),
|
|
20
|
-
organizationId: z.string().optional(),
|
|
21
20
|
cursor: z.string().optional(),
|
|
22
|
-
limit: z.number().int().min(1).max(
|
|
21
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
23
22
|
});
|
|
24
23
|
const nodesInput = z.object({
|
|
25
24
|
organization_id: z.string().optional(),
|
|
26
|
-
organizationId: z.string().optional(),
|
|
27
25
|
facility_id: z.string().optional(),
|
|
28
|
-
facilityId: z.string().optional(),
|
|
29
26
|
status: z.string().optional(),
|
|
30
27
|
reservation_id: z.string().optional(),
|
|
31
|
-
reservationId: z.string().optional(),
|
|
32
28
|
cursor: z.string().optional(),
|
|
33
29
|
max: z.number().int().positive().max(5000).optional(),
|
|
34
30
|
limit: z.number().int().positive().max(5000).optional(),
|
|
35
31
|
});
|
|
36
32
|
const nodeLifecycleInput = z.object({
|
|
37
33
|
node_id: z.string().min(1),
|
|
38
|
-
|
|
34
|
+
node_ref: z.string().optional(),
|
|
39
35
|
reason: z.string().optional(),
|
|
40
36
|
force: z.boolean().optional(),
|
|
37
|
+
skip_cleanup: z.boolean().optional(),
|
|
38
|
+
delete_node_record: z.literal(true).optional(),
|
|
39
|
+
idempotency_key: z.string().optional(),
|
|
40
|
+
yes: z.boolean().optional(),
|
|
41
|
+
wait: z.boolean().optional(),
|
|
42
|
+
timeout_seconds: z.number().positive().optional(),
|
|
41
43
|
});
|
|
42
44
|
const nodeExecInput = z.object({
|
|
43
45
|
node_id: z.string().min(1),
|
|
44
|
-
nodeId: z.string().optional(),
|
|
45
46
|
command: z.string().min(1),
|
|
46
47
|
timeout_seconds: z.number().int().positive(),
|
|
47
|
-
timeoutSeconds: z.number().int().positive().optional(),
|
|
48
48
|
yes: z.boolean().optional(),
|
|
49
49
|
});
|
|
50
|
-
function nodeIdOf(input) {
|
|
51
|
-
return (input.node_id || input.nodeId || "").trim();
|
|
52
|
-
}
|
|
53
50
|
export const listSuppliersCapability = defineCapability({
|
|
54
51
|
id: "supplier.list",
|
|
55
52
|
domain: "fleet",
|
|
56
53
|
roles: ["admin"],
|
|
57
|
-
description: "List supplier organizations (id, name). Resolve supplier names to organization_id with this before nodes.list or facilities.list.",
|
|
54
|
+
description: "List supplier organizations (id, name). Resolve supplier names to organization_id with this before nodes.list or facilities.list. When next_offset is present, pass it back as offset for the next page.",
|
|
58
55
|
mutation: "read",
|
|
59
56
|
http: { method: "GET", path: "/v1/organizations" },
|
|
60
57
|
slack: {
|
|
@@ -86,7 +83,7 @@ export const listFacilitiesCapability = defineCapability({
|
|
|
86
83
|
id: "facilities.list",
|
|
87
84
|
domain: "fleet",
|
|
88
85
|
roles: ["admin"],
|
|
89
|
-
description: "List facilities (id, display name, region, supplier organization id). Optional organization_id filter.
|
|
86
|
+
description: "List facilities (id, display name, region, supplier organization id). Optional organization_id filter. When nextCursor is present, pass it back as cursor for the next page. Use facilities.show to resolve one facility id.",
|
|
90
87
|
mutation: "read",
|
|
91
88
|
http: { method: "GET", path: "/provisioning/facilities" },
|
|
92
89
|
slack: {
|
|
@@ -97,6 +94,16 @@ export const listFacilitiesCapability = defineCapability({
|
|
|
97
94
|
type: "string",
|
|
98
95
|
description: "Optional supplier organization UUID from supplier.list.",
|
|
99
96
|
},
|
|
97
|
+
cursor: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "Pass the previous response's nextCursor to fetch the next page.",
|
|
100
|
+
},
|
|
101
|
+
limit: {
|
|
102
|
+
type: "integer",
|
|
103
|
+
minimum: 1,
|
|
104
|
+
maximum: 100,
|
|
105
|
+
description: "Optional page size (maximum 100).",
|
|
106
|
+
},
|
|
100
107
|
},
|
|
101
108
|
},
|
|
102
109
|
},
|
|
@@ -104,12 +111,38 @@ export const listFacilitiesCapability = defineCapability({
|
|
|
104
111
|
execute: async (ctx, input) => {
|
|
105
112
|
const parsed = facilitiesInput.parse(input);
|
|
106
113
|
return ctx.fetch(queryPath("/provisioning/facilities", {
|
|
107
|
-
organizationId: parsed.organization_id
|
|
114
|
+
organizationId: parsed.organization_id,
|
|
108
115
|
cursor: parsed.cursor,
|
|
109
116
|
limit: parsed.limit === undefined ? undefined : String(parsed.limit),
|
|
110
117
|
}), { method: "GET" });
|
|
111
118
|
},
|
|
112
119
|
});
|
|
120
|
+
export const facilityShowInput = z.object({ facilityId: z.uuid() });
|
|
121
|
+
export const facilityShowCapability = defineCapability({
|
|
122
|
+
id: "facilities.show",
|
|
123
|
+
domain: "fleet",
|
|
124
|
+
roles: ["admin"],
|
|
125
|
+
description: "Resolve one facility id (e.g. a listing's facilityId) to its display name, region and status.",
|
|
126
|
+
mutation: "read",
|
|
127
|
+
http: { method: "GET", path: "/provisioning/facilities/{facilityId}" },
|
|
128
|
+
cli: false,
|
|
129
|
+
slack: {
|
|
130
|
+
parameters: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
facilityId: { type: "string", format: "uuid" },
|
|
134
|
+
},
|
|
135
|
+
required: ["facilityId"],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
input: facilityShowInput,
|
|
139
|
+
execute: async (ctx, input) => {
|
|
140
|
+
const parsed = facilityShowInput.parse(input);
|
|
141
|
+
return ctx.fetch(`/provisioning/facilities/${encodeURIComponent(parsed.facilityId)}`, {
|
|
142
|
+
method: "GET",
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
});
|
|
113
146
|
export const listNodesCapability = defineCapability({
|
|
114
147
|
id: "nodes.list",
|
|
115
148
|
domain: "fleet",
|
|
@@ -160,10 +193,10 @@ export const listNodesCapability = defineCapability({
|
|
|
160
193
|
return ctx.fetch(queryPath("/nodes", pagination), { method: "GET" });
|
|
161
194
|
}
|
|
162
195
|
return ctx.fetch(queryPath("/nodes", {
|
|
163
|
-
organization_id: parsed.organization_id
|
|
164
|
-
facility_id: parsed.facility_id
|
|
196
|
+
organization_id: parsed.organization_id,
|
|
197
|
+
facility_id: parsed.facility_id,
|
|
165
198
|
status: parsed.status,
|
|
166
|
-
reservation_id: parsed.reservation_id
|
|
199
|
+
reservation_id: parsed.reservation_id,
|
|
167
200
|
...pagination,
|
|
168
201
|
}), { method: "GET" });
|
|
169
202
|
},
|
|
@@ -172,9 +205,21 @@ export const nodeOffGridCapability = defineCapability({
|
|
|
172
205
|
id: "node.off-grid",
|
|
173
206
|
domain: "fleet",
|
|
174
207
|
roles: ["admin"],
|
|
175
|
-
description: "Admin-only. Take a node off-grid (remove Ornn agent management, leave the host powered on).
|
|
208
|
+
description: "Admin-only. Take a node off-grid (remove Ornn agent management, leave the host powered on). Slack waits for a human confirm. ",
|
|
176
209
|
mutation: "preview-confirm",
|
|
177
210
|
http: { method: "POST", path: "/nodes/{id}/off-grid" },
|
|
211
|
+
cli: {
|
|
212
|
+
command: ["admin", "nodes", "suspend-management"],
|
|
213
|
+
vnext: {
|
|
214
|
+
audience: "admin",
|
|
215
|
+
category: "resource",
|
|
216
|
+
command: ["admin", "nodes", "suspend-management"],
|
|
217
|
+
outcome: "Remove Ornn management and managed access from one powered-on node.",
|
|
218
|
+
human: "ornn admin nodes suspend-management <node-ref> [--reason TEXT] [] [--no-wait]",
|
|
219
|
+
automation: "ornn admin nodes suspend-management <node-id> --idempotency-key KEY --format json",
|
|
220
|
+
review: "Node, supplier, facility, capacity assignment, management removal, and managed-access loss.",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
178
223
|
slack: {
|
|
179
224
|
parameters: {
|
|
180
225
|
type: "object",
|
|
@@ -188,14 +233,18 @@ export const nodeOffGridCapability = defineCapability({
|
|
|
188
233
|
input: nodeLifecycleInput,
|
|
189
234
|
execute: async (ctx, input) => {
|
|
190
235
|
const parsed = nodeLifecycleInput.parse(input);
|
|
191
|
-
const id =
|
|
192
|
-
const body = {
|
|
236
|
+
const id = parsed.node_id.trim();
|
|
237
|
+
const body = {
|
|
238
|
+
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
239
|
+
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
240
|
+
};
|
|
193
241
|
if (!ctx.confirmed) {
|
|
194
242
|
return preview("node.off-grid", { node_id: id, ...body });
|
|
195
243
|
}
|
|
196
244
|
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/off-grid`, {
|
|
197
245
|
method: "POST",
|
|
198
246
|
body,
|
|
247
|
+
...(parsed.idempotency_key ? { headers: { "Idempotency-Key": parsed.idempotency_key } } : {}),
|
|
199
248
|
});
|
|
200
249
|
},
|
|
201
250
|
});
|
|
@@ -203,9 +252,21 @@ export const nodeOnGridCapability = defineCapability({
|
|
|
203
252
|
id: "node.on-grid",
|
|
204
253
|
domain: "fleet",
|
|
205
254
|
roles: ["admin"],
|
|
206
|
-
description: "Admin-only. Re-enroll an off-grid node: orchestrator SSHs with the stored admin key and runs the installer with restore_node_id. No caller key.
|
|
255
|
+
description: "Admin-only. Re-enroll an off-grid node: orchestrator SSHs with the stored admin key and runs the installer with restore_node_id. No caller key. Slack waits for a human confirm. ",
|
|
207
256
|
mutation: "preview-confirm",
|
|
208
257
|
http: { method: "POST", path: "/nodes/{id}/on-grid" },
|
|
258
|
+
cli: {
|
|
259
|
+
command: ["admin", "nodes", "resume-management"],
|
|
260
|
+
vnext: {
|
|
261
|
+
audience: "admin",
|
|
262
|
+
category: "resource",
|
|
263
|
+
command: ["admin", "nodes", "resume-management"],
|
|
264
|
+
outcome: "Restore Ornn management and managed access on one off-grid node.",
|
|
265
|
+
human: "ornn admin nodes resume-management <node-ref> [] [--no-wait]",
|
|
266
|
+
automation: "ornn admin nodes resume-management <node-id> --idempotency-key KEY --format json",
|
|
267
|
+
review: "Node, supplier, facility, capacity assignment, remote re-enrollment, and restored managed access.",
|
|
268
|
+
},
|
|
269
|
+
},
|
|
209
270
|
slack: {
|
|
210
271
|
parameters: {
|
|
211
272
|
type: "object",
|
|
@@ -218,13 +279,16 @@ export const nodeOnGridCapability = defineCapability({
|
|
|
218
279
|
input: nodeLifecycleInput,
|
|
219
280
|
execute: async (ctx, input) => {
|
|
220
281
|
const parsed = nodeLifecycleInput.parse(input);
|
|
221
|
-
const id =
|
|
282
|
+
const id = parsed.node_id.trim();
|
|
222
283
|
if (!ctx.confirmed) {
|
|
223
284
|
return preview("node.on-grid", { node_id: id });
|
|
224
285
|
}
|
|
225
286
|
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/on-grid`, {
|
|
226
287
|
method: "POST",
|
|
227
|
-
body: {
|
|
288
|
+
body: {
|
|
289
|
+
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
290
|
+
},
|
|
291
|
+
...(parsed.idempotency_key ? { headers: { "Idempotency-Key": parsed.idempotency_key } } : {}),
|
|
228
292
|
});
|
|
229
293
|
},
|
|
230
294
|
});
|
|
@@ -233,7 +297,7 @@ export const nodeExecCapability = defineCapability({
|
|
|
233
297
|
domain: "fleet",
|
|
234
298
|
roles: ["admin"],
|
|
235
299
|
description: "Admin-only. Run one unrestricted command on an enrolled node over orchestrator SSH with the stored admin key. Always pass timeout_seconds. Not an interactive shell. On NVIDIA hosts use nvidia-smi; on AMD Instinct use amd-smi. Runs on the tool call — no confirm, no y/N.",
|
|
236
|
-
mutation: "
|
|
300
|
+
mutation: "immediate",
|
|
237
301
|
http: { method: "POST", path: "/remote" },
|
|
238
302
|
slack: {
|
|
239
303
|
parameters: {
|
|
@@ -252,8 +316,8 @@ export const nodeExecCapability = defineCapability({
|
|
|
252
316
|
input: nodeExecInput,
|
|
253
317
|
execute: async (ctx, input) => {
|
|
254
318
|
const parsed = nodeExecInput.parse(input);
|
|
255
|
-
const id =
|
|
256
|
-
const timeoutSeconds = parsed.timeout_seconds
|
|
319
|
+
const id = parsed.node_id.trim();
|
|
320
|
+
const timeoutSeconds = parsed.timeout_seconds;
|
|
257
321
|
return ctx.fetch("/remote", {
|
|
258
322
|
method: "POST",
|
|
259
323
|
body: { node_id: id, command: parsed.command, timeout_seconds: timeoutSeconds },
|
|
@@ -264,9 +328,21 @@ export const nodeTerminateCapability = defineCapability({
|
|
|
264
328
|
id: "node.terminate",
|
|
265
329
|
domain: "fleet",
|
|
266
330
|
roles: ["admin"],
|
|
267
|
-
description: "Admin-only. Terminate a node record (best-effort clean + delete). Off-grid nodes need force: true. There is no deenroll verb.
|
|
331
|
+
description: "Admin-only. Terminate a node record (best-effort clean + delete). Off-grid nodes need force: true. There is no deenroll verb. Slack waits for a human confirm. ",
|
|
268
332
|
mutation: "preview-confirm",
|
|
269
333
|
http: { method: "POST", path: "/nodes/{id}/terminate" },
|
|
334
|
+
cli: {
|
|
335
|
+
command: ["admin", "nodes", "terminate"],
|
|
336
|
+
vnext: {
|
|
337
|
+
audience: "admin",
|
|
338
|
+
category: "resource",
|
|
339
|
+
command: ["admin", "nodes", "terminate"],
|
|
340
|
+
outcome: "End managed access and permanently delete one exact node record.",
|
|
341
|
+
human: "ornn admin nodes terminate <node-ref> --delete-node-record [--skip-cleanup] [--reason TEXT] []",
|
|
342
|
+
automation: "ornn admin nodes terminate <node-id> --delete-node-record --idempotency-key KEY --format json",
|
|
343
|
+
review: "Node, supplier, facility, reservation/capacity, host cleanup, node-record deletion, and access loss.",
|
|
344
|
+
},
|
|
345
|
+
},
|
|
270
346
|
slack: {
|
|
271
347
|
parameters: {
|
|
272
348
|
type: "object",
|
|
@@ -281,10 +357,12 @@ export const nodeTerminateCapability = defineCapability({
|
|
|
281
357
|
input: nodeLifecycleInput,
|
|
282
358
|
execute: async (ctx, input) => {
|
|
283
359
|
const parsed = nodeLifecycleInput.parse(input);
|
|
284
|
-
const id =
|
|
360
|
+
const id = parsed.node_id.trim();
|
|
361
|
+
const force = parsed.force ?? parsed.skip_cleanup;
|
|
285
362
|
const body = {
|
|
286
363
|
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
287
|
-
...(
|
|
364
|
+
...(force !== undefined ? { force } : {}),
|
|
365
|
+
...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
|
|
288
366
|
};
|
|
289
367
|
if (!ctx.confirmed) {
|
|
290
368
|
return preview("node.terminate", { node_id: id, ...body });
|
|
@@ -292,12 +370,14 @@ export const nodeTerminateCapability = defineCapability({
|
|
|
292
370
|
return ctx.fetch(`/nodes/${encodeURIComponent(id)}/terminate`, {
|
|
293
371
|
method: "POST",
|
|
294
372
|
body,
|
|
373
|
+
...(parsed.idempotency_key ? { headers: { "Idempotency-Key": parsed.idempotency_key } } : {}),
|
|
295
374
|
});
|
|
296
375
|
},
|
|
297
376
|
});
|
|
298
377
|
export const fleetCapabilities = [
|
|
299
378
|
listSuppliersCapability,
|
|
300
379
|
listFacilitiesCapability,
|
|
380
|
+
facilityShowCapability,
|
|
301
381
|
listNodesCapability,
|
|
302
382
|
nodeOffGridCapability,
|
|
303
383
|
nodeOnGridCapability,
|
|
@@ -21,7 +21,7 @@ export function idempotencyHeaders(ctx, supplied) {
|
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* One consent gate for every surface: the CLI reviews the terms in the
|
|
24
|
-
* terminal (or takes
|
|
24
|
+
* terminal (or takes ``), MCP and Slack return a preview until the
|
|
25
25
|
* caller confirms. `null` means go ahead.
|
|
26
26
|
*/
|
|
27
27
|
export async function consent(ctx, action, title, terms, payload) {
|
|
@@ -173,13 +173,13 @@ export const marketBidCapability = defineCapability({
|
|
|
173
173
|
roles: ["user"],
|
|
174
174
|
description: "Buy nodes at market, each the GPUs of one whole machine, as Spot (the default; a scheduled booking that needs the GPUs can stop it) or On-demand (runs until stopped or the listing ends), " +
|
|
175
175
|
"in one region or any. The bid takes the cheapest nodes for sale now and cancels the rest. What one listing sells becomes a running reservation (a fill), prepaid hourly from Ornn credits first and then the saved card. " +
|
|
176
|
-
"Book prices are what you pay per GPU-hour
|
|
176
|
+
"Book prices are what you pay per GPU-hour..",
|
|
177
177
|
mutation: "preview-confirm",
|
|
178
178
|
http: { method: "POST", path: `${BASE}/orders` },
|
|
179
179
|
input: marketBidInput,
|
|
180
180
|
cli: {
|
|
181
181
|
command: ["market", "bid"],
|
|
182
|
-
vnext: help(["market", "bid"], "Buy nodes at market.", "ornn market bid --gpu-slug <slug> --nodes <n> [--sale-type spot|on-demand] [--region <slug>] [--disk-id <id>]", "ornn market bid --gpu-slug <slug> --nodes 2
|
|
182
|
+
vnext: help(["market", "bid"], "Buy nodes at market.", "ornn market bid --gpu-slug <slug> --nodes <n> [--sale-type spot|on-demand] [--region <slug>] [--disk-id <id>]", "ornn market bid --gpu-slug <slug> --nodes 2 --format json", ["market quote", "market orders", "market fills", "market cancel"], 35, ["catalog GPU slug"]),
|
|
183
183
|
},
|
|
184
184
|
slack: false,
|
|
185
185
|
execute: async (ctx, input) => {
|
|
@@ -237,13 +237,13 @@ export const marketCancelCapability = defineCapability({
|
|
|
237
237
|
id: "market.cancel",
|
|
238
238
|
domain: "markets",
|
|
239
239
|
roles: ["user"],
|
|
240
|
-
description: "Cancel what a resting bid has not bought yet. Its fills keep running; end them with market end
|
|
240
|
+
description: "Cancel what a resting bid has not bought yet. Its fills keep running; end them with market end..",
|
|
241
241
|
mutation: "preview-confirm",
|
|
242
242
|
http: { method: "DELETE", path: `${BASE}/orders/{orderId}` },
|
|
243
243
|
input: marketCancelInput,
|
|
244
244
|
cli: {
|
|
245
245
|
command: ["market", "cancel"],
|
|
246
|
-
vnext: help(["market", "cancel"], "Cancel the rest of a resting bid.", "ornn market cancel --order-id <id>", "ornn market cancel --order-id <id>
|
|
246
|
+
vnext: help(["market", "cancel"], "Cancel the rest of a resting bid.", "ornn market cancel --order-id <id>", "ornn market cancel --order-id <id> --format json", ["market orders", "market end"], 37, ["order ID"]),
|
|
247
247
|
},
|
|
248
248
|
slack: false,
|
|
249
249
|
execute: async (ctx, input) => {
|
|
@@ -291,13 +291,13 @@ export const marketEndCapability = defineCapability({
|
|
|
291
291
|
id: "market.end",
|
|
292
292
|
domain: "markets",
|
|
293
293
|
roles: ["user"],
|
|
294
|
-
description: "End a running fill: its reservation stops, its VMs stop, and its GPUs go back on the book
|
|
294
|
+
description: "End a running fill: its reservation stops, its VMs stop, and its GPUs go back on the book..",
|
|
295
295
|
mutation: "preview-confirm",
|
|
296
296
|
http: { method: "DELETE", path: "/commerce/reservations/{fillId}" },
|
|
297
297
|
input: marketEndInput,
|
|
298
298
|
cli: {
|
|
299
299
|
command: ["market", "end"],
|
|
300
|
-
vnext: help(["market", "end"], "End a running fill and stop its VMs.", "ornn market end --fill-id <id>", "ornn market end --fill-id <id>
|
|
300
|
+
vnext: help(["market", "end"], "End a running fill and stop its VMs.", "ornn market end --fill-id <id>", "ornn market end --fill-id <id> --format json", ["market fills"], 39, ["fill ID", "reservation ID"]),
|
|
301
301
|
},
|
|
302
302
|
slack: false,
|
|
303
303
|
execute: async (ctx, input) => {
|
|
@@ -366,13 +366,13 @@ export const offerPostCapability = defineCapability({
|
|
|
366
366
|
id: "offers.post",
|
|
367
367
|
domain: "markets",
|
|
368
368
|
roles: ["user"],
|
|
369
|
-
description: "Offer a price for a whole primary listing on one of its terms: every node, from the later of the listing's start and now, for durationSeconds. The listing owner accepts it to create your reservation. Relisted (secondary) capacity takes no offers: buy its windows with reservations.purchase
|
|
369
|
+
description: "Offer a price for a whole primary listing on one of its terms: every node, from the later of the listing's start and now, for durationSeconds. The listing owner accepts it to create your reservation. Relisted (secondary) capacity takes no offers: buy its windows with reservations.purchase..",
|
|
370
370
|
mutation: "preview-confirm",
|
|
371
371
|
http: { method: "POST", path: `${BASE}/offers` },
|
|
372
372
|
input: offerPostInput,
|
|
373
373
|
cli: {
|
|
374
374
|
command: ["offers", "post"],
|
|
375
|
-
vnext: help(["offers", "post"], "Offer a price for a whole primary listing on one of its terms.", "ornn offers post --listing-id <id> --listing-term-id <id> [--duration-seconds <n>] --price-per-gpu-hour-micro-usd <n> [--down-payment-cents <n>]", "ornn offers post --listing-id <id> --listing-term-id <id> --duration-seconds 2592000 --price-per-gpu-hour-micro-usd 2500000
|
|
375
|
+
vnext: help(["offers", "post"], "Offer a price for a whole primary listing on one of its terms.", "ornn offers post --listing-id <id> --listing-term-id <id> [--duration-seconds <n>] --price-per-gpu-hour-micro-usd <n> [--down-payment-cents <n>]", "ornn offers post --listing-id <id> --listing-term-id <id> --duration-seconds 2592000 --price-per-gpu-hour-micro-usd 2500000 --format json", ["capacity-offers terms", "offers list", "offers cancel"], 43, ["listing ID", "listingTermsId from capacity-offers terms"]),
|
|
376
376
|
},
|
|
377
377
|
slack: false,
|
|
378
378
|
execute: async (ctx, input) => {
|
|
@@ -418,13 +418,13 @@ export const offerAcceptCapability = defineCapability({
|
|
|
418
418
|
id: "offers.accept",
|
|
419
419
|
domain: "markets",
|
|
420
420
|
roles: ["user"],
|
|
421
|
-
description: "Accept an offer on a primary listing you own. The buyer's reservation is booked and the listing's overlapping open offers are cancelled
|
|
421
|
+
description: "Accept an offer on a primary listing you own. The buyer's reservation is booked and the listing's overlapping open offers are cancelled..",
|
|
422
422
|
mutation: "preview-confirm",
|
|
423
423
|
http: { method: "POST", path: `${BASE}/offers/{offerId}/accept` },
|
|
424
424
|
input: offerDecisionInput,
|
|
425
425
|
cli: {
|
|
426
426
|
command: ["offers", "accept"],
|
|
427
|
-
vnext: help(["offers", "accept"], "Accept one offer on a listing you own.", "ornn offers accept --offer-id <id>", "ornn offers accept --offer-id <id>
|
|
427
|
+
vnext: help(["offers", "accept"], "Accept one offer on a listing you own.", "ornn offers accept --offer-id <id>", "ornn offers accept --offer-id <id> --format json", ["offers list"], 44, ["offer ID"]),
|
|
428
428
|
},
|
|
429
429
|
slack: false,
|
|
430
430
|
execute: async (ctx, input) => {
|
|
@@ -448,13 +448,13 @@ export const offerCancelCapability = defineCapability({
|
|
|
448
448
|
id: "offers.cancel",
|
|
449
449
|
domain: "markets",
|
|
450
450
|
roles: ["user"],
|
|
451
|
-
description: "Cancel an open offer you made, or one on a listing you own
|
|
451
|
+
description: "Cancel an open offer you made, or one on a listing you own..",
|
|
452
452
|
mutation: "preview-confirm",
|
|
453
453
|
http: { method: "DELETE", path: `${BASE}/offers/{offerId}` },
|
|
454
454
|
input: offerDecisionInput,
|
|
455
455
|
cli: {
|
|
456
456
|
command: ["offers", "cancel"],
|
|
457
|
-
vnext: help(["offers", "cancel"], "Cancel one open offer.", "ornn offers cancel --offer-id <id>", "ornn offers cancel --offer-id <id>
|
|
457
|
+
vnext: help(["offers", "cancel"], "Cancel one open offer.", "ornn offers cancel --offer-id <id>", "ornn offers cancel --offer-id <id> --format json", ["offers list"], 45, ["offer ID"]),
|
|
458
458
|
},
|
|
459
459
|
slack: false,
|
|
460
460
|
execute: async (ctx, input) => {
|
|
@@ -84,7 +84,7 @@ export const mountCapabilities = [
|
|
|
84
84
|
order: 542,
|
|
85
85
|
outcome: "Reconcile one exact volume-mount goal and preserve per-machine readiness.",
|
|
86
86
|
human: "ornn volume-mounts attach <canonical-reservation-id> --volume <exact-name-or-id> --access read-only|read-write [--no-wait]",
|
|
87
|
-
automation: "ornn volume-mounts attach <canonical-reservation-id> --volume <canonical-volume-id> --access read-only|read-write
|
|
87
|
+
automation: "ornn volume-mounts attach <canonical-reservation-id> --volume <canonical-volume-id> --access read-only|read-write --idempotency-key <key> --format json",
|
|
88
88
|
acceptedReferences: [
|
|
89
89
|
"canonical capacity-reservation ID",
|
|
90
90
|
"canonical or exact unique managed-volume reference",
|
|
@@ -114,7 +114,7 @@ export const mountCapabilities = [
|
|
|
114
114
|
order: 544,
|
|
115
115
|
outcome: "Reconcile an exact detached goal and preserve the underlying volume.",
|
|
116
116
|
human: "ornn volume-mounts detach <canonical-reservation-id>",
|
|
117
|
-
automation: "ornn volume-mounts detach <canonical-reservation-id>
|
|
117
|
+
automation: "ornn volume-mounts detach <canonical-reservation-id> --idempotency-key <key> --format json",
|
|
118
118
|
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
119
119
|
review: "Review the exact relationship and access loss; the managed volume remains.",
|
|
120
120
|
resumableStates: ["detaching", "ended"],
|
|
@@ -128,7 +128,7 @@ export const mountCapabilities = [
|
|
|
128
128
|
order: 550,
|
|
129
129
|
outcome: "Create or rediscover one filesystem deployment and expose every machine mount.",
|
|
130
130
|
human: "ornn filesystems deploy <canonical-reservation-id> --performance-tier <tier> --capacity-gib <gib> [--no-wait]",
|
|
131
|
-
automation: "ornn filesystems deploy <canonical-reservation-id> --performance-tier <tier> --capacity-gib <gib>
|
|
131
|
+
automation: "ornn filesystems deploy <canonical-reservation-id> --performance-tier <tier> --capacity-gib <gib> --idempotency-key <key> --format json",
|
|
132
132
|
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
133
133
|
review: "Review the exact reservation, performance tier, capacity, and managed filesystem consequence.",
|
|
134
134
|
resumableStates: ["accepted", "partial", "ready"],
|
|
@@ -167,7 +167,7 @@ export const mountCapabilities = [
|
|
|
167
167
|
order: 553,
|
|
168
168
|
outcome: "Detach every filesystem member while preserving the underlying managed volume.",
|
|
169
169
|
human: "ornn filesystems delete <canonical-reservation-id>",
|
|
170
|
-
automation: "ornn filesystems delete <canonical-reservation-id>
|
|
170
|
+
automation: "ornn filesystems delete <canonical-reservation-id> --idempotency-key <key> --format json",
|
|
171
171
|
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
172
172
|
review: "Review the exact deployment and access loss; its managed volume remains.",
|
|
173
173
|
resumableStates: ["detaching", "ended"],
|
|
@@ -1089,7 +1089,7 @@ export const privateNetworkCreateCapability = customerResource({
|
|
|
1089
1089
|
order: 502,
|
|
1090
1090
|
outcome: "Create one reviewed private network with name-based durable replay.",
|
|
1091
1091
|
human: "ornn private-networks create --name <name> [--cidr <cidr>]",
|
|
1092
|
-
automation: "ornn private-networks create --name <name> [--cidr <cidr>]
|
|
1092
|
+
automation: "ornn private-networks create --name <name> [--cidr <cidr>] --idempotency-key <key> --format json",
|
|
1093
1093
|
review: "Review the normalized name and CIDR before creation.",
|
|
1094
1094
|
resumableStates: ["ready"],
|
|
1095
1095
|
related: ["private-networks list"],
|
|
@@ -1113,7 +1113,7 @@ export const privateNetworkUpdateCapability = customerResource({
|
|
|
1113
1113
|
order: 503,
|
|
1114
1114
|
outcome: "Apply one reviewed private-network name goal.",
|
|
1115
1115
|
human: "ornn private-networks update <exact-name-or-id> --name <new-name>",
|
|
1116
|
-
automation: "ornn private-networks update <exact-name-or-id> --name <new-name>
|
|
1116
|
+
automation: "ornn private-networks update <exact-name-or-id> --name <new-name> --idempotency-key <key> --format json",
|
|
1117
1117
|
acceptedReferences: ["canonical private-network ID", "exact unique private-network name"],
|
|
1118
1118
|
review: "Review the exact private network and normalized new name.",
|
|
1119
1119
|
related: ["private-networks show"],
|
|
@@ -1137,7 +1137,7 @@ export const privateNetworkDeleteCapability = customerResource({
|
|
|
1137
1137
|
order: 504,
|
|
1138
1138
|
outcome: "Delete a private network when the owner permits deletion.",
|
|
1139
1139
|
human: "ornn private-networks delete <exact-name-or-id>",
|
|
1140
|
-
automation: "ornn private-networks delete <exact-name-or-id>
|
|
1140
|
+
automation: "ornn private-networks delete <exact-name-or-id> --idempotency-key <key> --format json",
|
|
1141
1141
|
acceptedReferences: ["canonical private-network ID", "exact unique private-network name"],
|
|
1142
1142
|
review: "Review the exact organization network before permanent deletion.",
|
|
1143
1143
|
related: ["private-networks list"],
|
|
@@ -1184,7 +1184,7 @@ export const vpnConnectionConfigureCapability = customerResource({
|
|
|
1184
1184
|
order: 521,
|
|
1185
1185
|
outcome: "Configure or replay one VPN connection and explicitly export its secret configuration.",
|
|
1186
1186
|
human: "ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> (--print | --output <path>)",
|
|
1187
|
-
automation: "ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> --output <path>
|
|
1187
|
+
automation: "ornn vpn-connections configure <canonical-reservation-id> --name <name> --private-key <path> --output <path> --idempotency-key <key> --format json",
|
|
1188
1188
|
acceptedReferences: ["canonical capacity-reservation ID"],
|
|
1189
1189
|
review: "Review the exact private target, locally derived WireGuard public key, connection name, and explicit secret destination.",
|
|
1190
1190
|
resumableStates: ["ready"],
|
|
@@ -1209,7 +1209,7 @@ export const vpnConnectionDisconnectCapability = customerResource({
|
|
|
1209
1209
|
order: 522,
|
|
1210
1210
|
outcome: "Disconnect one VPN connection without deleting its private network.",
|
|
1211
1211
|
human: "ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id>",
|
|
1212
|
-
automation: "ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id>
|
|
1212
|
+
automation: "ornn vpn-connections disconnect <canonical-reservation-id> <exact-name-or-id> --idempotency-key <key> --format json",
|
|
1213
1213
|
acceptedReferences: [
|
|
1214
1214
|
"canonical capacity-reservation ID",
|
|
1215
1215
|
"canonical or exact unique VPN-connection reference",
|
|
@@ -16,7 +16,6 @@ function nodePrefix(role, id) {
|
|
|
16
16
|
}
|
|
17
17
|
const latestInput = z.object({
|
|
18
18
|
node_id: z.string().min(1),
|
|
19
|
-
nodeId: z.string().optional(),
|
|
20
19
|
start: z.string().optional(),
|
|
21
20
|
end: z.string().optional(),
|
|
22
21
|
span: z.string().optional(),
|
|
@@ -25,7 +24,6 @@ const latestInput = z.object({
|
|
|
25
24
|
});
|
|
26
25
|
const logsLatestInput = z.object({
|
|
27
26
|
node_id: z.string().min(1),
|
|
28
|
-
nodeId: z.string().optional(),
|
|
29
27
|
stream: z.enum(["serial", "kernel"]).default("kernel"),
|
|
30
28
|
count: z.number().int().min(1).max(1000).optional(),
|
|
31
29
|
before: z.string().optional(),
|
|
@@ -34,7 +32,6 @@ const logsLatestInput = z.object({
|
|
|
34
32
|
});
|
|
35
33
|
const tailInput = z.object({
|
|
36
34
|
node_id: z.string().min(1),
|
|
37
|
-
nodeId: z.string().optional(),
|
|
38
35
|
duration: z.number().int().min(1).max(120).optional(),
|
|
39
36
|
max_events: z.number().int().min(1).max(200).optional(),
|
|
40
37
|
fields: z.string().optional(),
|
|
@@ -42,9 +39,6 @@ const tailInput = z.object({
|
|
|
42
39
|
const logsTailInput = tailInput.extend({
|
|
43
40
|
stream: z.enum(["serial", "kernel"]).default("kernel"),
|
|
44
41
|
});
|
|
45
|
-
function nodeId(input) {
|
|
46
|
-
return (input.node_id || input.nodeId || "").trim();
|
|
47
|
-
}
|
|
48
42
|
function expandSpan(span, start, end) {
|
|
49
43
|
if (start?.trim() && end?.trim()) {
|
|
50
44
|
return { start: start.trim(), end: end.trim() };
|
|
@@ -72,7 +66,10 @@ export const telemetryLatestCapability = defineCapability({
|
|
|
72
66
|
start: { type: "string" },
|
|
73
67
|
end: { type: "string" },
|
|
74
68
|
max_points: { type: "number", description: "Default 240, max 500." },
|
|
75
|
-
metrics: {
|
|
69
|
+
metrics: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Optional comma-separated metric names; omit for all stored metrics.",
|
|
72
|
+
},
|
|
76
73
|
},
|
|
77
74
|
required: ["node_id"],
|
|
78
75
|
},
|
|
@@ -80,7 +77,7 @@ export const telemetryLatestCapability = defineCapability({
|
|
|
80
77
|
input: latestInput,
|
|
81
78
|
execute: async (ctx, input) => {
|
|
82
79
|
const parsed = latestInput.parse(input);
|
|
83
|
-
const id =
|
|
80
|
+
const id = parsed.node_id.trim();
|
|
84
81
|
const range = expandSpan(parsed.span, parsed.start, parsed.end);
|
|
85
82
|
return ctx.fetch(queryPath(`${nodePrefix(ctx.role, id)}/telemetry/query`, {
|
|
86
83
|
start: range.start,
|
|
@@ -90,6 +87,34 @@ export const telemetryLatestCapability = defineCapability({
|
|
|
90
87
|
}), { method: "GET" });
|
|
91
88
|
},
|
|
92
89
|
});
|
|
90
|
+
export const telemetryLatestBatchCapability = defineCapability({
|
|
91
|
+
id: "telemetry.latest_batch",
|
|
92
|
+
domain: "observability",
|
|
93
|
+
roles: ["user", "admin"],
|
|
94
|
+
description: "Latest telemetry tip (last heartbeat, public_ip, GPU/CPU/memory utilization) for up to 100 nodes in one call. Prefer this over telemetry_latest per node when checking several nodes.",
|
|
95
|
+
mutation: "read",
|
|
96
|
+
http: { method: "POST", path: "/v1/observability/nodes/telemetry/latest" },
|
|
97
|
+
cli: false,
|
|
98
|
+
slack: {
|
|
99
|
+
parameters: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
node_ids: {
|
|
103
|
+
type: "array",
|
|
104
|
+
items: { type: "string", format: "uuid" },
|
|
105
|
+
minItems: 1,
|
|
106
|
+
maxItems: 100,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ["node_ids"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
input: z.object({ node_ids: z.array(z.uuid()).min(1).max(100) }),
|
|
113
|
+
execute: async (ctx, input) => ctx.fetch("/v1/observability/nodes/telemetry/latest", {
|
|
114
|
+
method: "POST",
|
|
115
|
+
body: { node_ids: input.node_ids },
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
93
118
|
export const logsLatestCapability = defineCapability({
|
|
94
119
|
id: "logs.latest",
|
|
95
120
|
domain: "observability",
|
|
@@ -104,8 +129,14 @@ export const logsLatestCapability = defineCapability({
|
|
|
104
129
|
node_id: { type: "string", description: "Node UUID from nodes.list." },
|
|
105
130
|
stream: { type: "string", enum: ["serial", "kernel"] },
|
|
106
131
|
count: { type: "number", description: "Page size. Default 200, max 1000." },
|
|
107
|
-
before: {
|
|
108
|
-
|
|
132
|
+
before: {
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "RFC3339 upper bound. Default now. Keep fixed when paging.",
|
|
135
|
+
},
|
|
136
|
+
start: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "RFC3339 lower bound. Default seven days before the upper bound.",
|
|
139
|
+
},
|
|
109
140
|
cursor: { type: "string", description: "next_cursor from the previous page." },
|
|
110
141
|
},
|
|
111
142
|
required: ["node_id"],
|
|
@@ -114,7 +145,7 @@ export const logsLatestCapability = defineCapability({
|
|
|
114
145
|
input: logsLatestInput,
|
|
115
146
|
execute: async (ctx, input) => {
|
|
116
147
|
const parsed = logsLatestInput.parse(input);
|
|
117
|
-
const id =
|
|
148
|
+
const id = parsed.node_id.trim();
|
|
118
149
|
const end = parsed.before?.trim() || new Date().toISOString();
|
|
119
150
|
const start = parsed.start?.trim() || new Date(Date.parse(end) - 7 * 86_400_000).toISOString();
|
|
120
151
|
return ctx.fetch(queryPath(`${nodePrefix(ctx.role, id)}/logs/${parsed.stream}/query`, {
|
|
@@ -145,7 +176,10 @@ export const telemetrySnapshotCapability = defineCapability({
|
|
|
145
176
|
required: ["node_id"],
|
|
146
177
|
},
|
|
147
178
|
},
|
|
148
|
-
input: z.object({
|
|
179
|
+
input: z.object({
|
|
180
|
+
node_id: z.string().min(1),
|
|
181
|
+
limit: z.number().int().min(1).max(1000).optional(),
|
|
182
|
+
}),
|
|
149
183
|
execute: async (ctx, input) => ctx.fetch(queryPath(`/v1/observability/nodes/${encodeURIComponent(input.node_id)}/telemetry/latest`, {
|
|
150
184
|
limit: String(input.limit ?? 1),
|
|
151
185
|
}), { method: "GET" }),
|
|
@@ -172,7 +206,7 @@ export const telemetryTailCapability = defineCapability({
|
|
|
172
206
|
input: tailInput,
|
|
173
207
|
execute: async (ctx, input) => {
|
|
174
208
|
const parsed = tailInput.parse(input);
|
|
175
|
-
const id =
|
|
209
|
+
const id = parsed.node_id.trim();
|
|
176
210
|
return ctx.fetch(queryPath(`${nodePrefix(ctx.role, id)}/telemetry/subscribe`, {
|
|
177
211
|
fields: parsed.fields,
|
|
178
212
|
duration: String(parsed.duration ?? 20),
|
|
@@ -202,7 +236,7 @@ export const logsTailCapability = defineCapability({
|
|
|
202
236
|
input: logsTailInput,
|
|
203
237
|
execute: async (ctx, input) => {
|
|
204
238
|
const parsed = logsTailInput.parse(input);
|
|
205
|
-
const id =
|
|
239
|
+
const id = parsed.node_id.trim();
|
|
206
240
|
return ctx.fetch(queryPath(`${nodePrefix(ctx.role, id)}/logs/${parsed.stream}/subscribe`, {
|
|
207
241
|
duration: String(parsed.duration ?? 20),
|
|
208
242
|
max_events: String(parsed.max_events ?? 40),
|
|
@@ -212,6 +246,7 @@ export const logsTailCapability = defineCapability({
|
|
|
212
246
|
export const observabilityCapabilities = [
|
|
213
247
|
telemetrySnapshotCapability,
|
|
214
248
|
telemetryLatestCapability,
|
|
249
|
+
telemetryLatestBatchCapability,
|
|
215
250
|
telemetryTailCapability,
|
|
216
251
|
logsLatestCapability,
|
|
217
252
|
logsTailCapability,
|