@ornncompute/cli 1.0.72 → 1.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +24 -20
  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/external-bucket-commands.mjs +5 -73
  12. package/src/interaction-runtime.mjs +2 -37
  13. package/src/kubernetes-commands.mjs +9 -100
  14. package/src/launch-mode-handlers.mjs +0 -1
  15. package/src/mount-commands.mjs +25 -131
  16. package/src/storage-commands.mjs +2 -87
  17. package/src/vm-commands.mjs +9 -42
  18. package/vendor/capabilities/capabilities/admin-enrollment.js +18 -17
  19. package/vendor/capabilities/capabilities/admin-nodes-lifecycle.js +3 -101
  20. package/vendor/capabilities/capabilities/admin.js +3 -3
  21. package/vendor/capabilities/capabilities/clusters.js +3 -3
  22. package/vendor/capabilities/capabilities/commerce-market.js +9 -9
  23. package/vendor/capabilities/capabilities/compute.js +7 -7
  24. package/vendor/capabilities/capabilities/credits.js +7 -2
  25. package/vendor/capabilities/capabilities/external-buckets.js +4 -4
  26. package/vendor/capabilities/capabilities/fleet.js +109 -29
  27. package/vendor/capabilities/capabilities/market-shared.js +1 -1
  28. package/vendor/capabilities/capabilities/markets.js +12 -12
  29. package/vendor/capabilities/capabilities/mounts.js +4 -4
  30. package/vendor/capabilities/capabilities/networks.js +5 -5
  31. package/vendor/capabilities/capabilities/observability.js +49 -14
  32. package/vendor/capabilities/capabilities/tokens.js +5 -9
  33. package/vendor/capabilities/capabilities/users.js +8 -10
  34. package/vendor/capabilities/capabilities/volumes.js +5 -5
package/README.md CHANGED
@@ -104,9 +104,9 @@ CLI does not send them yet), end a fill with
104
104
  on a whole primary listing for one of its terms (`--listing-term-id`, optional
105
105
  `--duration-seconds`), list the offers your organization made or received, and
106
106
  accept or cancel offers by `--offer-id`.
107
- Prices are integer micro-USD per GPU-hour (`1000000` is $1.00). Every write
108
- reviews its terms and needs default-no consent, or `--yes` with
109
- `--format json`; writes accept `--idempotency-key`. Customer payment methods and
107
+ Prices are integer micro-USD per GPU-hour (`1000000` is $1.00). Writes execute
108
+ immediately; use `--format json` for machine-readable output and
109
+ `--idempotency-key` for safe retries. Customer payment methods and
110
110
  billing stay in the web app. Launch consumes already allocated
111
111
  capacity; internal reservation lookups remain necessary for access and
112
112
  ownership.
@@ -116,7 +116,7 @@ Only admins can create primary listings or reservations through
116
116
  They post to the role-aware `POST /commerce/v2/listings` and Markets
117
117
  `POST /v1/markets/bookings` with the admin's own session; the owners enforce
118
118
  the admin role. Both commands retain explicit commercial-term validation and
119
- default-no human confirmation. Reservation creation books a window for a
119
+ execute directly. Reservation creation books a window for a
120
120
  customer and takes `--organization-id UUID`, `--listing-id UUID`,
121
121
  `--listing-term-id UUID`, `--nodes COUNT`, `--starts-at TIME`, `--ends-at TIME`,
122
122
  `--price-per-gpu-hour-micro-usd N`, and `--idempotency-key KEY`. The window is
@@ -155,6 +155,8 @@ Every `ornn admin` command, read or mutation, requires an admin session.
155
155
  Authorization directory by person name, email, or company name, with bounded `--limit` and
156
156
  `--offset` pagination. Users can be restricted by `--organization`; organization
157
157
  results can be filtered by `--role`, `--approved`, or `--ids`.
158
+ User, organization, and supplier directory lists return `next_offset`; pass it
159
+ unchanged as the next `--offset`.
158
160
 
159
161
  `ornn admin organizations invite --organization UUID --email EMAIL` emails an
160
162
  invitation immediately using the selected organization UUID from the directory.
@@ -166,8 +168,9 @@ delivery or share the sign-in link before retrying. Use `--format json` for auto
166
168
  `ornn admin organizations grant-credit --organization UUID --amount-cents 10000
167
169
  --note "Customer goodwill credit" --idempotency-key UUID` grants $100.00 in Ornn
168
170
  credits to the organization. Find the organization with the directory commands
169
- above. Like admin reservation creation, it requires a human terminal and
170
- reviews the organization, USD amount, note, and UUID before default-no consent.
171
+ above. Like admin reservation creation, it requires an eligible human terminal
172
+ and executes immediately after validating the organization, USD amount, note,
173
+ and UUID.
171
174
  The key supplies the existing API's required `adjustmentId`; repeating it with
172
175
  the same details returns the same grant. The admin's session supplies audit
173
176
  identity. See the [credit-adjustment contract](../../services/commerce/README.md#staff-credit-adjustments).
@@ -212,15 +215,15 @@ never contains admin children. There is no second parser or command registry.
212
215
  ## Interaction contract
213
216
 
214
217
  An eligible human context requires TTY stdin and TTY stdout and no JSON/NDJSON
215
- format. Human commands may guide selection, render a normalized review, and ask
216
- default-no `[y/N]` consent. Empty input, EOF, and refusal submit nothing.
218
+ format. Human commands may guide selection and render a normalized summary;
219
+ mutations execute immediately without a confirmation prompt.
217
220
 
218
221
  Automation supplies complete semantic inputs and an explicit format:
219
222
 
220
223
  ```bash
221
224
  ornn status --format json
222
225
  ornn machines list --limit 100 --format ndjson
223
- ornn launch RESERVATION_ID --mode vm --yes \
226
+ ornn launch RESERVATION_ID --mode vm \
224
227
  --idempotency-key caller-stable-key --format json
225
228
  ```
226
229
 
@@ -229,19 +232,20 @@ Redirected commands require `--format human`, `--format json`, or
229
232
  prompts or launches a browser. Inputs missing for a safe decision fail before
230
233
  network, browser, or process I/O.
231
234
 
232
- Global interaction modifiers are `--format`, `--guided`, `--yes`, `--wait`,
235
+ Global interaction modifiers are `--format`, `--guided`, `--wait`,
233
236
  `--no-wait`, `--timeout`, `--no-browser`, `--overwrite`, and
234
- `--idempotency-key`. Mutations that create or increase spend, erase data, tear
235
- down resources, or reduce access require consent. Machine retries require a
237
+ `--idempotency-key`. Destructive operations require explicit intent flags such
238
+ as `--erase-local-data` or `--delete-node-record`. Machine retries require a
236
239
  caller-stable idempotency key and reconcile against durable owner state.
237
240
 
238
- Permanent deletion and teardown requests never fire without consent. Private
239
- networks, detached volumes, filesystem deployments, and Kubernetes or Slurm
240
- clusters accept `--yes` for automation or ask an eligible human for default-no
241
- confirmation. Clearing a volume or deleting nested content is live-terminal
242
- only and always asks, even when `--yes` is present.
241
+ Permanent deletion and teardown requests execute directly when explicitly
242
+ requested. Private networks, detached volumes, filesystem deployments, and
243
+ Kubernetes or Slurm clusters use their command arguments to express the exact
244
+ operation. Clearing a volume or deleting nested content is live-terminal only
245
+ and does not prompt for consent.
243
246
 
244
- List commands use bounded `--limit` values and opaque `--cursor` values. Waits
247
+ Other paged list commands use bounded `--limit` values and opaque `--cursor`
248
+ values. Pass the returned cursor unchanged as the next `--cursor`. Waits
245
249
  poll only retryable transitional states, honor the caller deadline and
246
250
  cancellation, and return the last observed state on timeout. Durable Bare Metal
247
251
  workspace flows persist local recovery pointers for `launch --resume`.
@@ -334,8 +338,8 @@ with one replacement and <https://docs.ornn.com/cli#migrating-to-vnext>.
334
338
  | `--json` | `--format json` |
335
339
  | `--interactive` | `--guided` |
336
340
 
337
- Changed behavior is intentional: waits are deadline-bound, consent is semantic
338
- and default-no, pagination is bounded and cursor-based, machine output is always
341
+ Changed behavior is intentional: waits are deadline-bound, mutations execute
342
+ directly, pagination is bounded and cursor-based, machine output is always
339
343
  enveloped, and exit statuses are semantic. Old help, completion entries,
340
344
  fixtures, staff roots, and owner-route spellings are not shipped.
341
345
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ornncompute/cli",
3
- "version": "1.0.72",
3
+ "version": "1.0.75",
4
4
  "description": "Command-line interface for Ornn compute access workflows.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,7 +5,7 @@ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-
5
5
 
6
6
  const USAGE = {
7
7
  "reservations.create":
8
- "Usage: ornn admin reservations create --organization-id ID --listing-id ID --listing-term-id ID --nodes COUNT --starts-at TIME --ends-at TIME --price-per-gpu-hour-micro-usd N --idempotency-key KEY [--yes]",
8
+ "Usage: ornn admin reservations create --organization-id ID --listing-id ID --listing-term-id ID --nodes COUNT --starts-at TIME --ends-at TIME --price-per-gpu-hour-micro-usd N --idempotency-key KEY",
9
9
  };
10
10
 
11
11
  function parseOptions(argv, { values, positionals }, usage) {
@@ -17,14 +17,6 @@ function parseOptions(argv, { values, positionals }, usage) {
17
17
  positionalValues.push(argument);
18
18
  continue;
19
19
  }
20
- if (argument === "--yes") {
21
- if (options.yes !== undefined)
22
- throw new AdminMutationError(`${usage}\n--yes may be supplied only once.`, {
23
- field: "yes",
24
- });
25
- options.yes = true;
26
- continue;
27
- }
28
20
  const separator = argument.indexOf("=");
29
21
  const name = argument.slice(2, separator === -1 ? undefined : separator);
30
22
  if (!values.includes(name)) throw new AdminMutationError(`${usage}\nUnknown option: --${name}`);
@@ -84,14 +76,13 @@ function timestamp(options, name, usage) {
84
76
  }
85
77
 
86
78
  function parseCapacityOffer(argv) {
87
- const { $positionals, yes, ...input } = parseSemanticArguments(argv);
79
+ const { $positionals, ...input } = parseSemanticArguments(argv);
88
80
  if ($positionals.length) throw new AdminMutationError("Use named options for listing writes.");
89
81
  return {
90
82
  input,
91
83
  mutation: {
92
84
  idempotencyKey: input.idempotencyKey,
93
85
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
94
- yes: yes === true,
95
86
  },
96
87
  };
97
88
  }
@@ -135,7 +126,6 @@ function parseReservationCreate(argv) {
135
126
  mutation: {
136
127
  idempotencyKey,
137
128
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
138
- yes: options.yes === true,
139
129
  },
140
130
  };
141
131
  }
@@ -50,7 +50,6 @@ export function preflightAdminCommand(catalog, argv, runtime) {
50
50
  ...runtime,
51
51
  idempotencyKey: parsed.mutation?.idempotencyKey,
52
52
  safety: handler.safety,
53
- yes: parsed.mutation?.yes,
54
53
  });
55
54
  }
56
55
  return { capabilityId: capability.id, command: command.join(" "), parsed };
@@ -2,7 +2,7 @@ import { ADMIN_MUTATION_SAFETY, AdminMutationError, runAdminMutation } from "./a
2
2
  import { parseSemanticArguments } from "./interaction-runtime.mjs";
3
3
 
4
4
  function parseGrant(argv, capability) {
5
- const { $positionals, yes, organization, amountCents, note, idempotencyKey, ...extra } =
5
+ const { $positionals, organization, amountCents, note, idempotencyKey, ...extra } =
6
6
  parseSemanticArguments(argv);
7
7
  if ($positionals.length || Object.keys(extra).length) {
8
8
  throw new AdminMutationError(`Usage: ${capability.cli.vnext.human}`);
@@ -21,7 +21,6 @@ function parseGrant(argv, capability) {
21
21
  mutation: {
22
22
  idempotencyKey: parsed.data.adjustmentId,
23
23
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
24
- yes: yes === true,
25
24
  },
26
25
  };
27
26
  }
@@ -11,15 +11,15 @@ const ENROLLMENT_LOG_READ_TIMEOUT_MS = 5_000;
11
11
 
12
12
  const USAGE = {
13
13
  "tokens.create":
14
- "Usage: ornn admin enrollment-tokens create --supplier UUID --expires-in SECONDS (--print | --output PATH) [--overwrite] [--yes]",
15
- "tokens.revoke":
16
- "Usage: ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] [--yes] [--idempotency-key KEY]",
14
+ "Usage: ornn admin enrollment-tokens create --supplier UUID --expires-in SECONDS (--print | --output PATH) [--overwrite]",
15
+ "enrollment.token.revoke":
16
+ "Usage: ornn admin enrollment-tokens revoke <token-id> [--expected-created-at TIME] [--idempotency-key KEY]",
17
17
  "nodes.enroll":
18
- "Usage: 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]",
18
+ "Usage: 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]",
19
19
  "nodes.export-key":
20
20
  "Usage: ornn admin nodes export-key <node-id> (--print | --output PATH) [--overwrite]",
21
21
  "nodes.rotate-key":
22
- "Usage: ornn admin nodes rotate-key <node-id> (--print | --output PATH) [--overwrite] [--yes]",
22
+ "Usage: ornn admin nodes rotate-key <node-id> (--print | --output PATH) [--overwrite]",
23
23
  };
24
24
 
25
25
  function parseOptions(argv, { booleans = [], positionals, values }, usage) {
@@ -108,7 +108,7 @@ function parseTokenCreate(argv) {
108
108
  const { options } = parseOptions(
109
109
  argv,
110
110
  {
111
- booleans: ["overwrite", "print", "yes"],
111
+ booleans: ["overwrite", "print"],
112
112
  positionals: 0,
113
113
  values: ["supplier", "expires-in", "output"],
114
114
  },
@@ -127,21 +127,22 @@ function parseTokenCreate(argv) {
127
127
  },
128
128
  mutation: {
129
129
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
130
- yes: options.yes === true,
131
130
  },
132
131
  };
133
132
  }
134
133
 
135
134
  function parseTokenRevoke(argv) {
136
- const usage = USAGE["tokens.revoke"];
135
+ const usage = USAGE["enrollment.token.revoke"];
137
136
  const { options, positionals } = parseOptions(
138
137
  argv,
139
- { booleans: ["yes"], positionals: 1, values: ["expected-created-at", "idempotency-key"] },
138
+ { booleans: [], positionals: 1, values: ["expected-created-at", "idempotency-key"] },
140
139
  usage
141
140
  );
141
+ const tokenId = uuid(positionals[0], "token-id");
142
142
  return {
143
143
  input: {
144
- token_id: uuid(positionals[0], "token-id"),
144
+ tokenId,
145
+ token_id: tokenId,
145
146
  ...(options["expected-created-at"]?.trim()
146
147
  ? { expected_created_at: options["expected-created-at"].trim() }
147
148
  : {}),
@@ -152,7 +153,6 @@ function parseTokenRevoke(argv) {
152
153
  mutation: {
153
154
  idempotencyKey: options["idempotency-key"]?.trim(),
154
155
  safety: ADMIN_MUTATION_SAFETY.reconcile,
155
- yes: options.yes === true,
156
156
  },
157
157
  };
158
158
  }
@@ -162,7 +162,7 @@ function parseNodeEnroll(argv) {
162
162
  const { options, positionals } = parseOptions(
163
163
  argv,
164
164
  {
165
- booleans: ["replace-existing-management", "verbose", "yes"],
165
+ booleans: ["replace-existing-management", "verbose"],
166
166
  positionals: 1,
167
167
  values: [
168
168
  "facility",
@@ -179,12 +179,9 @@ function parseNodeEnroll(argv) {
179
179
  );
180
180
  const filter = options.filter?.trim();
181
181
  if (!options["replace-existing-management"]) {
182
- throw new AdminMutationError(
183
- `${usage}\n--replace-existing-management is required and cannot be inferred by --yes.`,
184
- {
185
- field: "replace-existing-management",
186
- }
187
- );
182
+ throw new AdminMutationError(`${usage}\n--replace-existing-management is required.`, {
183
+ field: "replace-existing-management",
184
+ });
188
185
  }
189
186
  return {
190
187
  input: {
@@ -207,7 +204,6 @@ function parseNodeEnroll(argv) {
207
204
  },
208
205
  mutation: {
209
206
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
210
- yes: options.yes === true,
211
207
  },
212
208
  };
213
209
  }
@@ -218,7 +214,7 @@ function parseNodeKey(argv, rotate) {
218
214
  const { options, positionals } = parseOptions(
219
215
  argv,
220
216
  {
221
- booleans: rotate ? ["overwrite", "print", "yes"] : ["overwrite", "print"],
217
+ booleans: ["overwrite", "print"],
222
218
  positionals: 1,
223
219
  values: ["output"],
224
220
  },
@@ -233,7 +229,6 @@ function parseNodeKey(argv, rotate) {
233
229
  ? {
234
230
  mutation: {
235
231
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
236
- yes: options.yes === true,
237
232
  },
238
233
  }
239
234
  : {}),
@@ -353,7 +348,7 @@ function reviewFor(capabilityId, input, prepared) {
353
348
  consequence: "Creates a bearer credential that can enroll a node until it expires.",
354
349
  };
355
350
  }
356
- if (capabilityId === "tokens.revoke") {
351
+ if (capabilityId === "enrollment.token.revoke") {
357
352
  const target = prepared?.target;
358
353
  return {
359
354
  title: "Revoke enrollment token",
@@ -374,9 +369,7 @@ function reviewFor(capabilityId, input, prepared) {
374
369
  { label: "SSH target", value: `${input.username}@${input.host}:${input.port}` },
375
370
  { label: "Supplier", value: input.organization_id },
376
371
  { label: "Facility", value: input.facility_id },
377
- ...(input.filter
378
- ? [{ label: "Compartment filter", value: input.filter }]
379
- : []),
372
+ ...(input.filter ? [{ label: "Compartment filter", value: input.filter }] : []),
380
373
  { label: "Token lifetime", value: `${input.expires_in_seconds} seconds` },
381
374
  ],
382
375
  consequence:
@@ -573,7 +566,7 @@ async function runEnrollmentMutation({ capability, execute, fetch, input, mutati
573
566
  assertSecretOutputContext(input, runtime);
574
567
  await refuseExistingOutput(input.export);
575
568
  }
576
- if (capabilityId === "tokens.revoke" && runtime.machine && !input.expected_created_at) {
569
+ if (capabilityId === "enrollment.token.revoke" && runtime.machine && !input.expected_created_at) {
577
570
  throw new AdminMutationError(
578
571
  "Machine revocation requires --expected-created-at from the listed token generation.",
579
572
  { code: "exact_token_identity_required", field: "expected-created-at" }
@@ -585,7 +578,7 @@ async function runEnrollmentMutation({ capability, execute, fetch, input, mutati
585
578
  ...mutation,
586
579
  stdout: runtime.stderr ?? runtime.stdout,
587
580
  prepare: async () => {
588
- if (capabilityId === "tokens.revoke") {
581
+ if (capabilityId === "enrollment.token.revoke") {
589
582
  const prepared = await execute(input);
590
583
  return {
591
584
  review: reviewFor(capabilityId, input, prepared),
@@ -599,7 +592,7 @@ async function runEnrollmentMutation({ capability, execute, fetch, input, mutati
599
592
  return { review: reviewFor(capabilityId, input), value: input };
600
593
  },
601
594
  reconcile:
602
- capabilityId === "tokens.revoke"
595
+ capabilityId === "enrollment.token.revoke"
603
596
  ? async (value) => {
604
597
  if (value.prepared.disposition === "satisfied") {
605
598
  return {
@@ -635,7 +628,6 @@ async function runEnrollmentMutation({ capability, execute, fetch, input, mutati
635
628
  },
636
629
  });
637
630
 
638
- if (execution.status === "declined") return { ...execution, suppressResult: true };
639
631
  if (!SECRET_OUTPUT.has(capabilityId)) return execution;
640
632
  const exported = await exportSecret({
641
633
  capabilityId,
@@ -655,7 +647,7 @@ export const adminEnrollmentHandlers = [
655
647
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
656
648
  },
657
649
  {
658
- capabilityId: "tokens.revoke",
650
+ capabilityId: "enrollment.token.revoke",
659
651
  parse: parseTokenRevoke,
660
652
  run: runEnrollmentMutation,
661
653
  safety: ADMIN_MUTATION_SAFETY.reconcile,
@@ -1,6 +1,5 @@
1
- import { createInterface } from "node:readline/promises";
2
-
3
1
  export const ADMIN_MUTATION_SAFETY = Object.freeze({
2
+ direct: "direct",
4
3
  eligibleHumanOnly: "eligible-human-only",
5
4
  reconcile: "reconcile",
6
5
  });
@@ -46,32 +45,8 @@ export function writeAdminMutationReview(stdout, review) {
46
45
  }
47
46
  }
48
47
 
49
- async function defaultNoConsent(stdin, stdout) {
50
- const readline = createInterface({ input: stdin, output: stdout, terminal: false });
51
- try {
52
- const answer = await readline.question("Proceed? [y/N] ");
53
- return /^(y|yes)$/i.test(answer.trim());
54
- } catch (error) {
55
- if (
56
- error?.code === "ERR_USE_AFTER_CLOSE" ||
57
- error?.code === "ABORT_ERR" ||
58
- error?.name === "AbortError"
59
- ) {
60
- return false;
61
- }
62
- throw error;
63
- } finally {
64
- readline.close();
65
- }
66
- }
67
-
68
- export function assertAdminMutationContext({
69
- eligibleHuman,
70
- idempotencyKey,
71
- machine,
72
- safety,
73
- yes,
74
- }) {
48
+ export function assertAdminMutationContext({ eligibleHuman, idempotencyKey, machine, safety }) {
49
+ if (safety === ADMIN_MUTATION_SAFETY.direct) return;
75
50
  if (safety === ADMIN_MUTATION_SAFETY.eligibleHumanOnly) {
76
51
  if (!eligibleHuman || machine) {
77
52
  throw new AdminMutationError(
@@ -86,12 +61,6 @@ export function assertAdminMutationContext({
86
61
  code: "admin_mutation_policy_missing",
87
62
  });
88
63
  }
89
- if (!eligibleHuman && yes !== true) {
90
- throw new AdminMutationError("Noninteractive admin mutations require --yes.", {
91
- code: "consent_required",
92
- field: "yes",
93
- });
94
- }
95
64
  if (!eligibleHuman && !idempotencyKey?.trim()) {
96
65
  throw new AdminMutationError(
97
66
  "Noninteractive admin mutations require --idempotency-key with a caller-stable value.",
@@ -100,12 +69,6 @@ export function assertAdminMutationContext({
100
69
  }
101
70
  }
102
71
 
103
- /**
104
- * Shared privileged-mutation gate. All noninteractive prerequisites are checked
105
- * before prepare/reconcile/execute can perform I/O. Domain handlers own semantic
106
- * validation and exact goal comparison; this function owns context safety,
107
- * normalized review, default-no consent, and the apply/satisfied/conflict flow.
108
- */
109
72
  export async function runAdminMutation({
110
73
  eligibleHuman,
111
74
  execute,
@@ -114,11 +77,9 @@ export async function runAdminMutation({
114
77
  prepare,
115
78
  reconcile,
116
79
  safety,
117
- stdin,
118
80
  stdout,
119
- yes = false,
120
81
  }) {
121
- assertAdminMutationContext({ eligibleHuman, idempotencyKey, machine, safety, yes });
82
+ assertAdminMutationContext({ eligibleHuman, idempotencyKey, machine, safety });
122
83
  if (typeof prepare !== "function" || typeof execute !== "function") {
123
84
  throw new AdminMutationError("Admin mutation handler is incomplete.", {
124
85
  code: "admin_mutation_handler_incomplete",
@@ -132,8 +93,8 @@ export async function runAdminMutation({
132
93
 
133
94
  let prepared = await prepare();
134
95
  if (!prepared || typeof prepared !== "object") {
135
- throw new AdminMutationError("Admin mutation preparation returned no review.", {
136
- code: "admin_review_incomplete",
96
+ throw new AdminMutationError("Admin mutation preparation returned no operation.", {
97
+ code: "admin_preparation_invalid",
137
98
  });
138
99
  }
139
100
 
@@ -161,22 +122,11 @@ export async function runAdminMutation({
161
122
  }
162
123
  prepared = {
163
124
  ...prepared,
164
- ...(reconciliation.review ? { review: reconciliation.review } : {}),
165
125
  ...(reconciliation.value !== undefined ? { value: reconciliation.value } : {}),
166
126
  };
167
127
  }
168
128
 
169
129
  if (!machine) writeAdminMutationReview(stdout, prepared.review);
170
- if (eligibleHuman && yes !== true && !(await defaultNoConsent(stdin, stdout))) {
171
- return {
172
- data: { submitted: false },
173
- exitCode: 1,
174
- outcome: { class: "ended", phase: "declined" },
175
- reconciled: false,
176
- status: "declined",
177
- };
178
- }
179
-
180
130
  return {
181
131
  data: await execute(prepared.value),
182
132
  exitCode: 0,
@@ -36,11 +36,6 @@ function parseOptions(argv, allowed, usage) {
36
36
  if (options[name] !== undefined) {
37
37
  throw usageError(`${usage}\n--${name} may be supplied only once.`, name);
38
38
  }
39
- if (name === "yes") {
40
- if (separator !== -1) throw usageError(`${usage}\n--yes does not take a value.`, "yes");
41
- options.yes = true;
42
- continue;
43
- }
44
39
  const parsed = optionValue(argv, index, name, usage);
45
40
  options[name] = parsed.value;
46
41
  index += parsed.consumed;
@@ -64,18 +59,13 @@ function timeoutSeconds(value) {
64
59
  }
65
60
 
66
61
  export function parseAdminNodeDiagnoseArgs(argv) {
67
- const { options, positionals } = parseOptions(
68
- argv,
69
- new Set(["yes", "idempotency-key"]),
70
- DIAGNOSE_USAGE
71
- );
62
+ const { options, positionals } = parseOptions(argv, new Set(["idempotency-key"]), DIAGNOSE_USAGE);
72
63
  if (positionals.length !== 1 || !positionals[0]) throw usageError(DIAGNOSE_USAGE, "node-id");
73
64
  return {
74
65
  input: { node_id: positionals[0] },
75
66
  mutation: {
76
67
  idempotencyKey: options["idempotency-key"],
77
68
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
78
- yes: options.yes === true,
79
69
  },
80
70
  };
81
71
  }
@@ -83,7 +73,7 @@ export function parseAdminNodeDiagnoseArgs(argv) {
83
73
  export function parseAdminNodeExecArgs(argv) {
84
74
  const { options, positionals } = parseOptions(
85
75
  argv,
86
- new Set(["command", "timeout", "yes", "idempotency-key"]),
76
+ new Set(["command", "timeout", "idempotency-key"]),
87
77
  EXEC_USAGE
88
78
  );
89
79
  if (positionals.length !== 1 || !positionals[0]) throw usageError(EXEC_USAGE, "node-id");
@@ -99,7 +89,6 @@ export function parseAdminNodeExecArgs(argv) {
99
89
  mutation: {
100
90
  idempotencyKey: options["idempotency-key"],
101
91
  safety: ADMIN_MUTATION_SAFETY.eligibleHumanOnly,
102
- yes: options.yes === true,
103
92
  },
104
93
  };
105
94
  }
@@ -272,27 +261,6 @@ function executionResult(raw, input, target) {
272
261
  };
273
262
  }
274
263
 
275
- function declinedResult(input) {
276
- return {
277
- data: {
278
- declined: true,
279
- node: { node_id: input.node_id },
280
- ...(input.command ? { request: requestCorrelation(input) } : {}),
281
- },
282
- exitCode: 1,
283
- outcome: { class: "failed", phase: "consent_declined" },
284
- action: null,
285
- resume: null,
286
- errors: [
287
- {
288
- code: "consent_declined",
289
- message: "The action was declined; no mutation was submitted.",
290
- retryable: false,
291
- },
292
- ],
293
- };
294
- }
295
-
296
264
  function interruptedResult(input) {
297
265
  return {
298
266
  data: {
@@ -343,7 +311,6 @@ async function runDiagnosis({ input, mutation, runtime, execute, fetch }) {
343
311
  },
344
312
  execute: async () => execute({ node_id: input.node_id }, { confirmed: true }),
345
313
  });
346
- if (result.status === "declined") return declinedResult(input);
347
314
  return diagnosisResult(result.data, target);
348
315
  } catch (error) {
349
316
  if (wasInterrupted(error, runtime)) return interruptedResult(input);
@@ -388,7 +355,6 @@ async function runExecution({ input, mutation, runtime, execute, fetch }) {
388
355
  { confirmed: true }
389
356
  ),
390
357
  });
391
- if (result.status === "declined") return declinedResult(input);
392
358
  return executionResult(result.data, input, target);
393
359
  } catch (error) {
394
360
  if (wasInterrupted(error, runtime)) return interruptedResult(input);