@ornncompute/cli 1.0.72 → 1.0.97

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 (59) hide show
  1. package/README.md +55 -24
  2. package/package.json +4 -2
  3. package/src/admin-capacity-reservations.mjs +6 -13
  4. package/src/admin-command-handlers.mjs +2 -1
  5. package/src/admin-credits.mjs +1 -2
  6. package/src/admin-enrollment.mjs +22 -30
  7. package/src/admin-mutations.mjs +6 -56
  8. package/src/admin-node-diagnostics.mjs +3 -41
  9. package/src/admin-node-lifecycle.mjs +40 -160
  10. package/src/admin-node-sanitize.mjs +243 -0
  11. package/src/admin-shell.mjs +39 -32
  12. package/src/catalog-shell.mjs +6 -6
  13. package/src/evidence-commands.mjs +4 -6
  14. package/src/external-bucket-commands.mjs +76 -96
  15. package/src/interaction-runtime.mjs +5 -37
  16. package/src/kubernetes-commands.mjs +25 -183
  17. package/src/launch-mode-handlers.mjs +0 -1
  18. package/src/machine-reference.mjs +33 -56
  19. package/src/mount-commands.mjs +25 -131
  20. package/src/slurm-commands.mjs +13 -2
  21. package/src/storage-commands.mjs +2 -87
  22. package/src/vm-commands.mjs +48 -101
  23. package/vendor/api-client/commerce.js +743 -0
  24. package/vendor/capabilities/cli.js +7688 -7
  25. package/vendor/capabilities/capabilities/admin-capacity-reservations.js +0 -101
  26. package/vendor/capabilities/capabilities/admin-enrollment.js +0 -321
  27. package/vendor/capabilities/capabilities/admin-node-diagnostics.js +0 -88
  28. package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +0 -235
  29. package/vendor/capabilities/capabilities/admin.js +0 -91
  30. package/vendor/capabilities/capabilities/cli.js +0 -156
  31. package/vendor/capabilities/capabilities/clusters.js +0 -248
  32. package/vendor/capabilities/capabilities/commerce-market.js +0 -405
  33. package/vendor/capabilities/capabilities/compute.js +0 -523
  34. package/vendor/capabilities/capabilities/credits.js +0 -78
  35. package/vendor/capabilities/capabilities/evidence.js +0 -145
  36. package/vendor/capabilities/capabilities/external-buckets.js +0 -191
  37. package/vendor/capabilities/capabilities/fleet.js +0 -306
  38. package/vendor/capabilities/capabilities/identity.js +0 -129
  39. package/vendor/capabilities/capabilities/listings.js +0 -398
  40. package/vendor/capabilities/capabilities/locality.js +0 -42
  41. package/vendor/capabilities/capabilities/market-shared.js +0 -46
  42. package/vendor/capabilities/capabilities/markets.js +0 -482
  43. package/vendor/capabilities/capabilities/mounts.js +0 -176
  44. package/vendor/capabilities/capabilities/native-listing.js +0 -99
  45. package/vendor/capabilities/capabilities/networks.js +0 -1264
  46. package/vendor/capabilities/capabilities/observability.js +0 -218
  47. package/vendor/capabilities/capabilities/organization-invites.js +0 -53
  48. package/vendor/capabilities/capabilities/reservation-list.js +0 -68
  49. package/vendor/capabilities/capabilities/staff-shared.js +0 -64
  50. package/vendor/capabilities/capabilities/tokens.js +0 -165
  51. package/vendor/capabilities/capabilities/users.js +0 -159
  52. package/vendor/capabilities/capabilities/volumes.js +0 -178
  53. package/vendor/capabilities/capability.js +0 -99
  54. package/vendor/capabilities/cli-catalog.js +0 -73
  55. package/vendor/capabilities/cli-migrations.js +0 -264
  56. package/vendor/capabilities/names.js +0 -6
  57. package/vendor/capabilities/software-catalog.js +0 -82
  58. package/vendor/capabilities/spec-catalog.js +0 -151
  59. package/vendor/capabilities/supplier-directory.js +0 -20
@@ -1,101 +0,0 @@
1
- import { z } from "zod";
2
- import { defineCapability, preview } from "../capability.js";
3
- import { ensureFacility, locateRegion, unplacedLocation } from "./locality.js";
4
- import { nativeListingCreateInput } from "./native-listing.js";
5
- import { idempotencyHeaders, microUsd } from "./market-shared.js";
6
- function adminHelp(command, outcome, human, automation, review) {
7
- return {
8
- audience: "admin",
9
- category: "resource",
10
- command,
11
- outcome,
12
- human,
13
- automation,
14
- acceptedReferences: [],
15
- ...(review ? { review } : {}),
16
- };
17
- }
18
- export const adminCapacityOffersCreateCapability = defineCapability({
19
- id: "listing.create",
20
- domain: "listings",
21
- roles: ["admin"],
22
- description: "Create native capacity with an organization, a facilityId or a location, hardware, availability and advertised terms. A location is any state, city, address, or catalog region (us-west-3); Ornn places it in a catalog region and uses the organization's facility there. Primary capacity needs daysToRfs (0 when ready now) or both availableAt and availableUntil. nodeIds attach nodes at creation. Returns {listing, terms}. Requires an idempotencyKey reused on retries and confirm: true.",
23
- mutation: "preview-confirm",
24
- http: { method: "POST", path: "/commerce/v2/listings" },
25
- input: nativeListingCreateInput,
26
- slack: false,
27
- cli: {
28
- command: ["admin", "capacity-offers", "create"],
29
- vnext: adminHelp(["admin", "capacity-offers", "create"], "Create native capacity and its advertised financial terms.", "ornn admin capacity-offers create --gpu-slug GPU --gpus-per-node COUNT (--facility-id UUID | --location PLACE) --provisioning-type bare-metal|virtual-machine --state draft|available --terms JSON --idempotency-key KEY (--days-to-rfs DAYS | --available-at TIME --available-until TIME) [--capacity-node-count COUNT] [--node-ids JSON] [--organization-id UUID]", "Requires an eligible human terminal; --location also needs --organization-id; reuse --idempotency-key on retries.", "Review the native parent fields and each financial term before default-no consent."),
30
- },
31
- execute: async (ctx, input) => {
32
- const { confirm: _confirm, idempotencyKey, location, ...body } = nativeListingCreateInput.parse(input);
33
- const located = location ? await locateRegion(ctx.fetch, location) : null;
34
- if (location && !located)
35
- throw new Error(unplacedLocation(location));
36
- if (!ctx.confirmed)
37
- return preview("listing.create", located ? { ...body, located } : body);
38
- const facilityId = located
39
- ? (await ensureFacility(ctx.fetch, body.organizationId, located.region)).facilityId
40
- : body.facilityId;
41
- return ctx.fetch("/commerce/v2/listings", {
42
- method: "POST",
43
- body: { ...body, facilityId },
44
- headers: idempotencyHeaders(ctx, idempotencyKey),
45
- });
46
- },
47
- });
48
- // Markets books whole-second windows; a fractional digit would shift or refuse one.
49
- const wholeSecondInstant = z.iso
50
- .datetime({ offset: true })
51
- .refine((value) => !/\.\d*[1-9]/.test(value), "Use a time in whole seconds.");
52
- export const reservationCreateInput = z.object({
53
- organizationId: z.string().uuid().describe("The customer organization the window is booked for."),
54
- listingId: z
55
- .string()
56
- .uuid()
57
- .describe("The listing's capacityListingId. Staff may book any available listing."),
58
- listingTermId: z
59
- .string()
60
- .uuid()
61
- .describe("The listingTermsId of a non-renewable Reserved term on that listing."),
62
- nodes: z.number().int().positive().max(2_147_483_647).describe("Whole nodes to book."),
63
- startsAt: wholeSecondInstant.describe("RFC 3339 start of the window in whole seconds, no more than five minutes ago."),
64
- endsAt: wholeSecondInstant.describe("RFC 3339 end of the window in whole seconds, at least an hour after the start and within the term's duration range."),
65
- pricePerGpuHourMicroUsd: microUsd.describe("The term's price in micro-USD per GPU-hour. A changed price refuses the booking."),
66
- idempotencyKey: z
67
- .string()
68
- .trim()
69
- .min(1)
70
- .max(200)
71
- .describe("Reuse this key when retrying the same booking."),
72
- });
73
- export const adminReservationsCreateCapability = defineCapability({
74
- id: "reservations.create",
75
- domain: "reservations",
76
- roles: ["admin"],
77
- description: "Book a window of a listing term for a customer organization (admin). Markets holds the GPUs until the window starts, then settles a Commerce reservation with the booking's ID. Returns {bookings}.",
78
- mutation: "preview-confirm",
79
- http: { method: "POST", path: "/v1/markets/bookings" },
80
- cli: {
81
- command: ["admin", "reservations", "create"],
82
- vnext: adminHelp(["admin", "reservations", "create"], "Book a listing term window for a customer organization.", "ornn admin reservations create --organization-id UUID --listing-id UUID --listing-term-id UUID --nodes COUNT --starts-at TIME --ends-at TIME --price-per-gpu-hour-micro-usd N --idempotency-key KEY", "Requires an eligible human terminal. Reuse --idempotency-key when retrying the same booking.", "Customer organization, listing, term, nodes, window, price, and idempotency key are reviewed before default-no consent."),
83
- },
84
- slack: false,
85
- input: reservationCreateInput,
86
- execute: async (ctx, input) => {
87
- const { organizationId, idempotencyKey, ...leg } = reservationCreateInput.parse(input);
88
- const body = { organizationId, legs: [leg] };
89
- if (!ctx.confirmed)
90
- return { preview: body };
91
- return ctx.fetch("/v1/markets/bookings", {
92
- method: "POST",
93
- body,
94
- headers: idempotencyHeaders(ctx, idempotencyKey),
95
- });
96
- },
97
- });
98
- export const adminCapacityReservationCapabilities = [
99
- adminCapacityOffersCreateCapability,
100
- adminReservationsCreateCapability,
101
- ];
@@ -1,321 +0,0 @@
1
- import { z } from "zod";
2
- import { defineCapability } from "../capability.js";
3
- import { buildEnrollmentInstallCommand, tokensCreateCapability, tokensListCapability, } from "./tokens.js";
4
- const UUID = z.string().uuid();
5
- const nonEmpty = z.string().trim().min(1);
6
- const tokenTtl = z
7
- .number()
8
- .int()
9
- .min(60)
10
- .max(24 * 60 * 60);
11
- function adminHelp(command, outcome, human, automation, acceptedReferences = [], review) {
12
- return {
13
- audience: "admin",
14
- category: "resource",
15
- command,
16
- outcome,
17
- human,
18
- automation,
19
- acceptedReferences,
20
- ...(review ? { review } : {}),
21
- };
22
- }
23
- function withVnextBinding(capability, help) {
24
- if (!help.command)
25
- throw new Error(`${capability.id} requires a canonical CLI command`);
26
- return {
27
- ...capability,
28
- cli: {
29
- command: [...help.command],
30
- vnext: help,
31
- },
32
- };
33
- }
34
- export const adminEnrollmentTokensListCapability = {
35
- ...withVnextBinding(tokensListCapability, adminHelp(["admin", "enrollment-tokens", "list"], "List enrollment-token metadata without token bytes.", "ornn admin enrollment-tokens list [--limit 50] [--cursor CURSOR]", "ornn admin enrollment-tokens list --limit 50 --format json")),
36
- execute: async (ctx) => {
37
- const payload = await tokensListCapability.execute(ctx, {});
38
- if (!ctx.cli)
39
- return payload;
40
- return {
41
- enrollment_tokens: rowsFromTokenList(payload)
42
- .map(tokenMetadata)
43
- .filter((token) => token !== null),
44
- };
45
- },
46
- };
47
- export const adminEnrollmentTokenCreateCapability = withVnextBinding(tokensCreateCapability, adminHelp(["admin", "enrollment-tokens", "create"], "Create one expiring enrollment token through an explicit secret export.", "ornn admin enrollment-tokens create --supplier UUID --expires-in SECONDS (--print | --output PATH) [--overwrite] [--yes]", "Unsupported: token creation requires an eligible human terminal.", ["canonical supplier organization UUID"], "Review the supplier, expiry, and secret destination before minting the token."));
48
- const tokenRevokeInput = z.object({
49
- token_id: UUID,
50
- expected_created_at: nonEmpty.optional(),
51
- idempotency_key: nonEmpty.optional(),
52
- });
53
- function rowsFromTokenList(payload) {
54
- if (Array.isArray(payload))
55
- return payload;
56
- if (!payload || typeof payload !== "object")
57
- return [];
58
- const source = payload;
59
- for (const key of ["enrollment_tokens", "tokens", "items", "data"]) {
60
- if (Array.isArray(source[key]))
61
- return source[key];
62
- }
63
- return [];
64
- }
65
- function tokenMetadata(value) {
66
- if (!value || typeof value !== "object")
67
- return null;
68
- const row = value;
69
- if (typeof row.id !== "string" || !row.id)
70
- return null;
71
- return {
72
- id: row.id,
73
- organization_id: typeof row.organization_id === "string" ? row.organization_id : null,
74
- status: typeof row.status === "string" ? row.status : "unknown",
75
- expires_at: typeof row.expires_at === "string" ? row.expires_at : null,
76
- created_at: typeof row.created_at === "string" ? row.created_at : null,
77
- };
78
- }
79
- function codedError(code, message) {
80
- const error = new Error(message);
81
- error.detail = { code, retryable: false };
82
- return error;
83
- }
84
- async function readExactToken(ctx, tokenId, expectedCreatedAt) {
85
- // Filter at the owner before its list limit; an older active token may not be in the latest 500.
86
- const listed = await ctx.fetch(`/enrollment-tokens?id=${encodeURIComponent(tokenId)}`, {
87
- method: "GET",
88
- });
89
- const target = rowsFromTokenList(listed)
90
- .map(tokenMetadata)
91
- .find((candidate) => candidate?.id === tokenId);
92
- if (!target)
93
- return { disposition: "satisfied", target: null };
94
- if (expectedCreatedAt && target.created_at !== expectedCreatedAt) {
95
- return {
96
- disposition: "conflict",
97
- target,
98
- message: `Enrollment token ${tokenId} was replaced after ${expectedCreatedAt}; refusing to revoke it.`,
99
- };
100
- }
101
- if (target.status === "revoked" || target.status === "expired") {
102
- return { disposition: "satisfied", target };
103
- }
104
- return { disposition: "apply", target };
105
- }
106
- export const adminEnrollmentTokenRevokeCapability = defineCapability({
107
- id: "tokens.revoke",
108
- domain: "fleet",
109
- roles: ["admin"],
110
- description: "Revoke one exact enrollment-token generation after reconciling its durable goal.",
111
- mutation: "preview-confirm",
112
- http: { method: "DELETE", path: "/enrollment-tokens/{id}" },
113
- cli: {
114
- command: ["admin", "enrollment-tokens", "revoke"],
115
- vnext: adminHelp(["admin", "enrollment-tokens", "revoke"], "Revoke one exact enrollment-token generation.", "ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] [--yes]", "ornn admin enrollment-tokens revoke <token-id> --expected-created-at TIME --yes --idempotency-key KEY --format json", ["canonical token UUID", "token created_at generation"], "Review the exact token generation and the loss of future enrollment access."),
116
- },
117
- mcp: false,
118
- slack: false,
119
- input: tokenRevokeInput,
120
- execute: async (ctx, input) => {
121
- const parsed = tokenRevokeInput.parse(input);
122
- const current = await readExactToken(ctx, parsed.token_id, parsed.expected_created_at);
123
- if (!ctx.confirmed)
124
- return current;
125
- if (current.disposition === "conflict") {
126
- throw codedError("token_identity_changed", current.message);
127
- }
128
- if (current.disposition === "satisfied") {
129
- return {
130
- id: parsed.token_id,
131
- created_at: parsed.expected_created_at ?? current.target?.created_at ?? null,
132
- status: "revoked",
133
- reconciled: true,
134
- };
135
- }
136
- if (!current.target.created_at) {
137
- throw codedError("token_identity_unavailable", `Enrollment token ${parsed.token_id} has no stable creation identity and cannot be revoked safely.`);
138
- }
139
- const revoked = (await ctx.fetch(`/enrollment-tokens/${encodeURIComponent(parsed.token_id)}`, {
140
- method: "DELETE",
141
- ...(parsed.idempotency_key
142
- ? { headers: { "Idempotency-Key": parsed.idempotency_key } }
143
- : {}),
144
- }));
145
- return {
146
- id: parsed.token_id,
147
- created_at: current.target.created_at,
148
- organization_id: current.target.organization_id,
149
- expires_at: current.target.expires_at,
150
- status: "revoked",
151
- reconciled: false,
152
- ...(typeof revoked?.deleted === "boolean" ? { deleted: revoked.deleted } : {}),
153
- };
154
- },
155
- });
156
- const nodeEnrollInput = z.object({
157
- node_id: UUID,
158
- host: nonEmpty,
159
- organization_id: UUID,
160
- facility_id: UUID,
161
- filter: z.string().optional(),
162
- identity_file: nonEmpty,
163
- private_key: z.string().min(1).optional(),
164
- username: nonEmpty.default("root"),
165
- port: z.number().int().min(1).max(65535).default(22),
166
- expires_in_seconds: tokenTtl,
167
- installer_base: nonEmpty.optional(),
168
- replace_existing_management: z.literal(true),
169
- verbose: z.boolean().default(false),
170
- });
171
- function safeRemoteResult(value) {
172
- if (!value || typeof value !== "object")
173
- return {};
174
- const row = value;
175
- const safe = {};
176
- for (const key of ["id", "status", "state", "exit_code", "started_at", "completed_at"]) {
177
- const field = row[key];
178
- if (typeof field === "string" || typeof field === "number" || typeof field === "boolean") {
179
- safe[key] = field;
180
- }
181
- }
182
- return safe;
183
- }
184
- export const adminNodeEnrollCapability = defineCapability({
185
- id: "nodes.enroll",
186
- domain: "fleet",
187
- roles: ["admin"],
188
- description: "Enroll one explicit node over SSH with an explicit supplier and facility assignment.",
189
- mutation: "preview-confirm",
190
- http: { method: "POST", path: "/remote" },
191
- cli: {
192
- command: ["admin", "nodes", "enroll"],
193
- vnext: adminHelp(["admin", "nodes", "enroll"], "Install Ornn management on one explicitly assigned node.", "ornn admin nodes enroll <node-id> --host HOST --supplier UUID --facility UUID [--filter COMPARTMENT_IDS] --identity-file PATH --token-ttl SECONDS --replace-existing-management [--verbose] [--yes]", "Unsupported: remote enrollment requires an eligible human terminal.", ["canonical node UUID", "canonical supplier organization UUID", "canonical facility UUID"], "Review node identity, SSH target, assignment, compartment filter, and replacement of existing management."),
194
- },
195
- mcp: false,
196
- slack: false,
197
- input: nodeEnrollInput,
198
- execute: async (ctx, input) => {
199
- const parsed = nodeEnrollInput.parse(input);
200
- if (!ctx.confirmed) {
201
- return {
202
- node_id: parsed.node_id,
203
- host: parsed.host,
204
- organization_id: parsed.organization_id,
205
- facility_id: parsed.facility_id,
206
- filter: parsed.filter,
207
- expires_in_seconds: parsed.expires_in_seconds,
208
- replace_existing_management: true,
209
- };
210
- }
211
- if (!parsed.private_key) {
212
- throw codedError("identity_file_unreadable", "The SSH identity file could not be read.");
213
- }
214
- const minted = (await ctx.fetch("/enrollment-tokens", {
215
- method: "POST",
216
- body: {
217
- organization_id: parsed.organization_id,
218
- expires_in_seconds: parsed.expires_in_seconds,
219
- },
220
- }));
221
- const token = typeof minted.token === "string"
222
- ? minted.token
223
- : typeof minted.plaintext === "string"
224
- ? minted.plaintext
225
- : "";
226
- if (!token) {
227
- throw codedError("token_material_unavailable", "The enrollment token was created without usable token material.");
228
- }
229
- const command = buildEnrollmentInstallCommand({
230
- token,
231
- base: parsed.installer_base ?? ctx.installerBaseUrl,
232
- facilityId: parsed.facility_id,
233
- filter: parsed.filter,
234
- restoreNodeId: parsed.node_id,
235
- force: true,
236
- });
237
- const remote = await ctx.fetch("/remote", {
238
- method: "POST",
239
- body: {
240
- host: parsed.host,
241
- username: parsed.username,
242
- port: parsed.port,
243
- private_key: parsed.private_key,
244
- command,
245
- timeout_seconds: 600,
246
- },
247
- });
248
- return {
249
- node_id: parsed.node_id,
250
- host: parsed.host,
251
- organization_id: parsed.organization_id,
252
- facility_id: parsed.facility_id,
253
- filter: parsed.filter,
254
- management: "replaced",
255
- token_metadata: {
256
- id: typeof minted.id === "string" ? minted.id : null,
257
- expires_at: typeof minted.expires_at === "string" ? minted.expires_at : null,
258
- },
259
- remote: safeRemoteResult(remote),
260
- };
261
- },
262
- });
263
- const nodeKeyInput = z.object({ node_id: UUID });
264
- export const adminNodeExportKeyCapability = defineCapability({
265
- id: "nodes.export-key",
266
- domain: "fleet",
267
- roles: ["admin"],
268
- description: "Export an enrolled node's private admin key only through an explicit secret destination.",
269
- mutation: "read",
270
- http: { method: "GET", path: "/enrollment/admin-ssh-key" },
271
- cli: {
272
- command: ["admin", "nodes", "export-key"],
273
- vnext: adminHelp(["admin", "nodes", "export-key"], "Export one node admin key deliberately.", "ornn admin nodes export-key <node-id> (--print | --output PATH) [--overwrite]", "ornn admin nodes export-key <node-id> --output PATH [--overwrite] --format json", ["canonical node UUID"]),
274
- },
275
- mcp: false,
276
- slack: false,
277
- input: nodeKeyInput,
278
- execute: async (ctx, input) => {
279
- const parsed = nodeKeyInput.parse(input);
280
- return ctx.fetch(`/enrollment/admin-ssh-key?node_id=${encodeURIComponent(parsed.node_id)}`, {
281
- method: "GET",
282
- });
283
- },
284
- });
285
- export const adminNodeRotateKeyCapability = defineCapability({
286
- id: "nodes.rotate-key",
287
- domain: "fleet",
288
- roles: ["admin"],
289
- description: "Replace a node's admin key and export the new private key explicitly.",
290
- mutation: "preview-confirm",
291
- http: { method: "POST", path: "/enrollment/admin-ssh-key" },
292
- cli: {
293
- command: ["admin", "nodes", "rotate-key"],
294
- vnext: adminHelp(["admin", "nodes", "rotate-key"], "Rotate one node admin key and export the replacement deliberately.", "ornn admin nodes rotate-key <node-id> (--print | --output PATH) [--overwrite] [--yes]", "Unsupported: key rotation requires an eligible human terminal.", ["canonical node UUID"], "Review the node and loss of access through the previous private key."),
295
- },
296
- mcp: false,
297
- slack: false,
298
- input: nodeKeyInput,
299
- execute: async (ctx, input) => {
300
- const parsed = nodeKeyInput.parse(input);
301
- if (!ctx.confirmed)
302
- return { node_id: parsed.node_id };
303
- return ctx.fetch(`/enrollment/admin-ssh-key?node_id=${encodeURIComponent(parsed.node_id)}`, {
304
- method: "POST",
305
- });
306
- },
307
- });
308
- const replacements = {
309
- "tokens.create": adminEnrollmentTokenCreateCapability,
310
- "tokens.list": adminEnrollmentTokensListCapability,
311
- };
312
- /** Applies the canonical admin CLI bindings without changing MCP or Slack identities. */
313
- export function bindAdminEnrollmentCapabilities(capabilities) {
314
- return capabilities.map((capability) => replacements[capability.id] ?? capability);
315
- }
316
- export const adminEnrollmentCapabilities = [
317
- adminEnrollmentTokenRevokeCapability,
318
- adminNodeEnrollCapability,
319
- adminNodeExportKeyCapability,
320
- adminNodeRotateKeyCapability,
321
- ];
@@ -1,88 +0,0 @@
1
- import { z } from "zod";
2
- import { defineCapability } from "../capability.js";
3
- import { nodeExecCapability } from "./fleet.js";
4
- const nodeId = z
5
- .string()
6
- .min(1)
7
- .refine((value) => value === value.trim(), {
8
- message: "node_id must be an exact canonical node ID without surrounding whitespace",
9
- });
10
- export const adminNodeDiagnoseInput = z.object({
11
- node_id: nodeId,
12
- });
13
- export const adminNodeExecInput = z.object({
14
- node_id: nodeId,
15
- command: z.string().min(1),
16
- timeout_seconds: z.number().int().min(1).max(900),
17
- });
18
- function adminNodeHelp(command, outcome, human, automation, review) {
19
- return {
20
- audience: "admin",
21
- category: "resource",
22
- command,
23
- outcome,
24
- human,
25
- automation,
26
- acceptedReferences: ["canonical node ID"],
27
- review,
28
- };
29
- }
30
- export const adminNodeDiagnoseCapability = defineCapability({
31
- id: "node.diagnose",
32
- domain: "fleet",
33
- roles: ["admin"],
34
- description: "Queue the staff-authorized diagnosis workflow for one exact node after a live review.",
35
- mutation: "preview-confirm",
36
- http: { method: "POST", path: "/jobs" },
37
- cli: {
38
- command: ["admin", "nodes", "diagnose"],
39
- vnext: adminNodeHelp(["admin", "nodes", "diagnose"], "Queue diagnosis for one exact node.", "ornn admin nodes diagnose <node-id>", "Unavailable in machine and redirected modes; use an eligible human terminal.", "Exact node, lifecycle, reachability, and diagnosis workflow."),
40
- },
41
- mcp: false,
42
- slack: false,
43
- input: adminNodeDiagnoseInput,
44
- execute: async (ctx, input) => {
45
- const parsed = adminNodeDiagnoseInput.parse(input);
46
- if (!ctx.confirmed) {
47
- return {
48
- action: "node.diagnose",
49
- payload: { node_id: parsed.node_id, source: { mode: "cli" }, burn_time_minutes: 0 },
50
- };
51
- }
52
- return ctx.fetch("/jobs", {
53
- method: "POST",
54
- body: { node_id: parsed.node_id, source: { mode: "cli" }, burn_time_minutes: 0 },
55
- });
56
- },
57
- });
58
- export const adminNodeExecCapability = {
59
- ...nodeExecCapability,
60
- mutation: "preview-confirm",
61
- cli: {
62
- ...nodeExecCapability.cli,
63
- vnext: adminNodeHelp(["admin", "nodes", "exec"], "Execute exact command bytes on one exact node.", "ornn admin nodes exec <node-id> --command COMMAND --timeout DURATION", "Unavailable in machine and redirected modes; use an eligible human terminal.", "Exact node, lifecycle, reachability, command bytes, and timeout."),
64
- },
65
- input: adminNodeExecInput,
66
- execute: async (ctx, input) => {
67
- const parsed = adminNodeExecInput.parse(input);
68
- if (ctx.cli && !ctx.confirmed) {
69
- return {
70
- action: "node.exec",
71
- payload: {
72
- node_id: parsed.node_id,
73
- command: parsed.command,
74
- timeout_seconds: parsed.timeout_seconds,
75
- },
76
- };
77
- }
78
- return nodeExecCapability.execute(ctx, {
79
- node_id: parsed.node_id,
80
- command: parsed.command,
81
- timeout_seconds: parsed.timeout_seconds,
82
- });
83
- },
84
- };
85
- export const adminNodeDiagnosticCapabilities = [
86
- adminNodeDiagnoseCapability,
87
- adminNodeExecCapability,
88
- ];