@tiangong-lca/cli 0.0.28 → 0.0.29

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 +85 -2
  2. package/dist/src/cli.js +544 -3
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
  5. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js +175 -0
  6. package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.js.map +1 -0
  7. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +511 -0
  8. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -0
  9. package/dist/src/lib/dataset-maintenance-flow-identity-command.js +26 -0
  10. package/dist/src/lib/dataset-maintenance-flow-identity-command.js.map +1 -0
  11. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js +784 -0
  12. package/dist/src/lib/dataset-maintenance-flow-identity-contract.js.map +1 -0
  13. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js +1317 -0
  14. package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.js.map +1 -0
  15. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js +342 -0
  16. package/dist/src/lib/dataset-maintenance-flow-identity-freeze.js.map +1 -0
  17. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js +900 -0
  18. package/dist/src/lib/dataset-maintenance-flow-identity-plan.js.map +1 -0
  19. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js +688 -0
  20. package/dist/src/lib/dataset-maintenance-flow-identity-recovery.js.map +1 -0
  21. package/dist/src/lib/dataset-maintenance-flow-identity-run.js +1369 -0
  22. package/dist/src/lib/dataset-maintenance-flow-identity-run.js.map +1 -0
  23. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js +144 -0
  24. package/dist/src/lib/dataset-maintenance-flow-identity-seal.js.map +1 -0
  25. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js +377 -0
  26. package/dist/src/lib/dataset-maintenance-flow-identity-verify.js.map +1 -0
  27. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js +178 -0
  28. package/dist/src/lib/dataset-maintenance-flow-identity-wire.js.map +1 -0
  29. package/dist/src/lib/dataset-maintenance-remote.js +156 -10
  30. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  31. package/dist/src/lib/http.js.map +1 -1
  32. package/dist/src/lib/lca-release.js +683 -0
  33. package/dist/src/lib/lca-release.js.map +1 -0
  34. package/package.json +1 -1
package/dist/src/cli.js CHANGED
@@ -39,6 +39,7 @@ import { runFlowReviewedPublishData, } from './lib/flow-publish-reviewed-data.js
39
39
  import { runFlowBuildAliasMap, } from './lib/flow-build-alias-map.js';
40
40
  import { runFlowApplyProcessFlowRepairs, runFlowPlanProcessFlowRepairs, runFlowRegenProduct, runFlowScanProcessFlowRefs, runFlowValidateProcesses, } from './lib/flow-regen-product.js';
41
41
  import { executeRemoteCommand, getRemoteCommandHelp } from './lib/remote.js';
42
+ import { LCA_RELEASE_ACTIONS, renderLcaReleaseReport, runLcaRelease, } from './lib/lca-release.js';
42
43
  import { runValidation, } from './lib/validation.js';
43
44
  import { runDatasetValidate, } from './lib/dataset-validate.js';
44
45
  import { runDatasetCurationQueueBuild, runDatasetCurationQueueNext, runDatasetCurationQueueVerify, } from './lib/dataset-curation-queue.js';
@@ -60,6 +61,13 @@ import { freezeDatasetMaintenanceProtected } from './lib/dataset-maintenance-pro
60
61
  import { runDatasetMaintenanceProtected } from './lib/dataset-maintenance-protected-run.js';
61
62
  import { sealDatasetMaintenanceProtectedApproval } from './lib/dataset-maintenance-protected-seal.js';
62
63
  import { runDatasetMaintenanceVerify } from './lib/dataset-maintenance-verify.js';
64
+ import { runFlowIdentityPlanFromFiles } from './lib/dataset-maintenance-flow-identity-command.js';
65
+ import { captureFlowIdentity } from './lib/dataset-maintenance-flow-identity-capture.js';
66
+ import { freezeFlowIdentity } from './lib/dataset-maintenance-flow-identity-freeze.js';
67
+ import { sealFlowIdentityApproval } from './lib/dataset-maintenance-flow-identity-seal.js';
68
+ import { freezeFlowIdentityRecovery, sealFlowIdentityRecoveryApproval, } from './lib/dataset-maintenance-flow-identity-recovery.js';
69
+ import { runFlowIdentity } from './lib/dataset-maintenance-flow-identity-run.js';
70
+ import { verifyFlowIdentity } from './lib/dataset-maintenance-flow-identity-verify.js';
63
71
  import { runDatasetSourceUploadAttachments, } from './lib/dataset-source-upload-attachments.js';
64
72
  function renderMainHelp(dotEnvStatus) {
65
73
  return `TianGong LCA CLI
@@ -80,12 +88,13 @@ Implemented Commands:
80
88
  doctor show environment diagnostics
81
89
  search flow | process | lifecyclemodel
82
90
  process get | list | identity-preflight | build-plan | scope-statistics | dedup-review | auto-build | resume-build | publish-build | complete-required-fields | save-draft | batch-build | refresh-references | verify-rows
83
- dataset contract get | context-pack | classification children/path/audit/apply | curation-queue build/next/verify | import-lca convert | author | patch apply | save-draft | source upload-attachments | validate | verify-remote | bilingual extract/apply/validate | evidence-search plan/run | references rewrite/refresh-remote | maintenance clear-account/plan/apply/verify
91
+ dataset contract get | context-pack | classification children/path/audit/apply | curation-queue build/next/verify | import-lca convert | author | patch apply | save-draft | source upload-attachments | validate | verify-remote | bilingual extract/apply/validate | evidence-search plan/run | references rewrite/refresh-remote | maintenance clear-account/plan/apply/verify/flow-identity
84
92
  flow get | list | identity-preflight | build-plan | fetch-rows | materialize-decisions | remediate | publish-version | publish-reviewed-data | build-alias-map | scan-process-flow-refs | plan-process-flow-repairs | apply-process-flow-repairs | regen-product | validate-processes
85
93
  lifecyclemodel auto-build | validate-build | publish-build | save-draft | graph | build-resulting-process | publish-resulting-process | orchestrate
86
94
  qa process | flow | lifecyclemodel
87
95
  publish run
88
96
  validation run
97
+ release prepare | upload | finalize | approve | publish | readback-verify | unpublish | status | current | calculation-bundle | calculation-artifact | artifact-download
89
98
  admin embedding-run
90
99
 
91
100
  Planned Surface (not implemented yet):
@@ -163,6 +172,8 @@ Examples:
163
172
  tiangong-lca qa lifecyclemodel --rows-file ./lifecyclemodels.jsonl --out-dir ./lifecyclemodel-qa
164
173
  tiangong-lca publish run --input ./publish-request.json --dry-run
165
174
  tiangong-lca validation run --input-dir ./package --engine auto
175
+ tiangong-lca release status --release-run-id <run-id> --json
176
+ tiangong-lca release calculation-bundle --package-id <package-id> --output ./calculation-bundle.json
166
177
  tiangong-lca admin embedding-run --input ./jobs.json
167
178
 
168
179
  Environment:
@@ -226,6 +237,58 @@ Runtime note:
226
237
  and sends the resolved access token to Edge Functions.
227
238
  `.trim();
228
239
  }
240
+ function renderLcaReleaseHelp() {
241
+ return `Usage:
242
+ tiangong-lca release <action> [options]
243
+
244
+ Workflow actions:
245
+ prepare register an immutable publish plan from --input
246
+ upload verify four local ZIPs, upload them, and write --output receipt
247
+ finalize bind a Release Manifest and uploaded artifact refs from --input
248
+ approve create a durable manager approval from --input
249
+ publish publish an approved release from --input; credential fingerprint is derived locally
250
+ readback-verify record independent artifact readback hashes from --input
251
+ unpublish unpublish the current release from --input
252
+
253
+ Read and download actions:
254
+ status read --release-run-id
255
+ current read the current public release
256
+ calculation-bundle verify/sign bundle manifest and chunks for --package-id; requires --output
257
+ calculation-artifact download one exact --artifact-path from --package-id; requires --output
258
+ artifact-download download one release ZIP by --artifact-id; requires --output
259
+
260
+ Options:
261
+ --input <file> JSON command or upload request
262
+ --output <path> Durable JSON receipt/projection or downloaded bytes
263
+ --release-run-id <uuid> Release run for status
264
+ --package-id <uuid> LCIA result package for Calculation Bundle reads
265
+ --artifact-id <uuid> Published release artifact to download
266
+ --artifact-path <path> Exact path from a Calculation Bundle manifest
267
+ --force Replace an existing output file
268
+ --dry-run Validate and print the masked planned request without network writes
269
+ --json Print the stable compact JSON report; otherwise print Summary and Next
270
+ --api-key <key> Override TIANGONG_LCA_API_KEY; environment use is preferred
271
+ --base-url <url> Override TIANGONG_LCA_API_BASE_URL
272
+ --timeout-ms <n> Request timeout in milliseconds (default 60000)
273
+ -h, --help
274
+
275
+ Required environment:
276
+ TIANGONG_LCA_API_BASE_URL
277
+ TIANGONG_LCA_API_KEY
278
+ TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY
279
+
280
+ Authorization and safety:
281
+ The User API key is exchanged for a user session. Database RPCs then check that the live account
282
+ has data_product_manager. The service-role key never enters this CLI. Large bundle projections,
283
+ ZIPs, and chunks are written to files and verified against exact byte size and SHA-256.
284
+
285
+ Examples:
286
+ tiangong-lca release prepare --input ./release-prepare.json --json
287
+ tiangong-lca release upload --input ./release-upload.json --output ./upload-receipt.json
288
+ tiangong-lca release calculation-bundle --package-id 11111111-1111-4111-8111-111111111111 --output ./calculation-bundle.json
289
+ tiangong-lca release calculation-artifact --package-id 11111111-1111-4111-8111-111111111111 --artifact-path chunks/lci-00000.jsonl.gz --output ./lci-00000.jsonl.gz
290
+ `.trim();
291
+ }
229
292
  function renderPublishHelp() {
230
293
  return `Usage:
231
294
  tiangong-lca publish run --input <file> [options]
@@ -292,6 +355,7 @@ Implemented Subcommands:
292
355
  maintenance seal-protected-approval Record byte-exact human approval entirely offline
293
356
  maintenance run-protected Run or inspect one sealed, one-shot production maintenance execution
294
357
  maintenance verify Read back affected rows and references against the immutable plan
358
+ maintenance flow-identity Dedicated Step 3 capture/plan/freeze/approval/serial-run/verify workflow
295
359
 
296
360
  Examples:
297
361
  tiangong-lca dataset contract get --type process --include schema,methodology,ruleset --out-dir ./contract --help
@@ -321,11 +385,12 @@ Examples:
321
385
  tiangong-lca dataset maintenance freeze-protected --help
322
386
  tiangong-lca dataset maintenance seal-protected-approval --help
323
387
  tiangong-lca dataset maintenance run-protected --help
388
+ tiangong-lca dataset maintenance flow-identity --help
324
389
  `.trim();
325
390
  }
326
391
  function renderDatasetMaintenanceHelp() {
327
392
  return `Usage:
328
- tiangong-lca dataset maintenance <clear-account|plan|apply|freeze-protected|seal-protected-approval|run-protected|verify> [options]
393
+ tiangong-lca dataset maintenance <clear-account|plan|apply|freeze-protected|seal-protected-approval|run-protected|verify|flow-identity> [options]
329
394
 
330
395
  Actions:
331
396
  clear-account Dry-run or delete current authenticated account-owned lifecyclemodels, processes, flows, sources, and contacts.
@@ -335,6 +400,7 @@ Actions:
335
400
  seal-protected-approval Record a byte-exact human response into an approval artifact with no authentication or network access.
336
401
  run-protected Run or inspect one sealed production execution with one-shot admission and terminal derivative proof.
337
402
  verify Re-fetch rows, references, or derivative request state and report passed, pending, or failed independently of apply.
403
+ flow-identity Run the dedicated Step 3 capture/plan/freeze/seal-approval/serial-run/independent-verify workflow.
338
404
 
339
405
  Safety:
340
406
  plan, verify, and freeze-protected are read-only; seal-protected-approval is local-only.
@@ -343,6 +409,7 @@ Safety:
343
409
  freeze-protected is production-only and never calls preflight, gate, admission, execution, or mutation RPCs.
344
410
  seal-protected-approval only records the exact human-approved bytes; it never authenticates, connects, or submits execution.
345
411
  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.
412
+ flow-identity never reuses the Step 2 runner. Process writes are strictly ordinal and ambiguous responses permit read-only recovery, never automatic process replay.
346
413
 
347
414
  Required Artifact Contract:
348
415
  - maintenance-plan.json
@@ -364,6 +431,72 @@ Examples:
364
431
  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
432
  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
366
433
  tiangong-lca dataset maintenance verify --plan ./dataset-maintenance/maintenance-plan.json --out-dir ./dataset-maintenance/verify
434
+ tiangong-lca dataset maintenance flow-identity --help
435
+ `.trim();
436
+ }
437
+ function renderDatasetMaintenanceFlowIdentityHelp() {
438
+ return `Usage:
439
+ tiangong-lca dataset maintenance flow-identity <capture|plan|freeze|seal-approval|run|freeze-recovery|seal-recovery-approval|run-recovery|verify> [options]
440
+
441
+ Actions:
442
+ capture Authenticated production census and one database attestation; performs zero scope/preflight/rewrite/finalize calls.
443
+ plan Build immutable Step 3 reference-only process templates from an approved v3 review ledger and fresh post-Step2/post-#29 live capture.
444
+ freeze Offline-bind one production plan/toolchain/receipt and generate this execution's byte-exact approval request.
445
+ seal-approval Offline-record byte-exact human approval; performs zero authentication, network, or database calls.
446
+ run Create/read one guarded scope, serially submit only its durable next ordinal, then wait read-only for derivative readiness before at most one finalize POST. Ambiguous responses are never retried in the same invocation.
447
+ freeze-recovery Authenticated read-only scope snapshot that freezes a new exact continuation approval after the prior in-memory permit was lost.
448
+ seal-recovery-approval Offline-record byte-exact human approval for only that frozen durable-scope baseline.
449
+ run-recovery Consume that recovery approval once, obtain a fresh memory-only rotating permit, and continue the same durable scope.
450
+ verify Independently re-read the terminal scope, all 305 source rows, public/support rows, exact affected processes, and the complete owner-draft process reference closure.
451
+
452
+ Plan:
453
+ --policy <file> --review-ledger <file> --live-capture <file> --out-dir <dir>
454
+
455
+ Capture:
456
+ --policy <file> --review-ledger <file> --prerequisites <file> --toolchain-evidence <file>
457
+ --request-id <uuid> --operation-id <value> --expected-project-ref <ref> --confirm <email>
458
+ --sdk-version <value> --out-dir <dir>
459
+ [--page-size <n>] [--read-concurrency <n>] [--timeout-ms <n>]
460
+
461
+ Freeze:
462
+ --plan <file> --toolchain-evidence <file> --approved-at <iso> --expected-project-ref <ref> --confirm <email> --out-dir <dir>
463
+
464
+ Seal approval:
465
+ --plan <file> --freeze <file> --approval-request <file> --human-approval <file>
466
+ --approve-freeze-file <sha256> --approve-request <sha256> --approve-text <sha256>
467
+ --confirm <email> --approved-at <iso> --out-dir <dir>
468
+
469
+ Run:
470
+ --plan <file> --freeze <file> --approval <file> --out-dir <dir>
471
+ (--commit --approve-execution <sha256> --confirm <email> | --status-only)
472
+ [--wait-seconds <n>] [--poll-ms <n>] [--timeout-ms <n>]
473
+
474
+ Freeze recovery:
475
+ --plan <file> --freeze <original-file> --approval <original-file> --run-dir <prior-run-dir>
476
+ --toolchain-evidence <file> --approved-at <iso> --recovery-reason <reason>
477
+ --expected-project-ref <ref> --confirm <email> --out-dir <dir> [--timeout-ms <n>]
478
+
479
+ Seal recovery approval:
480
+ --recovery-freeze <file> --approval-request <file> --human-approval <file>
481
+ --approve-freeze-file <sha256> --approve-request <sha256> --approve-text <sha256>
482
+ --confirm <email> --approved-at <iso> --out-dir <dir>
483
+
484
+ Run recovery:
485
+ --plan <file> --freeze <original-file> --approval <original-file> --run-dir <prior-run-dir>
486
+ --recovery-freeze <file> --recovery-approval <file> --out-dir <dir>
487
+ --commit --approve-execution <recovery-identity-sha256> --confirm <email>
488
+ [--wait-seconds <n>] [--poll-ms <n>] [--timeout-ms <n>]
489
+
490
+ Verify:
491
+ --plan <file> --freeze <file> --approval <file> --run-dir <dir> --out-dir <dir>
492
+ [--page-size <n>] [--timeout-ms <n>]
493
+
494
+ Derivative failure boundary:
495
+ A failed/stale derivative will never retry a process. This command stops. Any derivative-only compensation requires a separate plan, new freeze, and new exact human approval.
496
+
497
+ Common:
498
+ --json
499
+ -h, --help
367
500
  `.trim();
368
501
  }
369
502
  function renderDatasetMaintenancePlanHelp() {
@@ -2167,6 +2300,71 @@ function parseRemoteFlags(args) {
2167
2300
  timeoutMs,
2168
2301
  };
2169
2302
  }
2303
+ function parseLcaReleaseFlags(args) {
2304
+ let values;
2305
+ try {
2306
+ ({ values } = parseArgs({
2307
+ args,
2308
+ allowPositionals: false,
2309
+ strict: true,
2310
+ options: {
2311
+ help: { type: 'boolean', short: 'h' },
2312
+ json: { type: 'boolean' },
2313
+ 'dry-run': { type: 'boolean' },
2314
+ force: { type: 'boolean' },
2315
+ input: { type: 'string' },
2316
+ output: { type: 'string' },
2317
+ 'release-run-id': { type: 'string' },
2318
+ 'package-id': { type: 'string' },
2319
+ 'artifact-id': { type: 'string' },
2320
+ 'artifact-path': { type: 'string' },
2321
+ 'api-key': { type: 'string' },
2322
+ 'base-url': { type: 'string' },
2323
+ 'timeout-ms': { type: 'string' },
2324
+ },
2325
+ }));
2326
+ }
2327
+ catch (error) {
2328
+ throw new CliError(String(error), {
2329
+ code: 'INVALID_ARGS',
2330
+ exitCode: 2,
2331
+ });
2332
+ }
2333
+ const timeoutText = typeof values['timeout-ms'] === 'string' ? values['timeout-ms'] : undefined;
2334
+ const timeoutMs = timeoutText ? Number.parseInt(timeoutText, 10) : 60_000;
2335
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
2336
+ throw new CliError('Expected --timeout-ms to be a positive integer.', {
2337
+ code: 'INVALID_TIMEOUT',
2338
+ exitCode: 2,
2339
+ });
2340
+ }
2341
+ const optionalString = (value) => typeof value === 'string' ? value : null;
2342
+ return {
2343
+ help: Boolean(values.help),
2344
+ json: Boolean(values.json),
2345
+ dryRun: Boolean(values['dry-run']),
2346
+ force: Boolean(values.force),
2347
+ inputPath: optionalString(values.input),
2348
+ outputPath: optionalString(values.output),
2349
+ releaseRunId: optionalString(values['release-run-id']),
2350
+ packageId: optionalString(values['package-id']),
2351
+ artifactId: optionalString(values['artifact-id']),
2352
+ artifactPath: optionalString(values['artifact-path']),
2353
+ apiKey: optionalString(values['api-key']),
2354
+ apiBaseUrl: optionalString(values['base-url']),
2355
+ timeoutMs,
2356
+ };
2357
+ }
2358
+ function requireLcaReleaseAction(value) {
2359
+ if (!LCA_RELEASE_ACTIONS.includes(value)) {
2360
+ throw new CliError(`Unsupported release action: ${value}`, {
2361
+ code: 'LCA_RELEASE_ACTION_UNSUPPORTED',
2362
+ exitCode: 2,
2363
+ details: { actions: LCA_RELEASE_ACTIONS },
2364
+ });
2365
+ }
2366
+ return value;
2367
+ }
2170
2368
  function parsePublishFlags(args) {
2171
2369
  let values;
2172
2370
  try {
@@ -3386,6 +3584,95 @@ function parseDatasetMaintenanceProtectedFlags(args) {
3386
3584
  timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
3387
3585
  };
3388
3586
  }
3587
+ function parseDatasetMaintenanceFlowIdentityFlags(args) {
3588
+ let values;
3589
+ try {
3590
+ ({ values } = parseArgs({
3591
+ args,
3592
+ allowPositionals: false,
3593
+ strict: true,
3594
+ options: {
3595
+ help: { type: 'boolean', short: 'h' },
3596
+ json: { type: 'boolean' },
3597
+ policy: { type: 'string' },
3598
+ 'review-ledger': { type: 'string' },
3599
+ prerequisites: { type: 'string' },
3600
+ 'live-capture': { type: 'string' },
3601
+ plan: { type: 'string' },
3602
+ 'toolchain-evidence': { type: 'string' },
3603
+ 'approval-text': { type: 'string' },
3604
+ 'expected-project-ref': { type: 'string' },
3605
+ 'request-id': { type: 'string' },
3606
+ 'operation-id': { type: 'string' },
3607
+ 'sdk-version': { type: 'string' },
3608
+ freeze: { type: 'string' },
3609
+ 'approval-request': { type: 'string' },
3610
+ 'human-approval': { type: 'string' },
3611
+ approval: { type: 'string' },
3612
+ 'recovery-freeze': { type: 'string' },
3613
+ 'recovery-approval': { type: 'string' },
3614
+ 'recovery-reason': { type: 'string' },
3615
+ 'approve-freeze-file': { type: 'string' },
3616
+ 'approve-request': { type: 'string' },
3617
+ 'approve-text': { type: 'string' },
3618
+ 'approve-execution': { type: 'string' },
3619
+ confirm: { type: 'string' },
3620
+ 'approved-at': { type: 'string' },
3621
+ 'out-dir': { type: 'string' },
3622
+ 'run-dir': { type: 'string' },
3623
+ commit: { type: 'boolean' },
3624
+ 'status-only': { type: 'boolean' },
3625
+ 'wait-seconds': { type: 'string' },
3626
+ 'poll-ms': { type: 'string' },
3627
+ 'page-size': { type: 'string' },
3628
+ 'read-concurrency': { type: 'string' },
3629
+ 'timeout-ms': { type: 'string' },
3630
+ },
3631
+ }));
3632
+ }
3633
+ catch (error) {
3634
+ throw new CliError(String(error), { code: 'INVALID_ARGS', exitCode: 2 });
3635
+ }
3636
+ const valueRecord = values;
3637
+ const string = (name) => typeof valueRecord[name] === 'string' ? String(valueRecord[name]) : '';
3638
+ return {
3639
+ help: Boolean(values.help),
3640
+ json: Boolean(values.json),
3641
+ policyPath: string('policy'),
3642
+ reviewLedgerPath: string('review-ledger'),
3643
+ prerequisitesPath: string('prerequisites'),
3644
+ liveCapturePath: string('live-capture'),
3645
+ planPath: string('plan'),
3646
+ toolchainEvidencePath: string('toolchain-evidence'),
3647
+ approvalTextPath: string('approval-text'),
3648
+ expectedProjectRef: string('expected-project-ref'),
3649
+ requestId: string('request-id'),
3650
+ operationId: string('operation-id'),
3651
+ sdkVersion: string('sdk-version'),
3652
+ freezePath: string('freeze'),
3653
+ approvalRequestPath: string('approval-request'),
3654
+ humanApprovalPath: string('human-approval'),
3655
+ approvalPath: string('approval'),
3656
+ recoveryFreezePath: string('recovery-freeze'),
3657
+ recoveryApprovalPath: string('recovery-approval'),
3658
+ recoveryReason: string('recovery-reason'),
3659
+ approveFreezeFile: string('approve-freeze-file'),
3660
+ approveRequest: string('approve-request'),
3661
+ approveText: string('approve-text'),
3662
+ approveExecution: string('approve-execution'),
3663
+ confirm: string('confirm'),
3664
+ approvedAtUtc: string('approved-at'),
3665
+ outDir: string('out-dir'),
3666
+ runDir: string('run-dir'),
3667
+ commit: Boolean(values.commit),
3668
+ statusOnly: Boolean(values['status-only']),
3669
+ waitSeconds: parseDatasetMaintenanceNonNegativeInteger(values['wait-seconds'], '--wait-seconds'),
3670
+ pollMs: parseDatasetMaintenancePositiveInteger(values['poll-ms'], '--poll-ms'),
3671
+ pageSize: parseDatasetMaintenancePositiveInteger(values['page-size'], '--page-size'),
3672
+ readConcurrency: parseDatasetMaintenancePositiveInteger(values['read-concurrency'], '--read-concurrency'),
3673
+ timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
3674
+ };
3675
+ }
3389
3676
  function parseDatasetMaintenanceVerifyFlags(args) {
3390
3677
  let values;
3391
3678
  try {
@@ -5100,6 +5387,7 @@ export async function executeCli(argv, deps) {
5100
5387
  const { flags, command, subcommand, commandArgs } = parseCommandLine(argv);
5101
5388
  const publishImpl = deps.runPublishImpl ?? runPublish;
5102
5389
  const validationImpl = deps.runValidationImpl ?? runValidation;
5390
+ const lcaReleaseImpl = deps.runLcaReleaseImpl ?? runLcaRelease;
5103
5391
  const lifecyclemodelAutoBuildImpl = deps.runLifecyclemodelAutoBuildImpl ?? runLifecyclemodelAutoBuild;
5104
5392
  const lifecyclemodelBuildImpl = deps.runLifecyclemodelBuildResultingProcessImpl ?? runLifecyclemodelBuildResultingProcess;
5105
5393
  const lifecyclemodelPublishImpl = deps.runLifecyclemodelPublishResultingProcessImpl ?? runLifecyclemodelPublishResultingProcess;
@@ -5169,6 +5457,14 @@ export async function executeCli(argv, deps) {
5169
5457
  const datasetMaintenanceProtectedImpl = deps.runDatasetMaintenanceProtectedImpl ?? runDatasetMaintenanceProtected;
5170
5458
  const datasetMaintenanceProtectedApprovalSealImpl = deps.sealDatasetMaintenanceProtectedApprovalImpl ?? sealDatasetMaintenanceProtectedApproval;
5171
5459
  const datasetMaintenanceVerifyImpl = deps.runDatasetMaintenanceVerifyImpl ?? runDatasetMaintenanceVerify;
5460
+ const flowIdentityPlanImpl = deps.runFlowIdentityPlanFromFilesImpl ?? runFlowIdentityPlanFromFiles;
5461
+ const flowIdentityCaptureImpl = deps.captureFlowIdentityImpl ?? captureFlowIdentity;
5462
+ const flowIdentityFreezeImpl = deps.freezeFlowIdentityImpl ?? freezeFlowIdentity;
5463
+ const flowIdentityApprovalSealImpl = deps.sealFlowIdentityApprovalImpl ?? sealFlowIdentityApproval;
5464
+ const flowIdentityRecoveryFreezeImpl = deps.freezeFlowIdentityRecoveryImpl ?? freezeFlowIdentityRecovery;
5465
+ const flowIdentityRecoveryApprovalSealImpl = deps.sealFlowIdentityRecoveryApprovalImpl ?? sealFlowIdentityRecoveryApproval;
5466
+ const flowIdentityRunImpl = deps.runFlowIdentityImpl ?? runFlowIdentity;
5467
+ const flowIdentityVerifyImpl = deps.verifyFlowIdentityImpl ?? verifyFlowIdentity;
5172
5468
  const datasetSourceUploadAttachmentsImpl = deps.runDatasetSourceUploadAttachmentsImpl ?? runDatasetSourceUploadAttachments;
5173
5469
  if (flags.version) {
5174
5470
  return { exitCode: 0, stdout: `${loadCliPackageVersion(import.meta.url)}\n`, stderr: '' };
@@ -5753,6 +6049,215 @@ export async function executeCli(argv, deps) {
5753
6049
  if (!action || action === '--help' || action === '-h') {
5754
6050
  return { exitCode: 0, stdout: `${renderDatasetMaintenanceHelp()}\n`, stderr: '' };
5755
6051
  }
6052
+ if (action === 'flow-identity') {
6053
+ const flowAction = commandArgs[1] ?? '';
6054
+ if (!flowAction || flowAction === '--help' || flowAction === '-h') {
6055
+ return {
6056
+ exitCode: 0,
6057
+ stdout: `${renderDatasetMaintenanceFlowIdentityHelp()}\n`,
6058
+ stderr: '',
6059
+ };
6060
+ }
6061
+ const datasetFlags = parseDatasetMaintenanceFlowIdentityFlags(commandArgs.slice(2));
6062
+ if (datasetFlags.help) {
6063
+ return {
6064
+ exitCode: 0,
6065
+ stdout: `${renderDatasetMaintenanceFlowIdentityHelp()}\n`,
6066
+ stderr: '',
6067
+ };
6068
+ }
6069
+ const required = (value, flag) => {
6070
+ if (!value) {
6071
+ throw new CliError(`dataset maintenance flow-identity ${flowAction} requires ${flag}.`, {
6072
+ code: 'DATASET_FLOW_IDENTITY_ARGUMENT_REQUIRED',
6073
+ exitCode: 2,
6074
+ });
6075
+ }
6076
+ return value;
6077
+ };
6078
+ if (flowAction === 'capture') {
6079
+ const report = await flowIdentityCaptureImpl({
6080
+ policyPath: required(datasetFlags.policyPath, '--policy'),
6081
+ reviewLedgerPath: required(datasetFlags.reviewLedgerPath, '--review-ledger'),
6082
+ prerequisitesPath: required(datasetFlags.prerequisitesPath, '--prerequisites'),
6083
+ toolchainEvidencePath: required(datasetFlags.toolchainEvidencePath, '--toolchain-evidence'),
6084
+ requestId: required(datasetFlags.requestId, '--request-id'),
6085
+ operationId: required(datasetFlags.operationId, '--operation-id'),
6086
+ expectedProjectRef: required(datasetFlags.expectedProjectRef, '--expected-project-ref'),
6087
+ confirm: required(datasetFlags.confirm, '--confirm'),
6088
+ cliVersion: loadCliPackageVersion(import.meta.url),
6089
+ sdkVersion: required(datasetFlags.sdkVersion, '--sdk-version'),
6090
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6091
+ pageSize: datasetFlags.pageSize,
6092
+ readConcurrency: datasetFlags.readConcurrency,
6093
+ timeoutMs: datasetFlags.timeoutMs,
6094
+ env: deps.env,
6095
+ fetchImpl: deps.fetchImpl,
6096
+ });
6097
+ return { exitCode: 0, stdout: stringifyJson(report, datasetFlags.json), stderr: '' };
6098
+ }
6099
+ if (flowAction === 'plan') {
6100
+ const plan = await flowIdentityPlanImpl({
6101
+ policyPath: required(datasetFlags.policyPath, '--policy'),
6102
+ reviewLedgerPath: required(datasetFlags.reviewLedgerPath, '--review-ledger'),
6103
+ liveCapturePath: required(datasetFlags.liveCapturePath, '--live-capture'),
6104
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6105
+ });
6106
+ return { exitCode: 0, stdout: stringifyJson(plan, datasetFlags.json), stderr: '' };
6107
+ }
6108
+ if (flowAction === 'freeze') {
6109
+ const report = await flowIdentityFreezeImpl({
6110
+ planPath: required(datasetFlags.planPath, '--plan'),
6111
+ toolchainEvidencePath: required(datasetFlags.toolchainEvidencePath, '--toolchain-evidence'),
6112
+ approvedAtUtc: required(datasetFlags.approvedAtUtc, '--approved-at'),
6113
+ expectedProjectRef: required(datasetFlags.expectedProjectRef, '--expected-project-ref'),
6114
+ confirm: required(datasetFlags.confirm, '--confirm'),
6115
+ cliVersion: loadCliPackageVersion(import.meta.url),
6116
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6117
+ });
6118
+ return { exitCode: 0, stdout: stringifyJson(report, datasetFlags.json), stderr: '' };
6119
+ }
6120
+ if (flowAction === 'seal-approval') {
6121
+ const report = await flowIdentityApprovalSealImpl({
6122
+ planPath: required(datasetFlags.planPath, '--plan'),
6123
+ freezePath: required(datasetFlags.freezePath, '--freeze'),
6124
+ approvalRequestPath: required(datasetFlags.approvalRequestPath, '--approval-request'),
6125
+ humanApprovalPath: required(datasetFlags.humanApprovalPath, '--human-approval'),
6126
+ approveFreezeFile: required(datasetFlags.approveFreezeFile, '--approve-freeze-file'),
6127
+ approveRequest: required(datasetFlags.approveRequest, '--approve-request'),
6128
+ approveText: required(datasetFlags.approveText, '--approve-text'),
6129
+ confirm: required(datasetFlags.confirm, '--confirm'),
6130
+ approvedAtUtc: required(datasetFlags.approvedAtUtc, '--approved-at'),
6131
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6132
+ });
6133
+ return { exitCode: 0, stdout: stringifyJson(report, datasetFlags.json), stderr: '' };
6134
+ }
6135
+ if (flowAction === 'freeze-recovery') {
6136
+ const recoveryReason = required(datasetFlags.recoveryReason, '--recovery-reason');
6137
+ if (![
6138
+ 'wrapper_exited_without_permit',
6139
+ 'process_response_ambiguous',
6140
+ 'process_domain_rejected',
6141
+ 'finalize_response_ambiguous',
6142
+ 'derivatives_became_ready_after_wrapper_exit',
6143
+ ].includes(recoveryReason)) {
6144
+ throw new CliError('flow-identity recovery reason is not allowed.', {
6145
+ code: 'DATASET_FLOW_IDENTITY_RECOVERY_REASON_INVALID',
6146
+ exitCode: 2,
6147
+ });
6148
+ }
6149
+ const report = await flowIdentityRecoveryFreezeImpl({
6150
+ planPath: required(datasetFlags.planPath, '--plan'),
6151
+ freezePath: required(datasetFlags.freezePath, '--freeze'),
6152
+ approvalPath: required(datasetFlags.approvalPath, '--approval'),
6153
+ runDir: required(datasetFlags.runDir, '--run-dir'),
6154
+ toolchainEvidencePath: required(datasetFlags.toolchainEvidencePath, '--toolchain-evidence'),
6155
+ expectedProjectRef: required(datasetFlags.expectedProjectRef, '--expected-project-ref'),
6156
+ confirm: required(datasetFlags.confirm, '--confirm'),
6157
+ approvedAtUtc: required(datasetFlags.approvedAtUtc, '--approved-at'),
6158
+ recoveryReason: recoveryReason,
6159
+ cliVersion: loadCliPackageVersion(import.meta.url),
6160
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6161
+ timeoutMs: datasetFlags.timeoutMs,
6162
+ env: deps.env,
6163
+ fetchImpl: deps.fetchImpl,
6164
+ });
6165
+ return { exitCode: 0, stdout: stringifyJson(report, datasetFlags.json), stderr: '' };
6166
+ }
6167
+ if (flowAction === 'seal-recovery-approval') {
6168
+ const report = flowIdentityRecoveryApprovalSealImpl({
6169
+ recoveryFreezePath: required(datasetFlags.recoveryFreezePath, '--recovery-freeze'),
6170
+ approvalRequestPath: required(datasetFlags.approvalRequestPath, '--approval-request'),
6171
+ humanApprovalPath: required(datasetFlags.humanApprovalPath, '--human-approval'),
6172
+ approveFreezeFile: required(datasetFlags.approveFreezeFile, '--approve-freeze-file'),
6173
+ approveRequest: required(datasetFlags.approveRequest, '--approve-request'),
6174
+ approveText: required(datasetFlags.approveText, '--approve-text'),
6175
+ confirm: required(datasetFlags.confirm, '--confirm'),
6176
+ approvedAtUtc: required(datasetFlags.approvedAtUtc, '--approved-at'),
6177
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6178
+ });
6179
+ return { exitCode: 0, stdout: stringifyJson(report, datasetFlags.json), stderr: '' };
6180
+ }
6181
+ if (flowAction === 'run-recovery') {
6182
+ if (!datasetFlags.commit || datasetFlags.statusOnly) {
6183
+ throw new CliError('flow-identity run-recovery requires --commit only.', {
6184
+ code: 'DATASET_FLOW_IDENTITY_RECOVERY_RUN_MODE_INVALID',
6185
+ exitCode: 2,
6186
+ });
6187
+ }
6188
+ const report = await flowIdentityRunImpl({
6189
+ planPath: required(datasetFlags.planPath, '--plan'),
6190
+ freezePath: required(datasetFlags.freezePath, '--freeze'),
6191
+ approvalPath: required(datasetFlags.approvalPath, '--approval'),
6192
+ recoveryRunDir: required(datasetFlags.runDir, '--run-dir'),
6193
+ recoveryFreezePath: required(datasetFlags.recoveryFreezePath, '--recovery-freeze'),
6194
+ recoveryApprovalPath: required(datasetFlags.recoveryApprovalPath, '--recovery-approval'),
6195
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6196
+ commit: true,
6197
+ statusOnly: false,
6198
+ approveExecution: required(datasetFlags.approveExecution, '--approve-execution'),
6199
+ confirm: required(datasetFlags.confirm, '--confirm'),
6200
+ waitSeconds: datasetFlags.waitSeconds,
6201
+ pollMs: datasetFlags.pollMs,
6202
+ timeoutMs: datasetFlags.timeoutMs,
6203
+ env: deps.env,
6204
+ fetchImpl: deps.fetchImpl,
6205
+ });
6206
+ return {
6207
+ exitCode: report.status === 'passed' ? 0 : 1,
6208
+ stdout: stringifyJson(report, datasetFlags.json),
6209
+ stderr: '',
6210
+ };
6211
+ }
6212
+ if (flowAction === 'run') {
6213
+ if (datasetFlags.commit === datasetFlags.statusOnly) {
6214
+ throw new CliError('flow-identity run requires exactly one of --commit or --status-only.', { code: 'DATASET_FLOW_IDENTITY_RUN_MODE_INVALID', exitCode: 2 });
6215
+ }
6216
+ if (datasetFlags.commit) {
6217
+ required(datasetFlags.approveExecution, '--approve-execution');
6218
+ required(datasetFlags.confirm, '--confirm');
6219
+ }
6220
+ const report = await flowIdentityRunImpl({
6221
+ planPath: required(datasetFlags.planPath, '--plan'),
6222
+ freezePath: required(datasetFlags.freezePath, '--freeze'),
6223
+ approvalPath: required(datasetFlags.approvalPath, '--approval'),
6224
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6225
+ commit: datasetFlags.commit,
6226
+ statusOnly: datasetFlags.statusOnly,
6227
+ approveExecution: datasetFlags.approveExecution || undefined,
6228
+ confirm: datasetFlags.confirm || undefined,
6229
+ waitSeconds: datasetFlags.waitSeconds,
6230
+ pollMs: datasetFlags.pollMs,
6231
+ timeoutMs: datasetFlags.timeoutMs,
6232
+ env: deps.env,
6233
+ fetchImpl: deps.fetchImpl,
6234
+ });
6235
+ return {
6236
+ exitCode: report.status === 'passed' ? 0 : 1,
6237
+ stdout: stringifyJson(report, datasetFlags.json),
6238
+ stderr: '',
6239
+ };
6240
+ }
6241
+ if (flowAction === 'verify') {
6242
+ const report = await flowIdentityVerifyImpl({
6243
+ planPath: required(datasetFlags.planPath, '--plan'),
6244
+ freezePath: required(datasetFlags.freezePath, '--freeze'),
6245
+ approvalPath: required(datasetFlags.approvalPath, '--approval'),
6246
+ runDir: required(datasetFlags.runDir, '--run-dir'),
6247
+ outDir: required(datasetFlags.outDir, '--out-dir'),
6248
+ pageSize: datasetFlags.pageSize,
6249
+ timeoutMs: datasetFlags.timeoutMs,
6250
+ env: deps.env,
6251
+ fetchImpl: deps.fetchImpl,
6252
+ });
6253
+ return {
6254
+ exitCode: report.status === 'passed' ? 0 : 1,
6255
+ stdout: stringifyJson(report, datasetFlags.json),
6256
+ stderr: '',
6257
+ };
6258
+ }
6259
+ throw new CliError("dataset maintenance flow-identity action must be 'capture', 'plan', 'freeze', 'seal-approval', 'run', 'freeze-recovery', 'seal-recovery-approval', 'run-recovery', or 'verify'.", { code: 'DATASET_FLOW_IDENTITY_ACTION_INVALID', exitCode: 2 });
6260
+ }
5756
6261
  if (action === 'clear-account') {
5757
6262
  const datasetFlags = parseDatasetMaintenanceClearAccountFlags(commandArgs.slice(1));
5758
6263
  if (datasetFlags.help) {
@@ -6125,7 +6630,7 @@ export async function executeCli(argv, deps) {
6125
6630
  stderr: '',
6126
6631
  };
6127
6632
  }
6128
- throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', 'freeze-protected', 'seal-protected-approval', 'run-protected', or 'verify'.", {
6633
+ throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', 'freeze-protected', 'seal-protected-approval', 'run-protected', 'verify', or 'flow-identity'.", {
6129
6634
  code: 'DATASET_MAINTENANCE_ACTION_INVALID',
6130
6635
  exitCode: 2,
6131
6636
  });
@@ -6969,6 +7474,42 @@ export async function executeCli(argv, deps) {
6969
7474
  stderr: '',
6970
7475
  };
6971
7476
  }
7477
+ if (command === 'release' && !subcommand) {
7478
+ return { exitCode: 0, stdout: `${renderLcaReleaseHelp()}\n`, stderr: '' };
7479
+ }
7480
+ if (command === 'release' && subcommand) {
7481
+ const releaseFlags = parseLcaReleaseFlags(commandArgs);
7482
+ if (releaseFlags.help) {
7483
+ return { exitCode: 0, stdout: `${renderLcaReleaseHelp()}\n`, stderr: '' };
7484
+ }
7485
+ const action = requireLcaReleaseAction(subcommand);
7486
+ const env = { ...deps.env };
7487
+ if (releaseFlags.apiKey !== null) {
7488
+ env.TIANGONG_LCA_API_KEY = releaseFlags.apiKey;
7489
+ }
7490
+ if (releaseFlags.apiBaseUrl !== null) {
7491
+ env.TIANGONG_LCA_API_BASE_URL = releaseFlags.apiBaseUrl;
7492
+ }
7493
+ const report = await lcaReleaseImpl({
7494
+ action,
7495
+ inputPath: releaseFlags.inputPath,
7496
+ outputPath: releaseFlags.outputPath,
7497
+ releaseRunId: releaseFlags.releaseRunId,
7498
+ packageId: releaseFlags.packageId,
7499
+ artifactId: releaseFlags.artifactId,
7500
+ artifactPath: releaseFlags.artifactPath,
7501
+ env,
7502
+ timeoutMs: releaseFlags.timeoutMs,
7503
+ dryRun: releaseFlags.dryRun,
7504
+ force: releaseFlags.force,
7505
+ fetchImpl: deps.fetchImpl,
7506
+ });
7507
+ return {
7508
+ exitCode: 0,
7509
+ stdout: releaseFlags.json ? stringifyJson(report, true) : renderLcaReleaseReport(report),
7510
+ stderr: '',
7511
+ };
7512
+ }
6972
7513
  if (command === 'admin' && !subcommand && commandArgs.includes('--help')) {
6973
7514
  return { exitCode: 0, stdout: `${renderAdminHelp()}\n`, stderr: '' };
6974
7515
  }