@tiangong-lca/cli 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ checkPaths:
18
18
  - src/cli.ts
19
19
  - src/main.ts
20
20
  lastReviewedAt: 2026-07-15
21
- lastReviewedCommit: ca0cdd7549cad9003d08fb338223ba74682955ae
21
+ lastReviewedCommit: bd145f692b3fd11e398302dd6a1d2831e058883a
22
22
  ---
23
23
 
24
24
  # TianGong LCA CLI
@@ -33,6 +33,8 @@ Review note, 2026-07-14: maintenance now includes the protected derivative-only
33
33
 
34
34
  Review note, 2026-07-15: `dataset maintenance run-protected` adds a production-only path for one sealed private alias execution and its exact 50-target derivative closure. The protected executor is server-dispatched and fenced by the authenticated owner plus exact actor/user_id/state_code=0 and plan-closure checks; RLS remains a defense on public and independent-read surfaces. It performs one server preflight, writes an immutable local attempt marker before one admission POST, and requires status-only recovery after any marker or ambiguous response. It has no dev, legacy-alias, publication, or state-code fallback.
35
35
 
36
+ Review note, 2026-07-15: `dataset maintenance freeze-protected` and `seal-protected-approval` close the preparation gap without adding a second execution path. Freeze preparation authenticates directly to the explicitly confirmed production project, performs only complete account/support reads plus the 50 derivative snapshot RPCs, and writes an unapproved canonical request. Approval sealing is entirely local and requires the exact human-returned UTF-8 bytes plus explicit freeze/request/text hashes and account confirmation. Only the later `run-protected` command can preflight or admit work.
37
+
36
38
  ## Run
37
39
 
38
40
  One-off published run:
@@ -285,7 +287,7 @@ tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operati
285
287
  tiangong-lca dataset maintenance plan --scope ./derivative-rebuild-scope.json --operation rebuild-derivatives --out-dir /abs/path/to/derivative-rebuild --json
286
288
  tiangong-lca dataset maintenance apply --plan /abs/path/to/dataset-maintenance/maintenance-plan.json --commit --approve-plan <sha256> --confirm <current-account-email> --timeout-ms 10000 --json
287
289
  tiangong-lca dataset maintenance verify --plan /abs/path/to/dataset-maintenance/maintenance-plan.json --out-dir /abs/path/to/dataset-maintenance/verify --page-size 1000 --timeout-ms 10000 --json
288
- tiangong-lca dataset maintenance run-protected --plan /abs/path/to/maintenance-plan.json --freeze /abs/path/to/protected-execution-seal.json --approval /abs/path/to/protected-approval.json --out-dir /abs/path/to/protected-run --status-only --json
290
+ tiangong-lca dataset maintenance run-protected --plan /abs/path/to/maintenance-plan.json --freeze /abs/path/to/protected-execution-freeze.json --approval /abs/path/to/protected-approval.json --out-dir /abs/path/to/protected-run --status-only --json
289
291
  tiangong-lca lifecyclemodel auto-build --input ./examples/lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run --json
290
292
  tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run --json
291
293
  tiangong-lca lifecyclemodel publish-build --run-dir /abs/path/to/lifecyclemodel-run --json
@@ -330,7 +332,7 @@ For `dataset references rewrite`, `--commit` executes the state-aware save-draft
330
332
 
331
333
  ## Dataset Maintenance
332
334
 
333
- `dataset maintenance plan/apply/run-protected/verify` is the row-level cleanup surface for bad imports, the fixed BAFU private alias rewrite, and protected derivative rebuilds. Ordinary planning, apply, and independent verification use the authenticated account and RLS. The protected executor is server-dispatched and additionally enforces the sealed actor, user_id, state_code=0, exact target set, and closure hashes on every write.
335
+ `dataset maintenance plan/apply/freeze-protected/seal-protected-approval/run-protected/verify` is the row-level cleanup surface for bad imports, the fixed BAFU private alias rewrite, and protected derivative rebuilds. Ordinary planning, apply, and independent verification use the authenticated account and RLS. Protected freeze preparation reads the live production owner-draft scope directly with no Dev data replay, while approval sealing is offline. The protected executor is server-dispatched and additionally enforces the sealed actor, user_id, state_code=0, exact target set, and closure hashes on every write.
334
336
 
335
337
  ```bash
336
338
  tiangong-lca dataset maintenance plan \
@@ -356,10 +358,33 @@ tiangong-lca dataset maintenance verify \
356
358
  --timeout-ms 10000 \
357
359
  --json
358
360
 
361
+ tiangong-lca dataset maintenance freeze-protected \
362
+ --plan ./protected-step2/maintenance-plan.json \
363
+ --toolchain-evidence ./protected-step2/toolchain-evidence.json \
364
+ --expected-project-ref <production-project-ref> \
365
+ --confirm <current-account-email> \
366
+ --out-dir ./protected-step2/freeze \
367
+ --page-size 1000 \
368
+ --timeout-ms 10000 \
369
+ --json
370
+
371
+ # After a human returns protected-approval-request.txt byte-for-byte as human-approval.txt:
372
+ tiangong-lca dataset maintenance seal-protected-approval \
373
+ --freeze ./protected-step2/freeze/protected-execution-freeze.json \
374
+ --approval-request ./protected-step2/freeze/protected-approval-request.json \
375
+ --human-approval ./protected-step2/human-approval.txt \
376
+ --approve-freeze-file <freeze-file-sha256> \
377
+ --approve-request <approval-request-sha256> \
378
+ --approve-text <approval-text-sha256> \
379
+ --confirm <current-account-email> \
380
+ --approved-at <approved-at-utc-from-request> \
381
+ --out-dir ./protected-step2/approval \
382
+ --json
383
+
359
384
  tiangong-lca dataset maintenance run-protected \
360
385
  --plan ./protected-step2/maintenance-plan.json \
361
- --freeze ./protected-step2/protected-execution-seal.json \
362
- --approval ./protected-step2/protected-approval.json \
386
+ --freeze ./protected-step2/freeze/protected-execution-freeze.json \
387
+ --approval ./protected-step2/approval/protected-approval.json \
363
388
  --out-dir ./protected-step2/run \
364
389
  --commit \
365
390
  --approve-execution <approved-execution-sha256> \
@@ -372,15 +397,19 @@ tiangong-lca dataset maintenance run-protected \
372
397
 
373
398
  tiangong-lca dataset maintenance run-protected \
374
399
  --plan ./protected-step2/maintenance-plan.json \
375
- --freeze ./protected-step2/protected-execution-seal.json \
376
- --approval ./protected-step2/protected-approval.json \
400
+ --freeze ./protected-step2/freeze/protected-execution-freeze.json \
401
+ --approval ./protected-step2/approval/protected-approval.json \
377
402
  --out-dir ./protected-step2/run \
378
403
  --status-only \
379
404
  --wait-seconds 60 \
380
405
  --json
381
406
  ```
382
407
 
383
- `run-protected` is a separate one-shot path for an already reviewed and sealed production execution; it does not replace ordinary planning. Both modes require the exact plan, freeze/seal, approval artifact, and private output directory. Commit mode additionally requires `--commit`, the exact approved execution identity through `--approve-execution`, and the authenticated account email through `--confirm`. `--status-only` is mutually exclusive with `--commit` and performs no preflight or admission.
408
+ `run-protected` is a separate one-shot path for an already reviewed and sealed production execution; it does not replace ordinary planning. Both modes require the exact plan, freeze artifact, approval artifact, and private output directory. Commit mode additionally requires `--commit`, the exact approved execution identity through `--approve-execution`, and the authenticated account email through `--confirm`. `--status-only` is mutually exclusive with `--commit` and performs no preflight or admission.
409
+
410
+ `freeze-protected` is the only supported generator for this fixed protected profile. Its toolchain evidence must be canonical JSON with schema `dataset-alias-protected-toolchain-evidence.v1`, production project ref, released-and-read-back database commit/evidence, the currently running published CLI version/commit/evidence, and the merged root-workspace integration commit/Issue. The fixed BAFU profile also has a compiled production project allowlist; the CLI rejects a Dev or arbitrary project even if the operator supplies matching flag/evidence values. The command verifies the exact 52 actions, two batches, six support snapshots, projected reference closure, and stable 23-flow + 27-process derivative snapshots; every derivative snapshot must have the same primary-row `modified_at` as the immediately preceding complete account census. It writes the entire private immutable alias-request, full baseline, freeze, unapproved request JSON/TXT, and final report into a sibling staging directory and atomically exposes the new output directory only after every file succeeds; preflight, gate, admission, mutation, and approval-artifact counts are all zero.
411
+
412
+ `seal-protected-approval` receives no environment or HTTP client. The freeze command puts one canonical approval-authority `approved_at_utc` into the request JSON, request hash, and human-visible approval text before review; seal requires `--approved-at` to equal that already approved value. The same text therefore cannot be resealed with another timestamp to mint a second database admission identity. The seal report records its actual generation time separately from this pre-authorized identity timestamp. Both commands hash raw file bytes, reject invalid UTF-8, publish their completed output directories atomically, and reject non-canonical freeze/request files, any changed whitespace or final newline, mismatched explicit hash/account/time bindings, and all three superseded historical Step-2 plan identities. Commit-mode `run-protected` rejects the same historical identities, so an old freeze/approval cannot bypass the fresh preparation chain; status-only remains read-only and available for recovery. Seal writes the canonical approval plus a local report, but does not submit execution. Human approval, sealing, and later execution remain separate events.
384
413
 
385
414
  Before requesting preflight, the command validates the sealed production project, full current-user RLS before-state, support closure, and exact derivative baseline. The server then returns the three expected gate digests and a token valid for at most 180 seconds; the CLI captures and compares the live gate receipts before admission. The server-dispatched write remains fenced to the authenticated actor's exact `user_id`, `state_code=0` rows and sealed plan/closure; independent readback still uses RLS. The CLI writes an immutable local submission marker and sends at most one admission POST. A marker, admission timeout, connection loss, or ambiguous admission response permanently switches that local run to status-only recovery; status-read failures may be polled only within the configured wait window and never cause a second admission or fallback to dev or the legacy whole-plan RPC. The default status polling interval is 10 seconds.
386
415
 
package/dist/src/cli.js CHANGED
@@ -56,7 +56,9 @@ import { runDatasetClassificationApply, runDatasetClassificationAudit, runDatase
56
56
  import { runDatasetMaintenanceClearAccount, } from './lib/dataset-maintenance-clear-account.js';
57
57
  import { runDatasetMaintenancePlan } from './lib/dataset-maintenance-plan.js';
58
58
  import { runDatasetMaintenanceApply } from './lib/dataset-maintenance-apply.js';
59
+ import { freezeDatasetMaintenanceProtected } from './lib/dataset-maintenance-protected-freeze.js';
59
60
  import { runDatasetMaintenanceProtected } from './lib/dataset-maintenance-protected-run.js';
61
+ import { sealDatasetMaintenanceProtectedApproval } from './lib/dataset-maintenance-protected-seal.js';
60
62
  import { runDatasetMaintenanceVerify } from './lib/dataset-maintenance-verify.js';
61
63
  import { runDatasetSourceUploadAttachments, } from './lib/dataset-source-upload-attachments.js';
62
64
  function renderMainHelp(dotEnvStatus) {
@@ -132,6 +134,7 @@ Examples:
132
134
  tiangong-lca dataset references rewrite --input ./rows.jsonl --from flow:<old-id>@<old-version> --to flow:<new-id>@<new-version> --out-dir /abs/path/to/dataset-rewrite
133
135
  tiangong-lca dataset maintenance clear-account --out-dir /abs/path/to/account-clear --json
134
136
  tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir /abs/path/to/dataset-maintenance
137
+ tiangong-lca dataset maintenance freeze-protected --plan ./maintenance-plan.json --toolchain-evidence ./toolchain.json --expected-project-ref <production-ref> --confirm bafudata@126.com --out-dir /abs/path/to/protected-freeze
135
138
  tiangong-lca dataset maintenance run-protected --plan ./maintenance-plan.json --freeze ./protected-execution-seal.json --approval ./protected-approval.json --out-dir /abs/path/to/protected-run --status-only
136
139
  tiangong-lca lifecyclemodel auto-build --input ./lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run
137
140
  tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run
@@ -285,6 +288,8 @@ Implemented Subcommands:
285
288
  maintenance clear-account Dry-run or clear current authenticated account-owned dataset rows through RLS
286
289
  maintenance plan Build an immutable, RLS-visible row-level maintenance plan
287
290
  maintenance apply Execute an explicitly approved maintenance plan through current-user RLS
291
+ maintenance freeze-protected Read and freeze one exact production owner-draft plan without admitting or mutating it
292
+ maintenance seal-protected-approval Record byte-exact human approval entirely offline
288
293
  maintenance run-protected Run or inspect one sealed, one-shot production maintenance execution
289
294
  maintenance verify Read back affected rows and references against the immutable plan
290
295
 
@@ -313,24 +318,30 @@ Examples:
313
318
  tiangong-lca dataset references refresh-remote --input ./rows.jsonl --out ./rows.refreshed.jsonl --out-dir ./dataset-reference-refresh --help
314
319
  tiangong-lca dataset maintenance clear-account --out-dir ./account-clear --json --help
315
320
  tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir ./dataset-maintenance --help
321
+ tiangong-lca dataset maintenance freeze-protected --help
322
+ tiangong-lca dataset maintenance seal-protected-approval --help
316
323
  tiangong-lca dataset maintenance run-protected --help
317
324
  `.trim();
318
325
  }
319
326
  function renderDatasetMaintenanceHelp() {
320
327
  return `Usage:
321
- tiangong-lca dataset maintenance <clear-account|plan|apply|run-protected|verify> [options]
328
+ tiangong-lca dataset maintenance <clear-account|plan|apply|freeze-protected|seal-protected-approval|run-protected|verify> [options]
322
329
 
323
330
  Actions:
324
331
  clear-account Dry-run or delete current authenticated account-owned lifecyclemodels, processes, flows, sources, and contacts.
325
332
  plan Build an immutable maintenance plan from a scope manifest, visible remote snapshot, dependency impact report, and intended operation.
326
333
  apply Execute or durably admit an ordinary approved plan through current-user RLS and platform dataset command paths.
334
+ freeze-protected Re-read the exact production owner-draft census/support/50 derivatives and generate an unapproved freeze request.
335
+ seal-protected-approval Record a byte-exact human response into an approval artifact with no authentication or network access.
327
336
  run-protected Run or inspect one sealed production execution with one-shot admission and terminal derivative proof.
328
337
  verify Re-fetch rows, references, or derivative request state and report passed, pending, or failed independently of apply.
329
338
 
330
339
  Safety:
331
- plan and verify are read-only.
340
+ plan, verify, and freeze-protected are read-only; seal-protected-approval is local-only.
332
341
  apply is commit-only and requires --commit, the exact plan SHA-256 via --approve-plan, and the current account email via --confirm.
333
342
  A sealed production merge-support-aliases execution must use run-protected; apply is not its fallback.
343
+ freeze-protected is production-only and never calls preflight, gate, admission, execution, or mutation RPCs.
344
+ seal-protected-approval only records the exact human-approved bytes; it never authenticates, connects, or submits execution.
334
345
  run-protected is production-only: authenticated owner context plus server-side actor/user_id/state_code=0 and exact-plan closure fences protect writes; RLS remains a defense on public and independent-read surfaces. It never falls back to dev, a legacy alias RPC, or a second admission POST.
335
346
 
336
347
  Required Artifact Contract:
@@ -349,6 +360,8 @@ Examples:
349
360
  tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation repair-references --out-dir ./dataset-maintenance
350
361
  tiangong-lca dataset maintenance plan --scope ./derivative-rebuild-scope.json --operation rebuild-derivatives --out-dir ./derivative-rebuild
351
362
  tiangong-lca dataset maintenance apply --plan ./dataset-maintenance/maintenance-plan.json --commit --approve-plan <sha256> --confirm user@example.com
363
+ tiangong-lca dataset maintenance freeze-protected --plan ./maintenance-plan.json --toolchain-evidence ./toolchain.json --expected-project-ref <production-ref> --confirm bafudata@126.com --out-dir ./protected-freeze
364
+ tiangong-lca dataset maintenance seal-protected-approval --freeze ./protected-freeze/protected-execution-freeze.json --approval-request ./protected-freeze/protected-approval-request.json --human-approval ./human-approval.txt --approve-freeze-file <sha256> --approve-request <sha256> --approve-text <sha256> --confirm bafudata@126.com --approved-at <iso> --out-dir ./protected-approval
352
365
  tiangong-lca dataset maintenance run-protected --plan ./maintenance-plan.json --freeze ./protected-execution-seal.json --approval ./protected-approval.json --out-dir ./protected-run --status-only
353
366
  tiangong-lca dataset maintenance verify --plan ./dataset-maintenance/maintenance-plan.json --out-dir ./dataset-maintenance/verify
354
367
  `.trim();
@@ -396,6 +409,63 @@ Options:
396
409
  Outputs include approval-record.json and commit-report.json alongside an append-only action or admission ledger.
397
410
  `.trim();
398
411
  }
412
+ function renderDatasetMaintenanceFreezeProtectedHelp() {
413
+ return `Usage:
414
+ tiangong-lca dataset maintenance freeze-protected --plan <file> --toolchain-evidence <file> --expected-project-ref <ref> --confirm <email> --out-dir <dir> [options]
415
+
416
+ Behavior:
417
+ Authenticates directly to the explicitly confirmed production project and re-reads the complete
418
+ owner-draft account census, six support snapshots, projected reference closure, and the stable
419
+ 23-flow + 27-process derivative snapshot set. It writes a canonical protected freeze and an
420
+ unapproved byte-exact human approval request. It never emits an approval artifact.
421
+
422
+ Required:
423
+ --plan <file> Ready owner-draft merge-support-aliases maintenance plan
424
+ --toolchain-evidence <file> Canonical released DB/CLI/root-integration evidence
425
+ --expected-project-ref <ref> Explicit production Supabase project ref
426
+ --confirm <email> Exact authenticated owner email
427
+ --out-dir <dir> New private immutable artifact directory
428
+
429
+ Options:
430
+ --page-size <n> Complete account scan page size, 1-5000
431
+ --timeout-ms <n> Positive authentication/read timeout in milliseconds
432
+ --json Print compact JSON
433
+ -h, --help
434
+
435
+ Remote safety:
436
+ This command uses only authenticated table reads and cmd_dataset_derivative_rebuild_snapshot.
437
+ Preflight, gate, admission, execution, save-draft, delete, state change, and all mutation calls are zero.
438
+ `.trim();
439
+ }
440
+ function renderDatasetMaintenanceSealProtectedApprovalHelp() {
441
+ return `Usage:
442
+ tiangong-lca dataset maintenance seal-protected-approval --freeze <file> --approval-request <file> --human-approval <file> --approve-freeze-file <sha256> --approve-request <sha256> --approve-text <sha256> --confirm <email> --approved-at <iso> --out-dir <dir> [options]
443
+
444
+ Behavior:
445
+ Records a human-provided approval text only when every UTF-8 byte, freeze-file hash, request hash,
446
+ text hash, account, and canonical timestamp matches the unapproved request. It writes the approval
447
+ artifact needed by a later, separate run-protected command; it does not submit that execution.
448
+
449
+ Required:
450
+ --freeze <file> Canonical protected freeze generated by freeze-protected
451
+ --approval-request <file> Canonical unapproved approval request JSON
452
+ --human-approval <file> Human-returned text, read byte-for-byte without trimming
453
+ --approve-freeze-file <sha256> Explicit SHA-256 of the exact freeze file bytes
454
+ --approve-request <sha256> Explicit request identity SHA-256
455
+ --approve-text <sha256> Explicit byte-exact approval text SHA-256
456
+ --confirm <email> Exact frozen account email
457
+ --approved-at <iso> Exact approval-authority timestamp already embedded in the request
458
+ --out-dir <dir> New private immutable artifact directory
459
+
460
+ Options:
461
+ --json Print compact JSON
462
+ -h, --help
463
+
464
+ Offline safety:
465
+ This command receives no environment or HTTP client and performs zero authentication, network,
466
+ database, preflight, gate, admission, execution, or mutation calls.
467
+ `.trim();
468
+ }
399
469
  function renderDatasetMaintenanceRunProtectedHelp() {
400
470
  return `Usage:
401
471
  tiangong-lca dataset maintenance run-protected --plan <file> --freeze <file> --approval <file> --out-dir <dir> --commit --approve-execution <sha256> --confirm <email> [options]
@@ -3188,6 +3258,86 @@ function parseDatasetMaintenanceApplyFlags(args) {
3188
3258
  dryRun: Boolean(values['dry-run']),
3189
3259
  };
3190
3260
  }
3261
+ function parseDatasetMaintenanceFreezeProtectedFlags(args) {
3262
+ let values;
3263
+ try {
3264
+ ({ values } = parseArgs({
3265
+ args,
3266
+ allowPositionals: false,
3267
+ strict: true,
3268
+ options: {
3269
+ help: { type: 'boolean', short: 'h' },
3270
+ json: { type: 'boolean' },
3271
+ plan: { type: 'string' },
3272
+ 'toolchain-evidence': { type: 'string' },
3273
+ 'out-dir': { type: 'string' },
3274
+ 'expected-project-ref': { type: 'string' },
3275
+ confirm: { type: 'string' },
3276
+ 'page-size': { type: 'string' },
3277
+ 'timeout-ms': { type: 'string' },
3278
+ },
3279
+ }));
3280
+ }
3281
+ catch (error) {
3282
+ throw new CliError(String(error), {
3283
+ code: 'INVALID_ARGS',
3284
+ exitCode: 2,
3285
+ });
3286
+ }
3287
+ return {
3288
+ help: Boolean(values.help),
3289
+ json: Boolean(values.json),
3290
+ planPath: typeof values.plan === 'string' ? values.plan : '',
3291
+ toolchainEvidencePath: typeof values['toolchain-evidence'] === 'string' ? values['toolchain-evidence'] : '',
3292
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
3293
+ expectedProjectRef: typeof values['expected-project-ref'] === 'string' ? values['expected-project-ref'] : '',
3294
+ confirm: typeof values.confirm === 'string' ? values.confirm : '',
3295
+ pageSize: parseDatasetMaintenancePositiveInteger(values['page-size'], '--page-size'),
3296
+ timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
3297
+ };
3298
+ }
3299
+ function parseDatasetMaintenanceSealProtectedApprovalFlags(args) {
3300
+ let values;
3301
+ try {
3302
+ ({ values } = parseArgs({
3303
+ args,
3304
+ allowPositionals: false,
3305
+ strict: true,
3306
+ options: {
3307
+ help: { type: 'boolean', short: 'h' },
3308
+ json: { type: 'boolean' },
3309
+ freeze: { type: 'string' },
3310
+ 'approval-request': { type: 'string' },
3311
+ 'human-approval': { type: 'string' },
3312
+ 'out-dir': { type: 'string' },
3313
+ 'approve-freeze-file': { type: 'string' },
3314
+ 'approve-request': { type: 'string' },
3315
+ 'approve-text': { type: 'string' },
3316
+ confirm: { type: 'string' },
3317
+ 'approved-at': { type: 'string' },
3318
+ },
3319
+ }));
3320
+ }
3321
+ catch (error) {
3322
+ throw new CliError(String(error), {
3323
+ code: 'INVALID_ARGS',
3324
+ exitCode: 2,
3325
+ });
3326
+ }
3327
+ return {
3328
+ help: Boolean(values.help),
3329
+ json: Boolean(values.json),
3330
+ freezePath: typeof values.freeze === 'string' ? values.freeze : '',
3331
+ approvalRequestPath: typeof values['approval-request'] === 'string' ? values['approval-request'] : '',
3332
+ humanApprovalPath: typeof values['human-approval'] === 'string' ? values['human-approval'] : '',
3333
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
3334
+ approveFreezeFile: typeof values['approve-freeze-file'] === 'string' ? values['approve-freeze-file'] : '',
3335
+ approveRequest: typeof values['approve-request'] === 'string' ? values['approve-request'] : '',
3336
+ approveText: typeof values['approve-text'] === 'string' ? values['approve-text'] : '',
3337
+ confirm: typeof values.confirm === 'string' ? values.confirm : '',
3338
+ approvedAtUtc: typeof values['approved-at'] === 'string' ? values['approved-at'] : '',
3339
+ };
3340
+ }
3191
3341
  function parseDatasetMaintenanceProtectedFlags(args) {
3192
3342
  let values;
3193
3343
  try {
@@ -5015,7 +5165,9 @@ export async function executeCli(argv, deps) {
5015
5165
  const datasetMaintenanceClearAccountImpl = deps.runDatasetMaintenanceClearAccountImpl ?? runDatasetMaintenanceClearAccount;
5016
5166
  const datasetMaintenancePlanImpl = deps.runDatasetMaintenancePlanImpl ?? runDatasetMaintenancePlan;
5017
5167
  const datasetMaintenanceApplyImpl = deps.runDatasetMaintenanceApplyImpl ?? runDatasetMaintenanceApply;
5168
+ const datasetMaintenanceProtectedFreezeImpl = deps.freezeDatasetMaintenanceProtectedImpl ?? freezeDatasetMaintenanceProtected;
5018
5169
  const datasetMaintenanceProtectedImpl = deps.runDatasetMaintenanceProtectedImpl ?? runDatasetMaintenanceProtected;
5170
+ const datasetMaintenanceProtectedApprovalSealImpl = deps.sealDatasetMaintenanceProtectedApprovalImpl ?? sealDatasetMaintenanceProtectedApproval;
5019
5171
  const datasetMaintenanceVerifyImpl = deps.runDatasetMaintenanceVerifyImpl ?? runDatasetMaintenanceVerify;
5020
5172
  const datasetSourceUploadAttachmentsImpl = deps.runDatasetSourceUploadAttachmentsImpl ?? runDatasetSourceUploadAttachments;
5021
5173
  if (flags.version) {
@@ -5728,6 +5880,143 @@ export async function executeCli(argv, deps) {
5728
5880
  stderr: '',
5729
5881
  };
5730
5882
  }
5883
+ if (action === 'freeze-protected') {
5884
+ const datasetFlags = parseDatasetMaintenanceFreezeProtectedFlags(commandArgs.slice(1));
5885
+ if (datasetFlags.help) {
5886
+ return {
5887
+ exitCode: 0,
5888
+ stdout: `${renderDatasetMaintenanceFreezeProtectedHelp()}\n`,
5889
+ stderr: '',
5890
+ };
5891
+ }
5892
+ if (!datasetFlags.planPath) {
5893
+ throw new CliError('dataset maintenance freeze-protected requires --plan.', {
5894
+ code: 'DATASET_MAINTENANCE_PROTECTED_PLAN_REQUIRED',
5895
+ exitCode: 2,
5896
+ });
5897
+ }
5898
+ if (!datasetFlags.toolchainEvidencePath) {
5899
+ throw new CliError('dataset maintenance freeze-protected requires --toolchain-evidence.', {
5900
+ code: 'DATASET_MAINTENANCE_PROTECTED_TOOLCHAIN_REQUIRED',
5901
+ exitCode: 2,
5902
+ });
5903
+ }
5904
+ if (!datasetFlags.expectedProjectRef) {
5905
+ throw new CliError('dataset maintenance freeze-protected requires --expected-project-ref.', {
5906
+ code: 'DATASET_MAINTENANCE_PROTECTED_PROJECT_REF_REQUIRED',
5907
+ exitCode: 2,
5908
+ });
5909
+ }
5910
+ if (!datasetFlags.confirm) {
5911
+ throw new CliError('dataset maintenance freeze-protected requires --confirm.', {
5912
+ code: 'DATASET_MAINTENANCE_PROTECTED_CONFIRM_REQUIRED',
5913
+ exitCode: 2,
5914
+ });
5915
+ }
5916
+ if (!datasetFlags.outDir) {
5917
+ throw new CliError('dataset maintenance freeze-protected requires --out-dir.', {
5918
+ code: 'DATASET_MAINTENANCE_PROTECTED_OUT_DIR_REQUIRED',
5919
+ exitCode: 2,
5920
+ });
5921
+ }
5922
+ const report = await datasetMaintenanceProtectedFreezeImpl({
5923
+ planPath: datasetFlags.planPath,
5924
+ toolchainEvidencePath: datasetFlags.toolchainEvidencePath,
5925
+ outDir: datasetFlags.outDir,
5926
+ expectedProjectRef: datasetFlags.expectedProjectRef,
5927
+ confirm: datasetFlags.confirm,
5928
+ cliVersion: loadCliPackageVersion(import.meta.url),
5929
+ pageSize: datasetFlags.pageSize,
5930
+ timeoutMs: datasetFlags.timeoutMs,
5931
+ env: deps.env,
5932
+ fetchImpl: deps.fetchImpl,
5933
+ });
5934
+ return {
5935
+ exitCode: 0,
5936
+ stdout: stringifyJson(report, datasetFlags.json),
5937
+ stderr: '',
5938
+ };
5939
+ }
5940
+ if (action === 'seal-protected-approval') {
5941
+ const datasetFlags = parseDatasetMaintenanceSealProtectedApprovalFlags(commandArgs.slice(1));
5942
+ if (datasetFlags.help) {
5943
+ return {
5944
+ exitCode: 0,
5945
+ stdout: `${renderDatasetMaintenanceSealProtectedApprovalHelp()}\n`,
5946
+ stderr: '',
5947
+ };
5948
+ }
5949
+ if (!datasetFlags.freezePath) {
5950
+ throw new CliError('dataset maintenance seal-protected-approval requires --freeze.', {
5951
+ code: 'DATASET_MAINTENANCE_PROTECTED_FREEZE_REQUIRED',
5952
+ exitCode: 2,
5953
+ });
5954
+ }
5955
+ if (!datasetFlags.approvalRequestPath) {
5956
+ throw new CliError('dataset maintenance seal-protected-approval requires --approval-request.', {
5957
+ code: 'DATASET_MAINTENANCE_PROTECTED_APPROVAL_REQUEST_REQUIRED',
5958
+ exitCode: 2,
5959
+ });
5960
+ }
5961
+ if (!datasetFlags.humanApprovalPath) {
5962
+ throw new CliError('dataset maintenance seal-protected-approval requires --human-approval.', {
5963
+ code: 'DATASET_MAINTENANCE_PROTECTED_HUMAN_APPROVAL_REQUIRED',
5964
+ exitCode: 2,
5965
+ });
5966
+ }
5967
+ if (!datasetFlags.approveFreezeFile) {
5968
+ throw new CliError('dataset maintenance seal-protected-approval requires --approve-freeze-file.', {
5969
+ code: 'DATASET_MAINTENANCE_PROTECTED_FREEZE_CONFIRMATION_REQUIRED',
5970
+ exitCode: 2,
5971
+ });
5972
+ }
5973
+ if (!datasetFlags.approveRequest) {
5974
+ throw new CliError('dataset maintenance seal-protected-approval requires --approve-request.', {
5975
+ code: 'DATASET_MAINTENANCE_PROTECTED_REQUEST_CONFIRMATION_REQUIRED',
5976
+ exitCode: 2,
5977
+ });
5978
+ }
5979
+ if (!datasetFlags.approveText) {
5980
+ throw new CliError('dataset maintenance seal-protected-approval requires --approve-text.', {
5981
+ code: 'DATASET_MAINTENANCE_PROTECTED_TEXT_CONFIRMATION_REQUIRED',
5982
+ exitCode: 2,
5983
+ });
5984
+ }
5985
+ if (!datasetFlags.confirm) {
5986
+ throw new CliError('dataset maintenance seal-protected-approval requires --confirm.', {
5987
+ code: 'DATASET_MAINTENANCE_PROTECTED_CONFIRM_REQUIRED',
5988
+ exitCode: 2,
5989
+ });
5990
+ }
5991
+ if (!datasetFlags.approvedAtUtc) {
5992
+ throw new CliError('dataset maintenance seal-protected-approval requires --approved-at.', {
5993
+ code: 'DATASET_MAINTENANCE_PROTECTED_APPROVED_AT_REQUIRED',
5994
+ exitCode: 2,
5995
+ });
5996
+ }
5997
+ if (!datasetFlags.outDir) {
5998
+ throw new CliError('dataset maintenance seal-protected-approval requires --out-dir.', {
5999
+ code: 'DATASET_MAINTENANCE_PROTECTED_OUT_DIR_REQUIRED',
6000
+ exitCode: 2,
6001
+ });
6002
+ }
6003
+ const report = await datasetMaintenanceProtectedApprovalSealImpl({
6004
+ freezePath: datasetFlags.freezePath,
6005
+ approvalRequestPath: datasetFlags.approvalRequestPath,
6006
+ humanApprovalPath: datasetFlags.humanApprovalPath,
6007
+ outDir: datasetFlags.outDir,
6008
+ approveFreezeFile: datasetFlags.approveFreezeFile,
6009
+ approveRequest: datasetFlags.approveRequest,
6010
+ approveText: datasetFlags.approveText,
6011
+ confirm: datasetFlags.confirm,
6012
+ approvedAtUtc: datasetFlags.approvedAtUtc,
6013
+ });
6014
+ return {
6015
+ exitCode: 0,
6016
+ stdout: stringifyJson(report, datasetFlags.json),
6017
+ stderr: '',
6018
+ };
6019
+ }
5731
6020
  if (action === 'run-protected') {
5732
6021
  const datasetFlags = parseDatasetMaintenanceProtectedFlags(commandArgs.slice(1));
5733
6022
  if (datasetFlags.help) {
@@ -5836,7 +6125,7 @@ export async function executeCli(argv, deps) {
5836
6125
  stderr: '',
5837
6126
  };
5838
6127
  }
5839
- throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', 'run-protected', or 'verify'.", {
6128
+ throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', 'freeze-protected', 'seal-protected-approval', 'run-protected', or 'verify'.", {
5840
6129
  code: 'DATASET_MAINTENANCE_ACTION_INVALID',
5841
6130
  exitCode: 2,
5842
6131
  });