@tiangong-lca/cli 0.0.24 → 0.0.26

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 (28) hide show
  1. package/README.md +66 -5
  2. package/dist/src/cli.js +484 -5
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-maintenance-alias-request.js +99 -0
  5. package/dist/src/lib/dataset-maintenance-alias-request.js.map +1 -0
  6. package/dist/src/lib/dataset-maintenance-apply.js +12 -367
  7. package/dist/src/lib/dataset-maintenance-apply.js.map +1 -1
  8. package/dist/src/lib/dataset-maintenance-protected-artifacts.js +100 -0
  9. package/dist/src/lib/dataset-maintenance-protected-artifacts.js.map +1 -0
  10. package/dist/src/lib/dataset-maintenance-protected-before.js +284 -0
  11. package/dist/src/lib/dataset-maintenance-protected-before.js.map +1 -0
  12. package/dist/src/lib/dataset-maintenance-protected-contract.js +918 -0
  13. package/dist/src/lib/dataset-maintenance-protected-contract.js.map +1 -0
  14. package/dist/src/lib/dataset-maintenance-protected-freeze.js +230 -0
  15. package/dist/src/lib/dataset-maintenance-protected-freeze.js.map +1 -0
  16. package/dist/src/lib/dataset-maintenance-protected-preparation.js +524 -0
  17. package/dist/src/lib/dataset-maintenance-protected-preparation.js.map +1 -0
  18. package/dist/src/lib/dataset-maintenance-protected-run.js +667 -0
  19. package/dist/src/lib/dataset-maintenance-protected-run.js.map +1 -0
  20. package/dist/src/lib/dataset-maintenance-protected-seal.js +160 -0
  21. package/dist/src/lib/dataset-maintenance-protected-seal.js.map +1 -0
  22. package/dist/src/lib/dataset-maintenance-protected-toolchain.js +86 -0
  23. package/dist/src/lib/dataset-maintenance-protected-toolchain.js.map +1 -0
  24. package/dist/src/lib/dataset-maintenance-protected-verify.js +435 -0
  25. package/dist/src/lib/dataset-maintenance-protected-verify.js.map +1 -0
  26. package/dist/src/lib/dataset-maintenance-remote.js +116 -2
  27. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  28. package/package.json +1 -1
package/dist/src/cli.js CHANGED
@@ -56,6 +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';
60
+ import { runDatasetMaintenanceProtected } from './lib/dataset-maintenance-protected-run.js';
61
+ import { sealDatasetMaintenanceProtectedApproval } from './lib/dataset-maintenance-protected-seal.js';
59
62
  import { runDatasetMaintenanceVerify } from './lib/dataset-maintenance-verify.js';
60
63
  import { runDatasetSourceUploadAttachments, } from './lib/dataset-source-upload-attachments.js';
61
64
  function renderMainHelp(dotEnvStatus) {
@@ -131,6 +134,8 @@ Examples:
131
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
132
135
  tiangong-lca dataset maintenance clear-account --out-dir /abs/path/to/account-clear --json
133
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
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
134
139
  tiangong-lca lifecyclemodel auto-build --input ./lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run
135
140
  tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run
136
141
  tiangong-lca lifecyclemodel publish-build --run-dir /abs/path/to/lifecyclemodel-run
@@ -283,6 +288,9 @@ Implemented Subcommands:
283
288
  maintenance clear-account Dry-run or clear current authenticated account-owned dataset rows through RLS
284
289
  maintenance plan Build an immutable, RLS-visible row-level maintenance plan
285
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
293
+ maintenance run-protected Run or inspect one sealed, one-shot production maintenance execution
286
294
  maintenance verify Read back affected rows and references against the immutable plan
287
295
 
288
296
  Examples:
@@ -310,21 +318,31 @@ Examples:
310
318
  tiangong-lca dataset references refresh-remote --input ./rows.jsonl --out ./rows.refreshed.jsonl --out-dir ./dataset-reference-refresh --help
311
319
  tiangong-lca dataset maintenance clear-account --out-dir ./account-clear --json --help
312
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
323
+ tiangong-lca dataset maintenance run-protected --help
313
324
  `.trim();
314
325
  }
315
326
  function renderDatasetMaintenanceHelp() {
316
327
  return `Usage:
317
- tiangong-lca dataset maintenance <clear-account|plan|apply|verify> [options]
328
+ tiangong-lca dataset maintenance <clear-account|plan|apply|freeze-protected|seal-protected-approval|run-protected|verify> [options]
318
329
 
319
330
  Actions:
320
331
  clear-account Dry-run or delete current authenticated account-owned lifecyclemodels, processes, flows, sources, and contacts.
321
332
  plan Build an immutable maintenance plan from a scope manifest, visible remote snapshot, dependency impact report, and intended operation.
322
- apply Execute or durably admit an approved plan through current-user RLS and platform dataset command paths; never bypass RLS.
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.
336
+ run-protected Run or inspect one sealed production execution with one-shot admission and terminal derivative proof.
323
337
  verify Re-fetch rows, references, or derivative request state and report passed, pending, or failed independently of apply.
324
338
 
325
339
  Safety:
326
- plan and verify are read-only.
340
+ plan, verify, and freeze-protected are read-only; seal-protected-approval is local-only.
327
341
  apply is commit-only and requires --commit, the exact plan SHA-256 via --approve-plan, and the current account email via --confirm.
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.
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.
328
346
 
329
347
  Required Artifact Contract:
330
348
  - maintenance-plan.json
@@ -334,13 +352,17 @@ Required Artifact Contract:
334
352
  - dry-run-report.json
335
353
  - commit-report.json
336
354
  - readback-verify-report.json
355
+ run-protected uses a separate immutable protected-execution seal, submission marker, append-only status ledger, and terminal report.
337
356
 
338
357
  Examples:
339
358
  tiangong-lca dataset maintenance clear-account --out-dir ./account-clear --json
340
359
  tiangong-lca dataset maintenance clear-account --commit --confirm user@example.com --out-dir ./account-clear
341
- tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir ./dataset-maintenance
360
+ tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation repair-references --out-dir ./dataset-maintenance
342
361
  tiangong-lca dataset maintenance plan --scope ./derivative-rebuild-scope.json --operation rebuild-derivatives --out-dir ./derivative-rebuild
343
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
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
344
366
  tiangong-lca dataset maintenance verify --plan ./dataset-maintenance/maintenance-plan.json --out-dir ./dataset-maintenance/verify
345
367
  `.trim();
346
368
  }
@@ -373,6 +395,7 @@ Behavior:
373
395
  Commit-only. The command rejects dry-run mode, a missing --commit flag, a plan hash mismatch,
374
396
  or a confirmation email that does not match the current authenticated account.
375
397
  For rebuild-derivatives, success means guarded admission is queued; it never means derivatives completed.
398
+ Sealed production merge-support-aliases executions must use run-protected; this command is not a fallback.
376
399
 
377
400
  Options:
378
401
  --plan <file> Immutable maintenance-plan.json
@@ -386,6 +409,102 @@ Options:
386
409
  Outputs include approval-record.json and commit-report.json alongside an append-only action or admission ledger.
387
410
  `.trim();
388
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
+ }
469
+ function renderDatasetMaintenanceRunProtectedHelp() {
470
+ return `Usage:
471
+ tiangong-lca dataset maintenance run-protected --plan <file> --freeze <file> --approval <file> --out-dir <dir> --commit --approve-execution <sha256> --confirm <email> [options]
472
+ tiangong-lca dataset maintenance run-protected --plan <file> --freeze <file> --approval <file> --out-dir <dir> --status-only [options]
473
+
474
+ Behavior:
475
+ Runs one sealed, server-dispatched production maintenance execution fenced to the authenticated actor's
476
+ exact owner-draft target set, or reads durable status through an auth.uid-, actor-, and plan-fenced RPC
477
+ without admitting work; only the independent table readback uses RLS.
478
+ Commit and status-only modes are mutually exclusive. The commit path validates the full private
479
+ before-state, accepts only server-derived gates within the 180-second preflight window, writes an immutable
480
+ local submission marker, and sends at most one admission POST. After a marker or ambiguous response,
481
+ use --status-only; the command never retries admission or falls back to dev or the legacy alias RPC.
482
+
483
+ Required in both modes:
484
+ --plan <file> Immutable maintenance-plan.json
485
+ --freeze <file> Protected production execution seal/freeze artifact
486
+ --approval <file> Exact human-approval binding artifact
487
+ --out-dir <dir> Private protected-run artifact directory
488
+
489
+ Commit-only guards:
490
+ --commit Submit the sealed execution exactly once
491
+ --approve-execution <sha256> Exact approved execution identity SHA-256
492
+ --confirm <email> Current authenticated account email
493
+
494
+ Recovery:
495
+ --status-only Read/poll and independently verify the sealed request; never preflight or admit
496
+
497
+ Options:
498
+ --wait-seconds <n> Maximum terminal-status wait in seconds; 0 performs one status read
499
+ --poll-ms <n> Positive polling interval in milliseconds; default 10000
500
+ --page-size <n> Requested RLS readback page size, 1-5000
501
+ --timeout-ms <n> Positive request timeout in milliseconds
502
+ --json Print compact JSON
503
+ -h, --help
504
+
505
+ Only a terminal passed proof exits successfully. pending, failed, and indeterminate return non-zero.
506
+ `.trim();
507
+ }
389
508
  function renderDatasetMaintenanceVerifyHelp() {
390
509
  return `Usage:
391
510
  tiangong-lca dataset maintenance verify --plan <file> [options]
@@ -3040,6 +3159,19 @@ function parseDatasetMaintenancePositiveInteger(value, flagName) {
3040
3159
  }
3041
3160
  return Number.parseInt(value.trim(), 10);
3042
3161
  }
3162
+ function parseDatasetMaintenanceNonNegativeInteger(value, flagName) {
3163
+ if (typeof value !== 'string') {
3164
+ return undefined;
3165
+ }
3166
+ if (!/^\d+$/u.test(value.trim())) {
3167
+ throw new CliError(`${flagName} must be a non-negative integer.`, {
3168
+ code: 'DATASET_MAINTENANCE_INTEGER_INVALID',
3169
+ exitCode: 2,
3170
+ details: { flag: flagName, value },
3171
+ });
3172
+ }
3173
+ return Number.parseInt(value.trim(), 10);
3174
+ }
3043
3175
  function parseDatasetMaintenancePlanFlags(args) {
3044
3176
  let values;
3045
3177
  try {
@@ -3126,6 +3258,134 @@ function parseDatasetMaintenanceApplyFlags(args) {
3126
3258
  dryRun: Boolean(values['dry-run']),
3127
3259
  };
3128
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
+ }
3341
+ function parseDatasetMaintenanceProtectedFlags(args) {
3342
+ let values;
3343
+ try {
3344
+ ({ values } = parseArgs({
3345
+ args,
3346
+ allowPositionals: false,
3347
+ strict: true,
3348
+ options: {
3349
+ help: { type: 'boolean', short: 'h' },
3350
+ json: { type: 'boolean' },
3351
+ plan: { type: 'string' },
3352
+ freeze: { type: 'string' },
3353
+ approval: { type: 'string' },
3354
+ 'out-dir': { type: 'string' },
3355
+ commit: { type: 'boolean' },
3356
+ 'status-only': { type: 'boolean' },
3357
+ 'approve-execution': { type: 'string' },
3358
+ confirm: { type: 'string' },
3359
+ 'wait-seconds': { type: 'string' },
3360
+ 'poll-ms': { type: 'string' },
3361
+ 'page-size': { type: 'string' },
3362
+ 'timeout-ms': { type: 'string' },
3363
+ },
3364
+ }));
3365
+ }
3366
+ catch (error) {
3367
+ throw new CliError(String(error), {
3368
+ code: 'INVALID_ARGS',
3369
+ exitCode: 2,
3370
+ });
3371
+ }
3372
+ return {
3373
+ help: Boolean(values.help),
3374
+ json: Boolean(values.json),
3375
+ planPath: typeof values.plan === 'string' ? values.plan : '',
3376
+ freezePath: typeof values.freeze === 'string' ? values.freeze : '',
3377
+ approvalPath: typeof values.approval === 'string' ? values.approval : '',
3378
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
3379
+ commit: Boolean(values.commit),
3380
+ statusOnly: Boolean(values['status-only']),
3381
+ approveExecution: typeof values['approve-execution'] === 'string' ? values['approve-execution'] : undefined,
3382
+ confirm: typeof values.confirm === 'string' ? values.confirm : undefined,
3383
+ waitSeconds: parseDatasetMaintenanceNonNegativeInteger(values['wait-seconds'], '--wait-seconds'),
3384
+ pollMs: parseDatasetMaintenancePositiveInteger(values['poll-ms'], '--poll-ms'),
3385
+ pageSize: parseDatasetMaintenancePositiveInteger(values['page-size'], '--page-size'),
3386
+ timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
3387
+ };
3388
+ }
3129
3389
  function parseDatasetMaintenanceVerifyFlags(args) {
3130
3390
  let values;
3131
3391
  try {
@@ -4905,6 +5165,9 @@ export async function executeCli(argv, deps) {
4905
5165
  const datasetMaintenanceClearAccountImpl = deps.runDatasetMaintenanceClearAccountImpl ?? runDatasetMaintenanceClearAccount;
4906
5166
  const datasetMaintenancePlanImpl = deps.runDatasetMaintenancePlanImpl ?? runDatasetMaintenancePlan;
4907
5167
  const datasetMaintenanceApplyImpl = deps.runDatasetMaintenanceApplyImpl ?? runDatasetMaintenanceApply;
5168
+ const datasetMaintenanceProtectedFreezeImpl = deps.freezeDatasetMaintenanceProtectedImpl ?? freezeDatasetMaintenanceProtected;
5169
+ const datasetMaintenanceProtectedImpl = deps.runDatasetMaintenanceProtectedImpl ?? runDatasetMaintenanceProtected;
5170
+ const datasetMaintenanceProtectedApprovalSealImpl = deps.sealDatasetMaintenanceProtectedApprovalImpl ?? sealDatasetMaintenanceProtectedApproval;
4908
5171
  const datasetMaintenanceVerifyImpl = deps.runDatasetMaintenanceVerifyImpl ?? runDatasetMaintenanceVerify;
4909
5172
  const datasetSourceUploadAttachmentsImpl = deps.runDatasetSourceUploadAttachmentsImpl ?? runDatasetSourceUploadAttachments;
4910
5173
  if (flags.version) {
@@ -5617,6 +5880,222 @@ export async function executeCli(argv, deps) {
5617
5880
  stderr: '',
5618
5881
  };
5619
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
+ }
6020
+ if (action === 'run-protected') {
6021
+ const datasetFlags = parseDatasetMaintenanceProtectedFlags(commandArgs.slice(1));
6022
+ if (datasetFlags.help) {
6023
+ return {
6024
+ exitCode: 0,
6025
+ stdout: `${renderDatasetMaintenanceRunProtectedHelp()}\n`,
6026
+ stderr: '',
6027
+ };
6028
+ }
6029
+ if (!datasetFlags.planPath) {
6030
+ throw new CliError('dataset maintenance run-protected requires --plan.', {
6031
+ code: 'DATASET_MAINTENANCE_PROTECTED_PLAN_REQUIRED',
6032
+ exitCode: 2,
6033
+ });
6034
+ }
6035
+ if (!datasetFlags.freezePath) {
6036
+ throw new CliError('dataset maintenance run-protected requires --freeze.', {
6037
+ code: 'DATASET_MAINTENANCE_PROTECTED_FREEZE_REQUIRED',
6038
+ exitCode: 2,
6039
+ });
6040
+ }
6041
+ if (!datasetFlags.approvalPath) {
6042
+ throw new CliError('dataset maintenance run-protected requires --approval.', {
6043
+ code: 'DATASET_MAINTENANCE_PROTECTED_APPROVAL_FILE_REQUIRED',
6044
+ exitCode: 2,
6045
+ });
6046
+ }
6047
+ if (!datasetFlags.outDir) {
6048
+ throw new CliError('dataset maintenance run-protected requires --out-dir.', {
6049
+ code: 'DATASET_MAINTENANCE_PROTECTED_OUT_DIR_REQUIRED',
6050
+ exitCode: 2,
6051
+ });
6052
+ }
6053
+ if (datasetFlags.commit && datasetFlags.statusOnly) {
6054
+ throw new CliError('Cannot pass both --commit and --status-only.', {
6055
+ code: 'DATASET_MAINTENANCE_PROTECTED_MODE_CONFLICT',
6056
+ exitCode: 2,
6057
+ });
6058
+ }
6059
+ if (!datasetFlags.commit && !datasetFlags.statusOnly) {
6060
+ throw new CliError('dataset maintenance run-protected requires either --commit or --status-only.', {
6061
+ code: 'DATASET_MAINTENANCE_PROTECTED_MODE_REQUIRED',
6062
+ exitCode: 2,
6063
+ });
6064
+ }
6065
+ if (datasetFlags.commit && !datasetFlags.approveExecution) {
6066
+ throw new CliError('dataset maintenance run-protected requires --approve-execution <sha256> with --commit.', {
6067
+ code: 'DATASET_MAINTENANCE_PROTECTED_APPROVAL_REQUIRED',
6068
+ exitCode: 2,
6069
+ });
6070
+ }
6071
+ if (datasetFlags.commit && !datasetFlags.confirm) {
6072
+ throw new CliError('dataset maintenance run-protected requires --confirm <email> with --commit.', {
6073
+ code: 'DATASET_MAINTENANCE_PROTECTED_CONFIRM_REQUIRED',
6074
+ exitCode: 2,
6075
+ });
6076
+ }
6077
+ const report = await datasetMaintenanceProtectedImpl({
6078
+ planPath: datasetFlags.planPath,
6079
+ freezePath: datasetFlags.freezePath,
6080
+ approvalPath: datasetFlags.approvalPath,
6081
+ outDir: datasetFlags.outDir,
6082
+ commit: datasetFlags.commit,
6083
+ statusOnly: datasetFlags.statusOnly,
6084
+ approveExecution: datasetFlags.approveExecution,
6085
+ confirm: datasetFlags.confirm,
6086
+ waitSeconds: datasetFlags.waitSeconds,
6087
+ pollMs: datasetFlags.pollMs,
6088
+ pageSize: datasetFlags.pageSize,
6089
+ timeoutMs: datasetFlags.timeoutMs,
6090
+ env: deps.env,
6091
+ fetchImpl: deps.fetchImpl,
6092
+ });
6093
+ return {
6094
+ exitCode: report.status === 'passed' ? 0 : 1,
6095
+ stdout: stringifyJson(report, datasetFlags.json),
6096
+ stderr: '',
6097
+ };
6098
+ }
5620
6099
  if (action === 'verify') {
5621
6100
  const datasetFlags = parseDatasetMaintenanceVerifyFlags(commandArgs.slice(1));
5622
6101
  if (datasetFlags.help) {
@@ -5646,7 +6125,7 @@ export async function executeCli(argv, deps) {
5646
6125
  stderr: '',
5647
6126
  };
5648
6127
  }
5649
- throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', or 'verify'.", {
6128
+ throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', 'freeze-protected', 'seal-protected-approval', 'run-protected', or 'verify'.", {
5650
6129
  code: 'DATASET_MAINTENANCE_ACTION_INVALID',
5651
6130
  exitCode: 2,
5652
6131
  });