@ornncompute/cli 0.2.9 → 0.2.10

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.
@@ -5,55 +5,50 @@ export declare const enrollmentTokenRevokeInput: z.ZodObject<{
5
5
  }, z.core.$strip>;
6
6
  export declare const enrollmentTokenRevokeCapability: import("../capability.ts").Capability<unknown, unknown>;
7
7
  export declare const operatorCreateInput: z.ZodObject<{
8
- slug: z.ZodString;
9
- displayName: z.ZodString;
10
- mode: z.ZodOptional<z.ZodEnum<{
11
- managed: "managed";
12
- federated: "federated";
13
- }>>;
14
- status: z.ZodOptional<z.ZodEnum<{
15
- active: "active";
16
- unavailable: "unavailable";
17
- revoked: "revoked";
18
- }>>;
8
+ slug: z.ZodOptional<z.ZodString>;
9
+ displayName: z.ZodOptional<z.ZodString>;
10
+ display_name: z.ZodOptional<z.ZodString>;
11
+ name: z.ZodOptional<z.ZodString>;
19
12
  confirm: z.ZodOptional<z.ZodBoolean>;
20
13
  }, z.core.$strip>;
21
14
  export declare const operatorCreateCapability: import("../capability.ts").Capability<unknown, unknown>;
22
15
  export declare const operatorUpdateInput: z.ZodObject<{
23
- operatorId: z.ZodString;
16
+ operatorId: z.ZodOptional<z.ZodString>;
17
+ operator_id: z.ZodOptional<z.ZodString>;
24
18
  displayName: z.ZodOptional<z.ZodString>;
25
- slug: z.ZodOptional<z.ZodString>;
26
- mode: z.ZodOptional<z.ZodEnum<{
27
- managed: "managed";
28
- federated: "federated";
29
- }>>;
30
- status: z.ZodOptional<z.ZodEnum<{
31
- active: "active";
32
- unavailable: "unavailable";
33
- revoked: "revoked";
34
- }>>;
19
+ display_name: z.ZodOptional<z.ZodString>;
20
+ name: z.ZodOptional<z.ZodString>;
21
+ version: z.ZodOptional<z.ZodNumber>;
35
22
  confirm: z.ZodOptional<z.ZodBoolean>;
36
23
  }, z.core.$strip>;
37
24
  export declare const operatorUpdateCapability: import("../capability.ts").Capability<unknown, unknown>;
38
25
  export declare const operatorDeleteInput: z.ZodObject<{
39
- operatorId: z.ZodString;
26
+ operatorId: z.ZodOptional<z.ZodString>;
27
+ operator_id: z.ZodOptional<z.ZodString>;
40
28
  confirm: z.ZodOptional<z.ZodBoolean>;
41
29
  }, z.core.$strip>;
42
30
  export declare const operatorDeleteCapability: import("../capability.ts").Capability<unknown, unknown>;
43
31
  export declare const facilityCreateInput: z.ZodObject<{
44
- operatorId: z.ZodString;
45
- slug: z.ZodString;
46
- displayName: z.ZodString;
32
+ operatorId: z.ZodOptional<z.ZodString>;
33
+ operator_id: z.ZodOptional<z.ZodString>;
34
+ slug: z.ZodOptional<z.ZodString>;
35
+ displayName: z.ZodOptional<z.ZodString>;
36
+ display_name: z.ZodOptional<z.ZodString>;
37
+ name: z.ZodOptional<z.ZodString>;
47
38
  region: z.ZodString;
48
39
  zone: z.ZodOptional<z.ZodString>;
49
40
  confirm: z.ZodOptional<z.ZodBoolean>;
50
41
  }, z.core.$strip>;
51
42
  export declare const facilityCreateCapability: import("../capability.ts").Capability<unknown, unknown>;
52
43
  export declare const facilityUpdateInput: z.ZodObject<{
53
- facilityId: z.ZodString;
44
+ facilityId: z.ZodOptional<z.ZodString>;
45
+ facility_id: z.ZodOptional<z.ZodString>;
54
46
  displayName: z.ZodOptional<z.ZodString>;
47
+ display_name: z.ZodOptional<z.ZodString>;
48
+ name: z.ZodOptional<z.ZodString>;
55
49
  region: z.ZodOptional<z.ZodString>;
56
50
  zone: z.ZodOptional<z.ZodString>;
51
+ version: z.ZodOptional<z.ZodNumber>;
57
52
  confirm: z.ZodOptional<z.ZodBoolean>;
58
53
  }, z.core.$strip>;
59
54
  export declare const facilityUpdateCapability: import("../capability.ts").Capability<unknown, unknown>;
@@ -100,6 +95,8 @@ export declare const nodeBenchmarkCapability: import("../capability.ts").Capabil
100
95
  export declare const nodeDeployInput: z.ZodObject<{
101
96
  nodeId: z.ZodString;
102
97
  targetTenantId: z.ZodString;
98
+ commerceReservationId: z.ZodOptional<z.ZodString>;
99
+ commerce_reservation_id: z.ZodOptional<z.ZodString>;
103
100
  targetAuthUserId: z.ZodOptional<z.ZodString>;
104
101
  networkMode: z.ZodOptional<z.ZodEnum<{
105
102
  public: "public";
@@ -131,4 +128,13 @@ export declare const nodeAdminKeyInput: z.ZodObject<{
131
128
  confirm: z.ZodOptional<z.ZodBoolean>;
132
129
  }, z.core.$strip>;
133
130
  export declare const nodeAdminKeyCapability: import("../capability.ts").Capability<unknown, unknown>;
131
+ export declare const nodeOperatorTransferInput: z.ZodObject<{
132
+ nodeId: z.ZodString;
133
+ operatorId: z.ZodString;
134
+ operatorSlug: z.ZodString;
135
+ facilityId: z.ZodOptional<z.ZodString>;
136
+ reason: z.ZodOptional<z.ZodString>;
137
+ confirm: z.ZodOptional<z.ZodBoolean>;
138
+ }, z.core.$strip>;
139
+ export declare const nodeOperatorTransferCapability: import("../capability.ts").Capability<unknown, unknown>;
134
140
  export declare const staffFleetCapabilities: import("../capability.ts").Capability<unknown, unknown>[];
@@ -1,14 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { defineStaffMcpCapability, preview } from "../capability.js";
3
- import { mapBody, seg } from "./staff-shared.js";
3
+ import { mapBody, resolveVersion, seg } from "./staff-shared.js";
4
4
  // Staff fleet verbs: operators, facilities, nodes, node jobs, and enrollment
5
5
  // token revocation. MCP-only — see `defineStaffMcpCapability`.
6
6
  const slugSchema = z
7
7
  .string()
8
8
  .regex(/^[a-z0-9][a-z0-9-]*$/, "slug must be lowercase alphanumeric with dashes")
9
9
  .max(80);
10
- const operatorModeSchema = z.enum(["managed", "federated"]);
11
- const operatorStatusSchema = z.enum(["active", "unavailable", "revoked"]);
12
10
  export const enrollmentTokenRevokeInput = z.object({
13
11
  tokenId: z.string(),
14
12
  confirm: z.boolean().optional(),
@@ -28,27 +26,34 @@ export const enrollmentTokenRevokeCapability = defineStaffMcpCapability({
28
26
  return ctx.fetch(`/enrollment/tokens/${seg(parsed.tokenId)}/revoke`, { method: "POST" });
29
27
  },
30
28
  });
31
- export const operatorCreateInput = z.object({
32
- slug: slugSchema,
33
- displayName: z.string().min(1).max(160),
34
- mode: operatorModeSchema.optional(),
35
- status: operatorStatusSchema.optional(),
29
+ export const operatorCreateInput = z
30
+ .object({
31
+ slug: z.string().optional(),
32
+ displayName: z.string().min(1).max(160).optional(),
33
+ display_name: z.string().min(1).max(160).optional(),
34
+ name: z.string().min(1).max(160).optional(),
36
35
  confirm: z.boolean().optional(),
36
+ })
37
+ .refine((data) => Boolean(data.displayName || data.display_name || data.name), {
38
+ message: "displayName is required",
39
+ path: ["displayName"],
37
40
  });
38
41
  export const operatorCreateCapability = defineStaffMcpCapability({
39
42
  id: "operator.create",
40
43
  domain: "fleet",
41
44
  roles: ["admin"],
42
- description: "Internal staff only: create an operator (managed or federated). Requires confirm: true. Does not touch any hardware.",
45
+ description: "Internal staff only: create an operator. Requires confirm: true. Does not touch any hardware.",
43
46
  mutation: "preview-confirm",
44
47
  http: { method: "POST", path: "/provisioning/operators" },
45
48
  input: operatorCreateInput,
46
49
  execute: async (ctx, input) => {
47
50
  const parsed = operatorCreateInput.parse(input);
51
+ const displayName = (parsed.displayName || parsed.display_name || parsed.name).trim();
52
+ const rawSlug = parsed.slug ? parsed.slug.trim() : displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
53
+ const slug = rawSlug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
48
54
  const body = {
49
- slug: parsed.slug,
50
- display_name: parsed.displayName,
51
- ...mapBody(parsed, { mode: "mode", status: "status" }),
55
+ slug: slug || "operator-" + Date.now(),
56
+ display_name: displayName,
52
57
  };
53
58
  if (!ctx.confirmed)
54
59
  return preview("operator.create", body);
@@ -56,63 +61,91 @@ export const operatorCreateCapability = defineStaffMcpCapability({
56
61
  },
57
62
  });
58
63
  export const operatorUpdateInput = z.object({
59
- operatorId: z.string(),
64
+ operatorId: z.string().optional(),
65
+ operator_id: z.string().optional(),
60
66
  displayName: z.string().min(1).max(160).optional(),
61
- slug: slugSchema.optional(),
62
- mode: operatorModeSchema.optional(),
63
- status: operatorStatusSchema.optional(),
67
+ display_name: z.string().min(1).max(160).optional(),
68
+ name: z.string().min(1).max(160).optional(),
69
+ version: z.number().int().positive().optional(),
64
70
  confirm: z.boolean().optional(),
65
71
  });
66
72
  export const operatorUpdateCapability = defineStaffMcpCapability({
67
73
  id: "operator.update",
68
74
  domain: "fleet",
69
75
  roles: ["admin"],
70
- description: "Internal staff only: update an operator's display name, slug, mode, or status. Requires confirm: true.",
76
+ description: "Internal staff only: rename an operator. Display name is the only field commerce can patch; slug, mode, and status are fixed at creation. Requires confirm: true.",
71
77
  mutation: "preview-confirm",
72
78
  http: { method: "PATCH", path: "/provisioning/operators/{operatorId}" },
73
79
  input: operatorUpdateInput,
74
80
  execute: async (ctx, input) => {
75
81
  const parsed = operatorUpdateInput.parse(input);
76
- const body = mapBody(parsed, {
77
- displayName: "display_name",
78
- slug: "slug",
79
- mode: "mode",
80
- status: "status",
81
- });
82
+ const operatorId = parsed.operatorId || parsed.operator_id;
83
+ if (!operatorId) {
84
+ throw new Error("operatorId is required");
85
+ }
86
+ const displayName = parsed.displayName || parsed.display_name || parsed.name;
87
+ if (!displayName) {
88
+ throw new Error("displayName is required; it is the only operator field commerce can patch.");
89
+ }
90
+ const body = {
91
+ display_name: displayName,
92
+ version: await resolveVersion(ctx, `/provisioning/operators/${seg(operatorId)}`, parsed.version),
93
+ };
82
94
  if (!ctx.confirmed)
83
95
  return preview("operator.update", body);
84
- return ctx.fetch(`/provisioning/operators/${seg(parsed.operatorId)}`, {
96
+ return ctx.fetch(`/provisioning/operators/${seg(operatorId)}`, {
85
97
  method: "PATCH",
86
98
  body,
87
99
  });
88
100
  },
89
101
  });
90
102
  export const operatorDeleteInput = z.object({
91
- operatorId: z.string(),
103
+ operatorId: z.string().optional(),
104
+ operator_id: z.string().optional(),
92
105
  confirm: z.boolean().optional(),
93
106
  });
94
107
  export const operatorDeleteCapability = defineStaffMcpCapability({
95
108
  id: "operator.delete",
96
109
  domain: "fleet",
97
110
  roles: ["admin"],
98
- description: "Internal staff only: delete an operator. Requires confirm: true.",
111
+ description: "Internal staff only: delete an operator and cascade-delete every facility attached to it. " +
112
+ "Refuses while any listing still references the operator. Requires confirm: true.",
99
113
  mutation: "preview-confirm",
100
114
  http: { method: "DELETE", path: "/provisioning/operators/{operatorId}" },
101
115
  input: operatorDeleteInput,
102
116
  execute: async (ctx, input) => {
103
117
  const parsed = operatorDeleteInput.parse(input);
118
+ const operatorId = parsed.operatorId || parsed.operator_id;
119
+ if (!operatorId) {
120
+ throw new Error("operatorId is required");
121
+ }
104
122
  if (!ctx.confirmed)
105
- return preview("operator.delete", { operatorId: parsed.operatorId });
106
- return ctx.fetch(`/provisioning/operators/${seg(parsed.operatorId)}`, { method: "DELETE" });
123
+ return preview("operator.delete", { operatorId });
124
+ const result = await ctx.fetch(`/provisioning/operators/${seg(operatorId)}`, {
125
+ method: "DELETE",
126
+ });
127
+ return result ?? { deleted: true, operatorId };
107
128
  },
108
129
  });
109
- export const facilityCreateInput = z.object({
110
- operatorId: z.string(),
111
- slug: slugSchema,
112
- displayName: z.string().min(1).max(160),
130
+ export const facilityCreateInput = z
131
+ .object({
132
+ operatorId: z.string().optional(),
133
+ operator_id: z.string().optional(),
134
+ slug: z.string().optional(),
135
+ displayName: z.string().min(1).max(160).optional(),
136
+ display_name: z.string().min(1).max(160).optional(),
137
+ name: z.string().min(1).max(160).optional(),
113
138
  region: z.string().min(1).max(120),
114
139
  zone: z.string().max(120).optional(),
115
140
  confirm: z.boolean().optional(),
141
+ })
142
+ .refine((data) => Boolean(data.operatorId || data.operator_id), {
143
+ message: "operatorId is required",
144
+ path: ["operatorId"],
145
+ })
146
+ .refine((data) => Boolean(data.displayName || data.display_name || data.name), {
147
+ message: "displayName is required",
148
+ path: ["displayName"],
116
149
  });
117
150
  export const facilityCreateCapability = defineStaffMcpCapability({
118
151
  id: "facility.create",
@@ -124,10 +157,14 @@ export const facilityCreateCapability = defineStaffMcpCapability({
124
157
  input: facilityCreateInput,
125
158
  execute: async (ctx, input) => {
126
159
  const parsed = facilityCreateInput.parse(input);
160
+ const operatorId = (parsed.operatorId || parsed.operator_id);
161
+ const displayName = (parsed.displayName || parsed.display_name || parsed.name).trim();
162
+ const rawSlug = parsed.slug ? parsed.slug.trim() : displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
163
+ const slug = rawSlug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
127
164
  const body = {
128
- operator_id: parsed.operatorId,
129
- slug: parsed.slug,
130
- display_name: parsed.displayName,
165
+ operator_id: operatorId,
166
+ slug: slug || "facility-" + Date.now(),
167
+ display_name: displayName,
131
168
  region: parsed.region,
132
169
  zone: parsed.zone ?? null,
133
170
  };
@@ -137,10 +174,14 @@ export const facilityCreateCapability = defineStaffMcpCapability({
137
174
  },
138
175
  });
139
176
  export const facilityUpdateInput = z.object({
140
- facilityId: z.string(),
177
+ facilityId: z.string().optional(),
178
+ facility_id: z.string().optional(),
141
179
  displayName: z.string().min(1).max(160).optional(),
180
+ display_name: z.string().min(1).max(160).optional(),
181
+ name: z.string().min(1).max(160).optional(),
142
182
  region: z.string().min(1).max(120).optional(),
143
183
  zone: z.string().max(120).optional(),
184
+ version: z.number().int().positive().optional(),
144
185
  confirm: z.boolean().optional(),
145
186
  });
146
187
  export const facilityUpdateCapability = defineStaffMcpCapability({
@@ -153,15 +194,22 @@ export const facilityUpdateCapability = defineStaffMcpCapability({
153
194
  input: facilityUpdateInput,
154
195
  execute: async (ctx, input) => {
155
196
  const parsed = facilityUpdateInput.parse(input);
156
- // "" clears zone; omitting it leaves zone unchanged (mirrors the dashboard PATCH).
157
- const body = mapBody(parsed, {
158
- displayName: "display_name",
159
- region: "region",
160
- zone: "zone",
161
- });
197
+ const facilityId = parsed.facilityId || parsed.facility_id;
198
+ if (!facilityId) {
199
+ throw new Error("facilityId is required");
200
+ }
201
+ const displayName = parsed.displayName || parsed.display_name || parsed.name;
202
+ const body = {
203
+ ...(displayName ? { display_name: displayName } : {}),
204
+ ...mapBody(parsed, {
205
+ region: "region",
206
+ zone: "zone",
207
+ }),
208
+ version: await resolveVersion(ctx, `/provisioning/facilities/${seg(facilityId)}`, parsed.version),
209
+ };
162
210
  if (!ctx.confirmed)
163
211
  return preview("facility.update", body);
164
- return ctx.fetch(`/provisioning/facilities/${seg(parsed.facilityId)}`, {
212
+ return ctx.fetch(`/provisioning/facilities/${seg(facilityId)}`, {
165
213
  method: "PATCH",
166
214
  body,
167
215
  });
@@ -266,7 +314,18 @@ export const nodeTestTriggerCapability = defineStaffMcpCapability({
266
314
  };
267
315
  if (!ctx.confirmed)
268
316
  return preview("node.test-trigger", body);
269
- return ctx.fetch("/jobs", { method: "POST", body });
317
+ try {
318
+ return await ctx.fetch("/jobs", { method: "POST", body });
319
+ }
320
+ catch (err) {
321
+ const errorObj = err;
322
+ if (errorObj?.status === 409 || errorObj?.message?.includes("409")) {
323
+ const detail = errorObj.detail ||
324
+ "Node is busy: a test or benchmark job is already running on this node.";
325
+ throw new Error(detail);
326
+ }
327
+ throw err;
328
+ }
270
329
  },
271
330
  });
272
331
  export const nodeTestResultInput = z.object({
@@ -304,6 +363,8 @@ export const nodeBenchmarkCapability = defineStaffMcpCapability({
304
363
  export const nodeDeployInput = z.object({
305
364
  nodeId: z.string(),
306
365
  targetTenantId: z.string(),
366
+ commerceReservationId: z.string().optional(),
367
+ commerce_reservation_id: z.string().optional(),
307
368
  targetAuthUserId: z.string().optional(),
308
369
  networkMode: z.enum(["public", "private"]).optional(),
309
370
  notes: z.string().max(2000).optional(),
@@ -319,8 +380,10 @@ export const nodeDeployCapability = defineStaffMcpCapability({
319
380
  input: nodeDeployInput,
320
381
  execute: async (ctx, input) => {
321
382
  const parsed = nodeDeployInput.parse(input);
383
+ const reservationId = parsed.commerceReservationId ?? parsed.commerce_reservation_id;
322
384
  const body = {
323
385
  target_tenant_id: parsed.targetTenantId,
386
+ ...(reservationId ? { commerce_reservation_id: reservationId } : {}),
324
387
  ...mapBody(parsed, {
325
388
  targetAuthUserId: "target_auth_user_id",
326
389
  networkMode: "network_mode",
@@ -419,6 +482,41 @@ export const nodeAdminKeyCapability = defineStaffMcpCapability({
419
482
  });
420
483
  },
421
484
  });
485
+ export const nodeOperatorTransferInput = z.object({
486
+ nodeId: z.string(),
487
+ operatorId: z.string(),
488
+ operatorSlug: z.string(),
489
+ facilityId: z.string().optional(),
490
+ reason: z.string().optional(),
491
+ confirm: z.boolean().optional(),
492
+ });
493
+ export const nodeOperatorTransferCapability = defineStaffMcpCapability({
494
+ id: "node.operator-transfer",
495
+ domain: "fleet",
496
+ roles: ["admin"],
497
+ description: "Internal staff only: transfer a node's ownership to a different operator/facility in Orchestrator. Requires confirm: true.",
498
+ mutation: "preview-confirm",
499
+ http: { method: "POST", path: "/internal/nodes/{nodeId}/transfer" },
500
+ input: nodeOperatorTransferInput,
501
+ execute: async (ctx, input) => {
502
+ const parsed = nodeOperatorTransferInput.parse(input);
503
+ const body = {
504
+ operator_id: parsed.operatorId,
505
+ operator_slug: parsed.operatorSlug,
506
+ ...mapBody(parsed, {
507
+ facilityId: "facility_id",
508
+ reason: "reason",
509
+ }),
510
+ };
511
+ if (!ctx.confirmed) {
512
+ return preview("node.operator-transfer", { nodeId: parsed.nodeId, ...body });
513
+ }
514
+ return ctx.fetch(`/internal/nodes/${seg(parsed.nodeId)}/transfer`, {
515
+ method: "POST",
516
+ body,
517
+ });
518
+ },
519
+ });
422
520
  export const staffFleetCapabilities = [
423
521
  enrollmentTokenRevokeCapability,
424
522
  operatorCreateCapability,
@@ -436,4 +534,5 @@ export const staffFleetCapabilities = [
436
534
  nodeRebootCapability,
437
535
  nodeHardResetCapability,
438
536
  nodeAdminKeyCapability,
537
+ nodeOperatorTransferCapability,
439
538
  ];
@@ -16,3 +16,32 @@ export declare function seg(value: string): string;
16
16
  * `WebApiError`, say), so read the status structurally.
17
17
  */
18
18
  export declare function statusOf(error: unknown): number | undefined;
19
+ /**
20
+ * Read a resource's current optimistic-concurrency version.
21
+ *
22
+ * Commerce rejects a PATCH or lifecycle action whose `version` is absent or
23
+ * non-positive ("missing version" / "version must be positive"), but no staff
24
+ * caller has that number to hand. Read it back when it was not supplied, and
25
+ * only once confirmed so a preview stays a pure dry run.
26
+ */
27
+ export declare function resolveVersion(ctx: {
28
+ fetch: (path: string, init: {
29
+ method: string;
30
+ }) => Promise<unknown>;
31
+ confirmed?: boolean;
32
+ }, path: string, provided?: number): Promise<number>;
33
+ /**
34
+ * Require a commerce identifier, named by this tool's own parameter.
35
+ *
36
+ * A malformed UUID never reaches a commerce handler's validation: Go fails the
37
+ * JSON decode first and answers with a bare "invalid request body" that names
38
+ * no field at all.
39
+ */
40
+ export declare function requireUuid(value: string | undefined, field: string): string;
41
+ /**
42
+ * The human-readable text a transport failure carries, wherever it put it.
43
+ *
44
+ * Mirrors how the MCP surface renders an error so a capability can rewrite
45
+ * that same string before it reaches the caller.
46
+ */
47
+ export declare function errorTextOf(error: unknown): string;
@@ -32,3 +32,67 @@ export function statusOf(error) {
32
32
  }
33
33
  return undefined;
34
34
  }
35
+ /**
36
+ * Read a resource's current optimistic-concurrency version.
37
+ *
38
+ * Commerce rejects a PATCH or lifecycle action whose `version` is absent or
39
+ * non-positive ("missing version" / "version must be positive"), but no staff
40
+ * caller has that number to hand. Read it back when it was not supplied, and
41
+ * only once confirmed so a preview stays a pure dry run.
42
+ */
43
+ export async function resolveVersion(ctx, path, provided) {
44
+ if (provided && provided > 0)
45
+ return provided;
46
+ if (!ctx.confirmed)
47
+ return 1;
48
+ try {
49
+ const current = (await ctx.fetch(path, { method: "GET" }));
50
+ if (typeof current?.version === "number" && current.version > 0)
51
+ return current.version;
52
+ }
53
+ catch {
54
+ // Fall through: send 1 and let commerce report the conflict, which names
55
+ // the expected version, rather than failing on a lookup the caller can't see.
56
+ }
57
+ return 1;
58
+ }
59
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
60
+ /**
61
+ * Require a commerce identifier, named by this tool's own parameter.
62
+ *
63
+ * A malformed UUID never reaches a commerce handler's validation: Go fails the
64
+ * JSON decode first and answers with a bare "invalid request body" that names
65
+ * no field at all.
66
+ */
67
+ export function requireUuid(value, field) {
68
+ const trimmed = (value ?? "").trim();
69
+ if (!trimmed)
70
+ throw new Error(`${field} is required.`);
71
+ if (!UUID_PATTERN.test(trimmed)) {
72
+ throw new Error(`${field} must be a UUID; received ${JSON.stringify(trimmed)}.`);
73
+ }
74
+ return trimmed;
75
+ }
76
+ /**
77
+ * The human-readable text a transport failure carries, wherever it put it.
78
+ *
79
+ * Mirrors how the MCP surface renders an error so a capability can rewrite
80
+ * that same string before it reaches the caller.
81
+ */
82
+ export function errorTextOf(error) {
83
+ if (error && typeof error === "object" && "detail" in error) {
84
+ const detail = error.detail;
85
+ if (typeof detail === "string" && detail)
86
+ return detail;
87
+ if (detail && typeof detail === "object") {
88
+ for (const key of ["summary", "message", "error", "reason"]) {
89
+ const value = detail[key];
90
+ if (typeof value === "string" && value)
91
+ return value;
92
+ }
93
+ }
94
+ }
95
+ if (error instanceof Error)
96
+ return error.message;
97
+ return String(error);
98
+ }
@@ -8,8 +8,8 @@ export { createListingCapability, listingCreateInput, listCatalogTermsCapability
8
8
  export { userCapabilities, usersQueryCapability } from "./capabilities/users.ts";
9
9
  export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.ts";
10
10
  export { logsLatestCapability, logsTailCapability, observabilityCapabilities, telemetryLatestCapability, telemetryTailCapability, } from "./capabilities/observability.ts";
11
- export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.ts";
12
- export { bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, inventoryUpdateCapability, inventoryUpdateInput, reservationCancelCapability, reservationCancelInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.ts";
11
+ export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeOperatorTransferCapability, nodeOperatorTransferInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.ts";
12
+ export { bidShowCapability, bidShowInput, bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, billingInvoicesListCapability, billingInvoicesListInput, inventoryDelistCapability, inventoryDelistInput, inventoryUpdateCapability, inventoryUpdateInput, reservationActivateCapability, reservationActivateInput, reservationCancelCapability, reservationCancelInput, reservationCompleteCapability, reservationCompleteInput, reservationDraftCreateCapability, reservationDraftCreateInput, reservationListCapability, reservationListInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationShowCapability, reservationShowInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, reservationUpdateCapability, reservationUpdateInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.ts";
13
13
  export { sshKeyAddCapability, sshKeyAddInput, sshKeyPushCapability, sshKeyPushInput, sshKeyRevokeCapability, sshKeyRevokeInput, staffAccessCapabilities, } from "./capabilities/staff-access.ts";
14
14
  export { CLIENTS, parseRole, roleAtLeast, ROLES, type ClientKind, type Role } from "./role.ts";
15
15
  export { bindHardwareFields, HARDWARE_CATALOG_PATH, HARDWARE_KINDS, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, looksLikeSpecDump, normalizeCatalogQuery, parseHardwareCatalog, projectCatalogTerms, proposeListingPayload, resolveCatalogTerm, UNRESOLVED_CATALOG_HINT, type CatalogBindResult, type CatalogTerm, type HardwareCatalog, type HardwareKind, type ResolvedCatalogTerm, } from "./spec-catalog.ts";
@@ -8,8 +8,8 @@ export { createListingCapability, listingCreateInput, listCatalogTermsCapability
8
8
  export { userCapabilities, usersQueryCapability } from "./capabilities/users.js";
9
9
  export { kubernetesCapabilities, slurmCapabilities } from "./capabilities/clusters.js";
10
10
  export { logsLatestCapability, logsTailCapability, observabilityCapabilities, telemetryLatestCapability, telemetryTailCapability, } from "./capabilities/observability.js";
11
- export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.js";
12
- export { bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, inventoryUpdateCapability, inventoryUpdateInput, reservationCancelCapability, reservationCancelInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.js";
11
+ export { enrollmentTokenRevokeCapability, enrollmentTokenRevokeInput, facilityCreateCapability, facilityCreateInput, facilityUpdateCapability, facilityUpdateInput, nodeAdminKeyCapability, nodeAdminKeyInput, nodeBenchmarkCapability, nodeBenchmarkInput, nodeDeployCapability, nodeDeployInput, nodeDetailsUpdateCapability, nodeDetailsUpdateInput, nodeHardResetCapability, nodeHardResetInput, nodeOperatorTransferCapability, nodeOperatorTransferInput, nodeRebootCapability, nodeRebootInput, nodeShowCapability, nodeShowInput, nodeTestResultCapability, nodeTestResultInput, nodeTestTriggerCapability, nodeTestTriggerInput, nodeUpdateCapability, nodeUpdateInput, operatorCreateCapability, operatorCreateInput, operatorDeleteCapability, operatorDeleteInput, operatorUpdateCapability, operatorUpdateInput, staffFleetCapabilities, } from "./capabilities/staff-fleet.js";
12
+ export { bidShowCapability, bidShowInput, bidAcceptCapability, bidAcceptInput, bidRejectCapability, bidRejectInput, billingInvoicesListCapability, billingInvoicesListInput, inventoryDelistCapability, inventoryDelistInput, inventoryUpdateCapability, inventoryUpdateInput, reservationActivateCapability, reservationActivateInput, reservationCancelCapability, reservationCancelInput, reservationCompleteCapability, reservationCompleteInput, reservationDraftCreateCapability, reservationDraftCreateInput, reservationListCapability, reservationListInput, reservationMachinesListCapability, reservationMachinesListInput, reservationPublishCapability, reservationPublishInput, reservationShowCapability, reservationShowInput, reservationSshKeyAddCapability, reservationSshKeyAddInput, reservationSshKeyRemoveCapability, reservationSshKeyRemoveInput, reservationTransferCapability, reservationTransferInput, reservationUpdateCapability, reservationUpdateInput, staffCommerceCapabilities, tenantCreditProfileShowCapability, tenantCreditProfileShowInput, } from "./capabilities/staff-commerce.js";
13
13
  export { sshKeyAddCapability, sshKeyAddInput, sshKeyPushCapability, sshKeyPushInput, sshKeyRevokeCapability, sshKeyRevokeInput, staffAccessCapabilities, } from "./capabilities/staff-access.js";
14
14
  export { CLIENTS, parseRole, roleAtLeast, ROLES } from "./role.js";
15
15
  export { bindHardwareFields, HARDWARE_CATALOG_PATH, HARDWARE_KINDS, HARDWARE_LISTING_FIELDS, HARDWARE_LISTING_FILTERS, loadHardwareCatalog, looksLikeSpecDump, normalizeCatalogQuery, parseHardwareCatalog, projectCatalogTerms, proposeListingPayload, resolveCatalogTerm, UNRESOLVED_CATALOG_HINT, } from "./spec-catalog.js";