@ornncompute/cli 1.0.67 → 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.
Files changed (39) hide show
  1. package/README.md +35 -27
  2. package/package.json +1 -1
  3. package/src/admin-capacity-reservations.mjs +2 -12
  4. package/src/admin-command-handlers.mjs +0 -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 +2 -36
  9. package/src/admin-node-lifecycle.mjs +25 -43
  10. package/src/admin-shell.mjs +39 -32
  11. package/src/catalog-shell.mjs +4 -18
  12. package/src/external-bucket-commands.mjs +5 -73
  13. package/src/interaction-runtime.mjs +2 -37
  14. package/src/kubernetes-commands.mjs +11 -102
  15. package/src/launch-mode-handlers.mjs +0 -1
  16. package/src/mount-commands.mjs +25 -131
  17. package/src/storage-commands.mjs +2 -87
  18. package/src/vm-commands.mjs +11 -44
  19. package/vendor/capabilities/capabilities/admin-capacity-reservations.js +12 -5
  20. package/vendor/capabilities/capabilities/admin-enrollment.js +18 -17
  21. package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +3 -101
  22. package/vendor/capabilities/capabilities/admin.js +3 -3
  23. package/vendor/capabilities/capabilities/clusters.js +3 -3
  24. package/vendor/capabilities/capabilities/commerce-market.js +9 -9
  25. package/vendor/capabilities/capabilities/compute.js +7 -7
  26. package/vendor/capabilities/capabilities/credits.js +7 -2
  27. package/vendor/capabilities/capabilities/external-buckets.js +4 -4
  28. package/vendor/capabilities/capabilities/fleet.js +109 -29
  29. package/vendor/capabilities/capabilities/listings.js +44 -16
  30. package/vendor/capabilities/capabilities/locality.js +42 -0
  31. package/vendor/capabilities/capabilities/market-shared.js +1 -1
  32. package/vendor/capabilities/capabilities/markets.js +14 -14
  33. package/vendor/capabilities/capabilities/mounts.js +4 -4
  34. package/vendor/capabilities/capabilities/native-listing.js +51 -15
  35. package/vendor/capabilities/capabilities/networks.js +5 -5
  36. package/vendor/capabilities/capabilities/observability.js +49 -14
  37. package/vendor/capabilities/capabilities/tokens.js +5 -9
  38. package/vendor/capabilities/capabilities/users.js +8 -10
  39. package/vendor/capabilities/capabilities/volumes.js +5 -5
@@ -303,7 +303,6 @@ function failedOperationRecoveryCommand(value, interaction) {
303
303
  `--mode ${accessMode}`,
304
304
  `--network ${networkMode}`,
305
305
  accessMode !== sourceMode ? "--erase-local-data" : null,
306
- "--yes",
307
306
  `--idempotency-key ${requestId}`,
308
307
  ]
309
308
  .filter(Boolean)
@@ -317,7 +316,6 @@ function failedOperationRecoveryCommand(value, interaction) {
317
316
  `ornn machines ${command}`,
318
317
  shellQuote(id),
319
318
  command === "reset" ? "--erase-local-data" : null,
320
- "--yes",
321
319
  `--idempotency-key ${requestId}`,
322
320
  ]
323
321
  .filter(Boolean)
@@ -392,7 +390,7 @@ function positional(input, expected, usage, interaction) {
392
390
  return values;
393
391
  }
394
392
 
395
- function machineMutationPreflight(input, interaction, { requireYes = true } = {}) {
393
+ function machineMutationPreflight(input, interaction) {
396
394
  if (!interaction.machine) return;
397
395
  if (!text(input.idempotencyKey)) {
398
396
  interaction.usage("--idempotency-key is required for automation.", {
@@ -400,12 +398,6 @@ function machineMutationPreflight(input, interaction, { requireYes = true } = {}
400
398
  field: "idempotencyKey",
401
399
  });
402
400
  }
403
- if (requireYes && input.yes !== true) {
404
- interaction.usage("--yes is required for automation after reviewing the exact intent.", {
405
- code: "consent_required",
406
- field: "yes",
407
- });
408
- }
409
401
  }
410
402
 
411
403
  async function selectedOrganization(context, interaction, expectedId) {
@@ -415,8 +407,8 @@ async function selectedOrganization(context, interaction, expectedId) {
415
407
  const selected = record(record(result.data).selected_organization);
416
408
  const id = text(selected.id);
417
409
  const name = text(selected.name);
418
- if (!id || !name || selected.approved !== true) {
419
- interaction.usage("Your current organization must be approved before launch.", {
410
+ if (!id || !name) {
411
+ interaction.usage("Join an organization before launching compute.", {
420
412
  code: "organization_required",
421
413
  field: "organization",
422
414
  });
@@ -821,12 +813,6 @@ function launchInputPreflight(input, interaction) {
821
813
  field: "label",
822
814
  });
823
815
  }
824
- if (input.yes !== true) {
825
- interaction.usage("--yes is required for automation after reviewing the launch intent.", {
826
- code: "consent_required",
827
- field: "yes",
828
- });
829
- }
830
816
  if (!text(input.idempotencyKey)) {
831
817
  interaction.usage("--idempotency-key is required for automation.", {
832
818
  code: "idempotency_key_required",
@@ -1199,7 +1185,7 @@ export const vmLaunchModeHandler = {
1199
1185
  resourceId(key),
1200
1186
  "--idempotency-key",
1201
1187
  idempotencyKey,
1202
- ...(interaction.machine ? ["--yes", "--no-wait", "--format", "json"] : []),
1188
+ ...(interaction.machine ? ["--no-wait", "--format", "json"] : []),
1203
1189
  ].join(" ");
1204
1190
  return uncertainMutationFailure(interaction, error, {
1205
1191
  data: {
@@ -1559,7 +1545,6 @@ function durableWorkspaceAllocation(reservation, interaction) {
1559
1545
  function workspaceStepContext(context, interaction, signal) {
1560
1546
  const stepInteraction = Object.create(interaction);
1561
1547
  stepInteraction.review = async () => true;
1562
- stepInteraction.options = { ...interaction.options, yes: true };
1563
1548
  return {
1564
1549
  ...context,
1565
1550
  fetch: (path, init = {}) =>
@@ -1681,7 +1666,7 @@ function workspaceResumeCommand(input, goal, facts, id, interaction, localResume
1681
1666
  if (goal.username) parts.push("--username", goal.username);
1682
1667
  if (input.noWait) parts.push("--no-wait");
1683
1668
  if (input.wait) parts.push("--wait", "--timeout", text(input.timeout));
1684
- if (interaction.machine) parts.push("--yes", "--format", interaction.format);
1669
+ if (interaction.machine) parts.push("--format", interaction.format);
1685
1670
  return parts.map(shellQuote).join(" ");
1686
1671
  }
1687
1672
 
@@ -1952,7 +1937,6 @@ async function launchDurableBareMetalWorkspace({
1952
1937
  $positionals: [reservationId],
1953
1938
  key: resourceId(key),
1954
1939
  idempotencyKey: `${id}:ssh-key`,
1955
- yes: true,
1956
1940
  }
1957
1941
  );
1958
1942
  states.ssh_key_assignment = {
@@ -1988,7 +1972,6 @@ async function launchDurableBareMetalWorkspace({
1988
1972
  idempotencyKey: `${id}:volume-mount`,
1989
1973
  noWait: true,
1990
1974
  volume: facts.volume.id,
1991
- yes: true,
1992
1975
  }
1993
1976
  );
1994
1977
  facts.volumeMount = mount.data.volume_mount;
@@ -2464,7 +2447,7 @@ export const bareMetalLaunchModeHandler = {
2464
2447
  resourceId(key),
2465
2448
  "--idempotency-key",
2466
2449
  idempotencyKey,
2467
- ...(interaction.machine ? ["--yes", "--no-wait", "--format", "json"] : []),
2450
+ ...(interaction.machine ? ["--no-wait", "--format", "json"] : []),
2468
2451
  ].join(" ");
2469
2452
  return uncertainMutationFailure(interaction, error, {
2470
2453
  data: {
@@ -3002,24 +2985,12 @@ function validateWaitInput(input, interaction, { waitCommand = false } = {}) {
3002
2985
  function requireMachineMutationSafety(input, interaction, { destructive = false } = {}) {
3003
2986
  validateWaitInput(input, interaction);
3004
2987
  if (destructive && input.eraseLocalData !== true) {
3005
- interaction.usage(
3006
- "machines reset requires --erase-local-data; --yes cannot infer destructive scope.",
3007
- {
3008
- code: "erase_local_data_required",
3009
- field: "eraseLocalData",
3010
- }
3011
- );
2988
+ interaction.usage("machines reset requires --erase-local-data.", {
2989
+ code: "erase_local_data_required",
2990
+ field: "eraseLocalData",
2991
+ });
3012
2992
  }
3013
2993
  if (interaction.machine || !interaction.eligibleHuman) {
3014
- if (input.yes !== true) {
3015
- interaction.usage(
3016
- "--yes is required outside an eligible terminal after reviewing the exact intent.",
3017
- {
3018
- code: "consent_required",
3019
- field: "yes",
3020
- }
3021
- );
3022
- }
3023
2994
  if (!text(input.idempotencyKey)) {
3024
2995
  interaction.usage("--idempotency-key is required outside an eligible terminal.", {
3025
2996
  code: "idempotency_key_required",
@@ -3452,7 +3423,7 @@ async function switchMachine(input, context, interaction) {
3452
3423
  const erasesLocalData = targetMode !== currentMode;
3453
3424
  if (erasesLocalData && input.eraseLocalData !== true) {
3454
3425
  interaction.usage(
3455
- "Changing between VM and Bare Metal permanently erases source-local data and requires --erase-local-data; --yes cannot infer destructive scope.",
3426
+ "Changing between VM and Bare Metal permanently erases source-local data and requires --erase-local-data; cannot infer destructive scope.",
3456
3427
  { code: "erase_local_data_required", field: "eraseLocalData" }
3457
3428
  );
3458
3429
  }
@@ -3561,7 +3532,6 @@ async function switchMachine(input, context, interaction) {
3561
3532
  requestedMode ? `--mode ${requestedMode}` : null,
3562
3533
  requestedNetwork ? `--network ${requestedNetwork}` : null,
3563
3534
  erasesLocalData ? "--erase-local-data" : null,
3564
- "--yes",
3565
3535
  "--no-wait",
3566
3536
  `--idempotency-key ${shellQuote(idempotencyKey)}`,
3567
3537
  ]
@@ -3663,7 +3633,6 @@ async function switchMachine(input, context, interaction) {
3663
3633
  `--mode ${targetMode}`,
3664
3634
  `--network ${targetNetwork}`,
3665
3635
  erasesLocalData ? "--erase-local-data" : null,
3666
- "--yes",
3667
3636
  `--idempotency-key ${shellQuote(interaction.idempotencyKey())}`,
3668
3637
  ]
3669
3638
  .filter(Boolean)
@@ -3781,7 +3750,6 @@ async function maintainMachine(command, input, context, interaction) {
3781
3750
  `ornn machines ${command}`,
3782
3751
  shellQuote(machineId(machine)),
3783
3752
  destructive ? "--erase-local-data" : null,
3784
- "--yes",
3785
3753
  "--no-wait",
3786
3754
  `--idempotency-key ${shellQuote(idempotencyKey)}`,
3787
3755
  ]
@@ -3874,7 +3842,6 @@ async function maintainMachine(command, input, context, interaction) {
3874
3842
  `ornn machines ${command}`,
3875
3843
  shellQuote(machineId(waitOutcome.value)),
3876
3844
  destructive ? "--erase-local-data" : null,
3877
- "--yes",
3878
3845
  `--idempotency-key ${shellQuote(interaction.idempotencyKey())}`,
3879
3846
  ]
3880
3847
  .filter(Boolean)
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { defineCapability, preview } from "../capability.js";
3
+ import { ensureFacility, locateRegion, unplacedLocation } from "./locality.js";
3
4
  import { nativeListingCreateInput } from "./native-listing.js";
4
5
  import { idempotencyHeaders, microUsd } from "./market-shared.js";
5
6
  function adminHelp(command, outcome, human, automation, review) {
@@ -18,22 +19,28 @@ export const adminCapacityOffersCreateCapability = defineCapability({
18
19
  id: "listing.create",
19
20
  domain: "listings",
20
21
  roles: ["admin"],
21
- description: "Create native capacity with explicit organization/facility IDs, hardware, availability and advertised terms. Returns {listing, terms}. Requires an idempotencyKey reused on retries and confirm: true.",
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.",
22
23
  mutation: "preview-confirm",
23
24
  http: { method: "POST", path: "/commerce/v2/listings" },
24
25
  input: nativeListingCreateInput,
25
26
  slack: false,
26
27
  cli: {
27
28
  command: ["admin", "capacity-offers", "create"],
28
- vnext: adminHelp(["admin", "capacity-offers", "create"], "Create native capacity and its advertised financial terms.", "ornn admin capacity-offers create --gpu-slug GPU --capacity-node-count COUNT --gpus-per-node COUNT --facility-id UUID --provisioning-type bare-metal|virtual-machine --state draft|available --terms JSON --idempotency-key KEY [--organization-id UUID] [--available-at TIME --available-until TIME] [--days-to-rfs DAYS]", "Requires an eligible human terminal; reuse --idempotency-key on retries.", "Review the native parent fields and each financial term before default-no consent."),
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."),
29
30
  },
30
31
  execute: async (ctx, input) => {
31
- const { confirm: _confirm, idempotencyKey, ...body } = nativeListingCreateInput.parse(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));
32
36
  if (!ctx.confirmed)
33
- return preview("listing.create", body);
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;
34
41
  return ctx.fetch("/commerce/v2/listings", {
35
42
  method: "POST",
36
- body,
43
+ body: { ...body, facilityId },
37
44
  headers: idempotencyHeaders(ctx, idempotencyKey),
38
45
  });
39
46
  },
@@ -44,11 +44,13 @@ export const adminEnrollmentTokensListCapability = {
44
44
  };
45
45
  },
46
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."));
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] []", "Unsupported: token creation requires an eligible human terminal.", ["canonical supplier organization UUID"], "Review the supplier, expiry, and secret destination before minting the token."));
48
48
  const tokenRevokeInput = z.object({
49
- token_id: UUID,
49
+ tokenId: z.string().min(1),
50
+ token_id: UUID.optional(),
50
51
  expected_created_at: nonEmpty.optional(),
51
52
  idempotency_key: nonEmpty.optional(),
53
+ confirm: z.boolean().optional(),
52
54
  });
53
55
  function rowsFromTokenList(payload) {
54
56
  if (Array.isArray(payload))
@@ -104,22 +106,23 @@ async function readExactToken(ctx, tokenId, expectedCreatedAt) {
104
106
  return { disposition: "apply", target };
105
107
  }
106
108
  export const adminEnrollmentTokenRevokeCapability = defineCapability({
107
- id: "tokens.revoke",
109
+ id: "enrollment.token.revoke",
108
110
  domain: "fleet",
109
111
  roles: ["admin"],
110
- description: "Revoke one exact enrollment-token generation after reconciling its durable goal.",
112
+ description: "Internal staff only: revoke one exact enrollment-token generation after reconciling its durable goal.",
111
113
  mutation: "preview-confirm",
112
- http: { method: "DELETE", path: "/enrollment-tokens/{id}" },
114
+ http: { method: "DELETE", path: "/enrollment-tokens/{tokenId}" },
113
115
  cli: {
114
116
  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."),
117
+ vnext: adminHelp(["admin", "enrollment-tokens", "revoke"], "Revoke one exact enrollment-token generation.", "ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] []", "ornn admin enrollment-tokens revoke <token-id> --expected-created-at TIME --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
118
  },
117
- mcp: false,
118
119
  slack: false,
120
+ mcp: { name: "enrollment_token_revoke" },
119
121
  input: tokenRevokeInput,
120
122
  execute: async (ctx, input) => {
121
123
  const parsed = tokenRevokeInput.parse(input);
122
- const current = await readExactToken(ctx, parsed.token_id, parsed.expected_created_at);
124
+ const tokenId = parsed.tokenId ?? parsed.token_id;
125
+ const current = await readExactToken(ctx, tokenId, parsed.expected_created_at);
123
126
  if (!ctx.confirmed)
124
127
  return current;
125
128
  if (current.disposition === "conflict") {
@@ -127,23 +130,21 @@ export const adminEnrollmentTokenRevokeCapability = defineCapability({
127
130
  }
128
131
  if (current.disposition === "satisfied") {
129
132
  return {
130
- id: parsed.token_id,
133
+ id: tokenId,
131
134
  created_at: parsed.expected_created_at ?? current.target?.created_at ?? null,
132
135
  status: "revoked",
133
136
  reconciled: true,
134
137
  };
135
138
  }
136
139
  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.`);
140
+ throw codedError("token_identity_unavailable", `Enrollment token ${tokenId} has no stable creation identity and cannot be revoked safely.`);
138
141
  }
139
- const revoked = (await ctx.fetch(`/enrollment-tokens/${encodeURIComponent(parsed.token_id)}`, {
142
+ const revoked = (await ctx.fetch(`/enrollment-tokens/${encodeURIComponent(tokenId)}`, {
140
143
  method: "DELETE",
141
- ...(parsed.idempotency_key
142
- ? { headers: { "Idempotency-Key": parsed.idempotency_key } }
143
- : {}),
144
+ ...(parsed.idempotency_key ? { headers: { "Idempotency-Key": parsed.idempotency_key } } : {}),
144
145
  }));
145
146
  return {
146
- id: parsed.token_id,
147
+ id: tokenId,
147
148
  created_at: current.target.created_at,
148
149
  organization_id: current.target.organization_id,
149
150
  expires_at: current.target.expires_at,
@@ -190,7 +191,7 @@ export const adminNodeEnrollCapability = defineCapability({
190
191
  http: { method: "POST", path: "/remote" },
191
192
  cli: {
192
193
  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
+ 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] []", "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
  },
195
196
  mcp: false,
196
197
  slack: false,
@@ -291,7 +292,7 @@ export const adminNodeRotateKeyCapability = defineCapability({
291
292
  http: { method: "POST", path: "/enrollment/admin-ssh-key" },
292
293
  cli: {
293
294
  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
+ 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] []", "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
  },
296
297
  mcp: false,
297
298
  slack: false,
@@ -18,16 +18,10 @@ const rebootInput = z.object({
18
18
  const hardResetInput = rebootInput.extend({
19
19
  erase_local_data: z.literal(true),
20
20
  });
21
- const terminationInput = z.object({
22
- ...commonInput,
23
- delete_node_record: z.literal(true),
24
- skip_cleanup: z.boolean().optional(),
25
- });
26
21
  const assignmentInput = z.object({
27
22
  ...commonInput,
28
23
  reservation_id: z.string().trim().min(1),
29
24
  });
30
- const managementInput = z.object(commonInput);
31
25
  function adminLifecycleHelp(command, outcome, human, automation, review) {
32
26
  return {
33
27
  audience: "admin",
@@ -83,7 +77,7 @@ export const adminNodeRebootCapability = defineCapability({
83
77
  http: { method: "POST", path: "/nodes/{id}/reboot" },
84
78
  cli: {
85
79
  command: ["admin", "nodes", "reboot"],
86
- vnext: adminLifecycleHelp(["admin", "nodes", "reboot"], "Reboot one exact live machine while preserving local data and access configuration.", "ornn admin nodes reboot <node-ref> [--yes] [--no-wait] [--timeout SECONDS]", "Unsupported: reboot requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, and preserved access."),
80
+ vnext: adminLifecycleHelp(["admin", "nodes", "reboot"], "Reboot one exact live machine while preserving local data and access configuration.", "ornn admin nodes reboot <node-ref> [] [--no-wait] [--timeout SECONDS]", "Unsupported: reboot requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, and preserved access."),
87
81
  },
88
82
  mcp: false,
89
83
  slack: false,
@@ -99,102 +93,13 @@ export const adminNodeHardResetCapability = defineCapability({
99
93
  http: { method: "POST", path: "/nodes/{id}/reboot" },
100
94
  cli: {
101
95
  command: ["admin", "nodes", "hard-reset"],
102
- vnext: adminLifecycleHelp(["admin", "nodes", "hard-reset"], "Erase local tenant data and hard reset one exact live machine.", "ornn admin nodes hard-reset <node-ref> --erase-local-data [--yes] [--no-wait] [--timeout SECONDS]", "Unsupported: hard reset requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, erased local data, and restored keys."),
96
+ vnext: adminLifecycleHelp(["admin", "nodes", "hard-reset"], "Erase local tenant data and hard reset one exact live machine.", "ornn admin nodes hard-reset <node-ref> --erase-local-data [] [--no-wait] [--timeout SECONDS]", "Unsupported: hard reset requires an eligible human terminal.", "Node and live-machine identities, supplier, facility, capacity assignment, erased local data, and restored keys."),
103
97
  },
104
98
  mcp: false,
105
99
  slack: false,
106
100
  input: hardResetInput,
107
101
  execute: (ctx, input) => reboot(ctx, input, "hard_reset"),
108
102
  });
109
- export const adminNodeSuspendManagementCapability = defineCapability({
110
- id: "nodes.suspend_management",
111
- domain: "fleet",
112
- roles: ["admin"],
113
- description: "Suspend Ornn management while leaving the exact host powered on.",
114
- mutation: "preview-confirm",
115
- http: { method: "POST", path: "/nodes/{id}/off-grid" },
116
- cli: {
117
- command: ["admin", "nodes", "suspend-management"],
118
- vnext: adminLifecycleHelp(["admin", "nodes", "suspend-management"], "Remove Ornn management and managed access from one powered-on node.", "ornn admin nodes suspend-management <node-ref> [--reason TEXT] [--yes] [--no-wait]", "ornn admin nodes suspend-management <node-id> --yes --idempotency-key KEY --format json", "Node, supplier, facility, capacity assignment, management removal, and managed-access loss."),
119
- },
120
- mcp: false,
121
- slack: false,
122
- input: managementInput,
123
- execute: async (ctx, input) => {
124
- const parsed = managementInput.parse(input);
125
- const id = nodeId(parsed);
126
- const body = {
127
- ...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
128
- ...(parsed.reason ? { reason: parsed.reason } : {}),
129
- };
130
- if (!ctx.confirmed)
131
- return preview("nodes.suspend_management", { node_id: id, ...body });
132
- return ctx.fetch(`/nodes/${encodeURIComponent(id)}/off-grid`, {
133
- method: "POST",
134
- body,
135
- headers: requestHeaders(parsed.idempotency_key),
136
- });
137
- },
138
- });
139
- export const adminNodeResumeManagementCapability = defineCapability({
140
- id: "nodes.resume_management",
141
- domain: "fleet",
142
- roles: ["admin"],
143
- description: "Resume Ornn management for one exact off-grid node.",
144
- mutation: "preview-confirm",
145
- http: { method: "POST", path: "/nodes/{id}/on-grid" },
146
- cli: {
147
- command: ["admin", "nodes", "resume-management"],
148
- vnext: adminLifecycleHelp(["admin", "nodes", "resume-management"], "Restore Ornn management and managed access on one off-grid node.", "ornn admin nodes resume-management <node-ref> [--yes] [--no-wait]", "ornn admin nodes resume-management <node-id> --yes --idempotency-key KEY --format json", "Node, supplier, facility, capacity assignment, remote re-enrollment, and restored managed access."),
149
- },
150
- mcp: false,
151
- slack: false,
152
- input: managementInput,
153
- execute: async (ctx, input) => {
154
- const parsed = managementInput.parse(input);
155
- const id = nodeId(parsed);
156
- if (!ctx.confirmed)
157
- return preview("nodes.resume_management", { node_id: id });
158
- return ctx.fetch(`/nodes/${encodeURIComponent(id)}/on-grid`, {
159
- method: "POST",
160
- body: {
161
- ...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
162
- },
163
- headers: requestHeaders(parsed.idempotency_key),
164
- });
165
- },
166
- });
167
- export const adminNodeTerminateCapability = defineCapability({
168
- id: "nodes.terminate",
169
- domain: "fleet",
170
- roles: ["admin"],
171
- description: "Delete one exact node record with explicit cleanup scope and consequence review.",
172
- mutation: "preview-confirm",
173
- http: { method: "POST", path: "/nodes/{id}/terminate" },
174
- cli: {
175
- command: ["admin", "nodes", "terminate"],
176
- vnext: adminLifecycleHelp(["admin", "nodes", "terminate"], "End managed access and permanently delete one exact node record.", "ornn admin nodes terminate <node-ref> --delete-node-record [--skip-cleanup] [--reason TEXT] [--yes]", "ornn admin nodes terminate <node-id> --delete-node-record --yes --idempotency-key KEY --format json", "Node, supplier, facility, reservation/capacity, host cleanup, node-record deletion, and access loss."),
177
- },
178
- mcp: false,
179
- slack: false,
180
- input: terminationInput,
181
- execute: async (ctx, input) => {
182
- const parsed = terminationInput.parse(input);
183
- const id = nodeId(parsed);
184
- const body = {
185
- force: parsed.skip_cleanup === true,
186
- ...(parsed.idempotency_key ? { request_id: parsed.idempotency_key } : {}),
187
- ...(parsed.reason ? { reason: parsed.reason } : {}),
188
- };
189
- if (!ctx.confirmed)
190
- return preview("nodes.terminate", { node_id: id, ...body });
191
- return ctx.fetch(`/nodes/${encodeURIComponent(id)}/terminate`, {
192
- method: "POST",
193
- body,
194
- headers: requestHeaders(parsed.idempotency_key),
195
- });
196
- },
197
- });
198
103
  export const adminNodeAssignCapability = defineCapability({
199
104
  id: "nodes.assign",
200
105
  domain: "fleet",
@@ -204,7 +109,7 @@ export const adminNodeAssignCapability = defineCapability({
204
109
  http: { method: "POST", path: "/nodes/{id}/assign" },
205
110
  cli: {
206
111
  command: ["admin", "nodes", "assign"],
207
- vnext: adminLifecycleHelp(["admin", "nodes", "assign"], "Assign one exact node to one exact capacity reservation.", "ornn admin nodes assign <node-ref> --reservation <reservation-id> [--yes] [--no-wait]", "ornn admin nodes assign <node-id> --reservation <reservation-id> --yes --idempotency-key KEY --format json", "Node, supplier, facility, previous and requested capacity assignments, and tenant access change."),
112
+ vnext: adminLifecycleHelp(["admin", "nodes", "assign"], "Assign one exact node to one exact capacity reservation.", "ornn admin nodes assign <node-ref> --reservation <reservation-id> [] [--no-wait]", "ornn admin nodes assign <node-id> --reservation <reservation-id> --idempotency-key KEY --format json", "Node, supplier, facility, previous and requested capacity assignments, and tenant access change."),
208
113
  },
209
114
  mcp: false,
210
115
  slack: false,
@@ -228,8 +133,5 @@ export const adminNodeAssignCapability = defineCapability({
228
133
  export const adminNodeLifecycleCapabilities = [
229
134
  adminNodeRebootCapability,
230
135
  adminNodeHardResetCapability,
231
- adminNodeSuspendManagementCapability,
232
- adminNodeResumeManagementCapability,
233
- adminNodeTerminateCapability,
234
136
  adminNodeAssignCapability,
235
137
  ];
@@ -36,9 +36,9 @@ function withAdminBinding(capability, help) {
36
36
  },
37
37
  };
38
38
  }
39
- export const adminUsersListCapability = withAdminBinding(usersListCapability, adminHelp(["admin", "users", "list"], "List users belonging to organizations in the Authorization directory.", "ornn admin users list [--query QUERY] [--organization UUID] [--limit 25] [--offset OFFSET] [--cursor CURSOR]", "ornn admin users list --query QUERY --limit 25 [--cursor CURSOR] --format json"));
40
- export const adminOrganizationsListCapability = withAdminBinding(organizationListCapability, adminHelp(["admin", "organizations", "list"], "List organizations in the Authorization directory.", "ornn admin organizations list [--query QUERY] [--role consumer|supplier] [--approved true|false] [--ids UUID,...] [--limit 25] [--offset OFFSET] [--cursor CURSOR]", "ornn admin organizations list --query QUERY --limit 25 [--cursor CURSOR] --format json"));
41
- export const adminSuppliersListCapability = withAdminBinding(listSuppliersCapability, adminHelp(["admin", "suppliers", "list"], "List supplier organizations visible to staff.", "ornn admin suppliers list [--limit 50] [--cursor CURSOR]", "ornn admin suppliers list --limit 50 --format json"));
39
+ export const adminUsersListCapability = withAdminBinding(usersListCapability, adminHelp(["admin", "users", "list"], "List users in the Authorization directory; pass next_offset as --offset to continue.", "ornn admin users list [--query QUERY] [--organization UUID] [--limit 25] [--offset OFFSET]", "ornn admin users list --query QUERY --limit 25 [--offset OFFSET] --format json"));
40
+ export const adminOrganizationsListCapability = withAdminBinding(organizationListCapability, adminHelp(["admin", "organizations", "list"], "List organizations in the Authorization directory; pass next_offset as --offset to continue.", "ornn admin organizations list [--query QUERY] [--role consumer|supplier] [--approved true|false] [--ids UUID,...] [--limit 25] [--offset OFFSET]", "ornn admin organizations list --query QUERY --limit 25 [--offset OFFSET] --format json"));
41
+ export const adminSuppliersListCapability = withAdminBinding(listSuppliersCapability, adminHelp(["admin", "suppliers", "list"], "List supplier organizations visible to staff; pass next_offset as --offset to continue.", "ornn admin suppliers list [--limit 50] [--offset OFFSET]", "ornn admin suppliers list --limit 50 --offset OFFSET --format json"));
42
42
  export const adminFacilitiesListCapability = withAdminBinding(listFacilitiesCapability, adminHelp(["admin", "facilities", "list"], "List facilities visible to staff.", "ornn admin facilities list [--supplier UUID] [--limit 50] [--cursor CURSOR]", "ornn admin facilities list --limit 50 --format json"));
43
43
  export const adminNodesListCapability = withAdminBinding(listNodesCapability, adminHelp(["admin", "nodes", "list"], "List enrolled, ghost, and off-grid nodes.", "ornn admin nodes list [--supplier UUID] [--facility FACILITY] [--status STATUS] [--reservation UUID] [--limit 50] [--cursor CURSOR]", "ornn admin nodes list --limit 50 [--cursor CURSOR] --format json"));
44
44
  const nodeInput = z.object({ node_id: z.string().min(1) });
@@ -204,7 +204,7 @@ export const clusterCliCapabilities = [
204
204
  mutation: "preview-confirm",
205
205
  outcome: "Add one currently eligible machine, or rediscover that exact membership goal.",
206
206
  human: "ornn clusters add-node [<cluster-id>] [<machine-id-or-hostname>] [--guided] [--no-wait]",
207
- automation: "ornn clusters add-node <cluster-id> <machine-id> --yes --idempotency-key <key> --format json",
207
+ automation: "ornn clusters add-node <cluster-id> <machine-id> --idempotency-key <key> --format json",
208
208
  acceptedReferences: [
209
209
  "canonical cluster ID",
210
210
  "canonical machine ID",
@@ -221,7 +221,7 @@ export const clusterCliCapabilities = [
221
221
  mutation: "preview-confirm",
222
222
  outcome: "Remove one exact member, or rediscover that exact membership goal.",
223
223
  human: "ornn clusters remove-node [<cluster-id>] [<machine-id-or-hostname>] [--guided] [--no-wait]",
224
- automation: "ornn clusters remove-node <cluster-id> <machine-id> --yes --idempotency-key <key> --format json",
224
+ automation: "ornn clusters remove-node <cluster-id> <machine-id> --idempotency-key <key> --format json",
225
225
  acceptedReferences: [
226
226
  "canonical cluster ID",
227
227
  "canonical machine ID",
@@ -238,7 +238,7 @@ export const clusterCliCapabilities = [
238
238
  mutation: "preview-confirm",
239
239
  outcome: "End cluster lifecycle while reporting partial member failures and continuing reservation charges.",
240
240
  human: "ornn clusters teardown [<cluster-id>] [--guided] [--no-wait]",
241
- automation: "ornn clusters teardown <cluster-id> --yes --idempotency-key <key> --format json",
241
+ automation: "ornn clusters teardown <cluster-id> --idempotency-key <key> --format json",
242
242
  acceptedReferences: ["canonical cluster ID", "invocation-local guided choice"],
243
243
  review: "The cluster and native access end; the capacity reservation and its charges remain.",
244
244
  related: ["clusters show", "reservations show"],
@@ -33,7 +33,7 @@ export const listingsListCapability = defineCapability({
33
33
  id: "listings.list",
34
34
  domain: "listings",
35
35
  roles: ["user"],
36
- description: "List native capacity listings. Primary listings require organization approval. Returns capacityListingId, hardware, capacity and availability; read listings.terms separately for prices and durations.",
36
+ description: "List native capacity listings. Primary listings require organization approval. Returns capacityListingId, hardware, capacity and availability; when nextCursor is present, pass it back as cursor for the next page. Read listings.terms separately for prices and durations.",
37
37
  mutation: "read",
38
38
  http: { method: "GET", path: "/commerce/v2/listings" },
39
39
  input: listingsListInput,
@@ -142,13 +142,13 @@ export const listingRelistCapability = defineCapability({
142
142
  id: "listings.relist",
143
143
  domain: "listings",
144
144
  roles: ["user"],
145
- description: "Relist whole nodes of a reservation's capacity using a native availability window, nodeCount and financial terms. Buyers run on nodes Ornn assigns when their rental starts. Requires confirm: true.",
145
+ description: "Relist whole nodes of a reservation's capacity using a native availability window, nodeCount and financial terms. Buyers run on nodes Ornn assigns when their rental starts..",
146
146
  mutation: "preview-confirm",
147
147
  http: { method: "POST", path: "/commerce/reservations/{reservationId}/relist" },
148
148
  input: listingRelistInput,
149
149
  cli: {
150
150
  command: ["capacity-offers", "relist"],
151
- vnext: help(["capacity-offers", "relist"], "Relist reserved capacity with explicit financial terms.", "ornn capacity-offers relist --reservation-id UUID --node-count COUNT --starts-at TIME --ends-at TIME --terms JSON", "ornn capacity-offers relist --reservation-id UUID --node-count 1 --starts-at TIME --ends-at TIME --terms JSON --idempotency-key KEY --yes --format json", ["capacity-offers terms", "capacity-offers delist"], 24, ["reservation ID"]),
151
+ vnext: help(["capacity-offers", "relist"], "Relist reserved capacity with explicit financial terms.", "ornn capacity-offers relist --reservation-id UUID --node-count COUNT --starts-at TIME --ends-at TIME --terms JSON", "ornn capacity-offers relist --reservation-id UUID --node-count 1 --starts-at TIME --ends-at TIME --terms JSON --idempotency-key KEY --format json", ["capacity-offers terms", "capacity-offers delist"], 24, ["reservation ID"]),
152
152
  },
153
153
  slack: false,
154
154
  execute: async (ctx, input) => {
@@ -179,7 +179,7 @@ export const listingUpdateCapability = defineCapability({
179
179
  id: "listings.update",
180
180
  domain: "listings",
181
181
  roles: ["admin"],
182
- description: "Staff only: edit one advertised native financial term with its complete financial fields. Reuse idempotencyKey on retries. Requires confirm: true.",
182
+ description: "Staff only: edit one advertised native financial term with its complete financial fields. Reuse idempotencyKey on retries..",
183
183
  mutation: "preview-confirm",
184
184
  http: { method: "PATCH", path: "/commerce/v2/listings/{listingId}/terms/{listingTermsId}" },
185
185
  input: listingUpdateInput,
@@ -212,13 +212,13 @@ export const listingDelistCapability = defineCapability({
212
212
  id: "listings.delist",
213
213
  domain: "listings",
214
214
  roles: ["user"],
215
- description: "Delist a listing you own. Its unsold time leaves the market, its spot asks are cancelled and running spot reservations on it end. Windows already bought stay sold. Requires confirm: true.",
215
+ description: "Delist a listing you own. Its unsold time leaves the market, its spot asks are cancelled and running spot reservations on it end. Windows already bought stay sold..",
216
216
  mutation: "preview-confirm",
217
217
  http: { method: "POST", path: "/commerce/v2/listings/{listingId}/delist" },
218
218
  input: listingDelistInput,
219
219
  cli: {
220
220
  command: ["capacity-offers", "delist"],
221
- vnext: help(["capacity-offers", "delist"], "Delist a listing you relisted.", "ornn capacity-offers delist --listing-id <id>", "ornn capacity-offers delist --listing-id <id> --yes --format json", ["capacity-offers relist"], 26, ["listing ID"]),
221
+ vnext: help(["capacity-offers", "delist"], "Delist a listing you relisted.", "ornn capacity-offers delist --listing-id <id>", "ornn capacity-offers delist --listing-id <id> --format json", ["capacity-offers relist"], 26, ["listing ID"]),
222
222
  },
223
223
  slack: false,
224
224
  execute: async (ctx, input) => {
@@ -256,7 +256,7 @@ export const reservationsListCapability = defineCapability({
256
256
  id: "reservations.list",
257
257
  domain: "reservations",
258
258
  roles: ["user"],
259
- description: "List this organization's reservations, schedule and spot alike (a running spot reservation has no end time). With listingId, list the trades on a listing you own.",
259
+ description: "List this organization's reservations, schedule and spot alike (a running spot reservation has no end time). With listingId, list the trades on a listing you own. When nextCursor is present, pass it back as cursor for the next page.",
260
260
  mutation: "read",
261
261
  http: { method: "GET", path: "/commerce/reservations" },
262
262
  input: reservationsListInput,
@@ -348,13 +348,13 @@ export const reservationPurchaseCapability = defineCapability({
348
348
  id: "reservations.purchase",
349
349
  domain: "reservations",
350
350
  roles: ["user"],
351
- description: "Book a window of a relisted (secondary) listing on one of its terms at the term's price. The GPUs are held for you until the window starts; then it becomes your reservation, with the booking's ID. Nothing is charged when you book. Primary listings take offers instead (offers.post). Requires confirm: true.",
351
+ description: "Book a window of a relisted (secondary) listing on one of its terms at the term's price. The GPUs are held for you until the window starts; then it becomes your reservation, with the booking's ID. Nothing is charged when you book. Primary listings take offers instead (offers.post)..",
352
352
  mutation: "preview-confirm",
353
353
  http: { method: "POST", path: "/v1/markets/bookings" },
354
354
  input: reservationPurchaseInput,
355
355
  cli: {
356
356
  command: ["reservations", "buy"],
357
- vnext: help(["reservations", "buy"], "Book a window of relisted capacity at its term's price.", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd <n> [--node-count <n>]", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd 4000000 --idempotency-key <key> --yes --format json", ["capacity-offers availability", "capacity-offers terms", "reservations show"], 30, ["listingTermsId from capacity-offers terms"]),
357
+ vnext: help(["reservations", "buy"], "Book a window of relisted capacity at its term's price.", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd <n> [--node-count <n>]", "ornn reservations buy --listing-id <id> --listing-term-id <id> --starts-at <time> --ends-at <time> --price-per-gpu-hour-micro-usd 4000000 --idempotency-key <key> --format json", ["capacity-offers availability", "capacity-offers terms", "reservations show"], 30, ["listingTermsId from capacity-offers terms"]),
358
358
  },
359
359
  slack: false,
360
360
  execute: async (ctx, input) => {