@tiangong-lca/cli 0.0.21 → 0.0.23
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 +61 -4
- package/dist/src/cli.js +334 -22
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-alias-rewrite.js +541 -0
- package/dist/src/lib/dataset-maintenance-alias-rewrite.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-apply.js +1540 -0
- package/dist/src/lib/dataset-maintenance-apply.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-clear-account.js +121 -58
- package/dist/src/lib/dataset-maintenance-clear-account.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-contract.js +844 -0
- package/dist/src/lib/dataset-maintenance-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-pagination.js +270 -0
- package/dist/src/lib/dataset-maintenance-pagination.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-plan.js +488 -0
- package/dist/src/lib/dataset-maintenance-plan.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-remote.js +295 -0
- package/dist/src/lib/dataset-maintenance-remote.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-support-validation.js +18 -0
- package/dist/src/lib/dataset-maintenance-support-validation.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-verify.js +693 -0
- package/dist/src/lib/dataset-maintenance-verify.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,14 +17,18 @@ checkPaths:
|
|
|
17
17
|
- bin/**
|
|
18
18
|
- src/cli.ts
|
|
19
19
|
- src/main.ts
|
|
20
|
-
lastReviewedAt: 2026-
|
|
21
|
-
lastReviewedCommit:
|
|
20
|
+
lastReviewedAt: 2026-07-13
|
|
21
|
+
lastReviewedCommit: 4c79df4623e3cf296bc8d1baeea688d78351570a
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
# TianGong LCA CLI
|
|
25
25
|
|
|
26
26
|
Package: `@tiangong-lca/cli` Executable: `tiangong-lca` Node: `24.x`
|
|
27
27
|
|
|
28
|
+
Review note, 2026-07-12: `dataset maintenance plan/apply/verify` provides current-user RLS-scoped exact-row maintenance with immutable plans, explicit approval, per-action logs, platform audit correlation, and independent readback. `merge-support-aliases` now runs only in `target_mode=owner_draft`: source/target support and all changed rows stay private `state_code=0`; publication is a separate future workflow.
|
|
29
|
+
|
|
30
|
+
Review note, 2026-07-13: maintenance scans now prove exact-count pagination even when PostgREST returns fewer rows than the requested `--page-size`. An incomplete or inconsistent scan fails before artifacts, approval, or mutation; under stable filtered membership/order the proof represents a complete ordered multi-request traversal, not one transaction-level/MVCC snapshot.
|
|
31
|
+
|
|
28
32
|
## Run
|
|
29
33
|
|
|
30
34
|
One-off published run:
|
|
@@ -273,7 +277,9 @@ tiangong-lca dataset curation-queue verify --queue-dir /abs/path/to/curation-que
|
|
|
273
277
|
tiangong-lca dataset evidence-search plan --query "中国2026年电力结构数据" --out-dir /abs/path/to/evidence-search --json
|
|
274
278
|
tiangong-lca dataset evidence-search run --input ./evidence-search.request.json --results ./search-results.json --out-dir /abs/path/to/evidence-search --json
|
|
275
279
|
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 --json
|
|
276
|
-
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation redo-import --out-dir /abs/path/to/dataset-maintenance
|
|
280
|
+
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation redo-import --out-dir /abs/path/to/dataset-maintenance --page-size 1000 --timeout-ms 10000 --json
|
|
281
|
+
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
|
|
282
|
+
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
|
|
277
283
|
tiangong-lca lifecyclemodel auto-build --input ./examples/lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run --json
|
|
278
284
|
tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run --json
|
|
279
285
|
tiangong-lca lifecyclemodel publish-build --run-dir /abs/path/to/lifecyclemodel-run --json
|
|
@@ -316,7 +322,58 @@ For `dataset curation-queue build/next/verify`, the CLI owns entity-level Foundr
|
|
|
316
322
|
|
|
317
323
|
For `dataset references rewrite`, `--commit` executes the state-aware save-draft path for patched process and lifecyclemodel rows; without `--commit`, the command only writes local rewrite artifacts.
|
|
318
324
|
|
|
319
|
-
|
|
325
|
+
## Dataset Maintenance
|
|
326
|
+
|
|
327
|
+
`dataset maintenance plan/apply/verify` is the row-level cleanup surface for bad imports and the fixed BAFU private alias rewrite. It runs as the currently authenticated user and relies on RLS for visibility and ownership enforcement.
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
tiangong-lca dataset maintenance plan \
|
|
331
|
+
--scope ./maintenance-scope.json \
|
|
332
|
+
--operation merge-support-aliases \
|
|
333
|
+
--out-dir ./dataset-maintenance \
|
|
334
|
+
--page-size 1000 \
|
|
335
|
+
--timeout-ms 10000 \
|
|
336
|
+
--json
|
|
337
|
+
|
|
338
|
+
tiangong-lca dataset maintenance apply \
|
|
339
|
+
--plan ./dataset-maintenance/maintenance-plan.json \
|
|
340
|
+
--commit \
|
|
341
|
+
--approve-plan <sha256> \
|
|
342
|
+
--confirm <current-account-email> \
|
|
343
|
+
--timeout-ms 10000 \
|
|
344
|
+
--json
|
|
345
|
+
|
|
346
|
+
tiangong-lca dataset maintenance verify \
|
|
347
|
+
--plan ./dataset-maintenance/maintenance-plan.json \
|
|
348
|
+
--out-dir ./dataset-maintenance/verify \
|
|
349
|
+
--page-size 1000 \
|
|
350
|
+
--timeout-ms 10000 \
|
|
351
|
+
--json
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`--page-size` accepts `1-5000` and is only the requested maximum. PostgREST may enforce a lower server-side cap. The CLI requests `Prefer: count=exact`, validates the exact total and returned range from each `Content-Range`, advances the next offset by the number of rows actually returned, and requires strict `id`/`version` ordering without missing or duplicate identities. Each accepted scan records per-table requested/effective page size, page count, rows fetched, exact total, and aggregate entity counts.
|
|
355
|
+
|
|
356
|
+
This completeness proof means the CLI traversed the filtered result while that table's membership and ordering keys remained stable. Because the tables are read through multiple HTTP requests, it is not a transaction-level or MVCC snapshot of one instant; same-cardinality delete/insert churn can evade total and ordering checks. Plan hashes and apply-time drift checks provide the later mutation guard, and operators must avoid concurrent maintenance of the same account while planning or clearing it.
|
|
357
|
+
|
|
358
|
+
The scope is intentionally narrow:
|
|
359
|
+
|
|
360
|
+
- Each requested row must name its table, exact `id`, exact `version`, expected current owner, and draft `state_code=0` state.
|
|
361
|
+
- `--operation` accepts `delete`, `retire`, `redo-import`, `repair-references`, or `merge-support-aliases`; it records the operator's maintenance intent and does not broaden the eligible row actions.
|
|
362
|
+
- Only current-user `contacts`, `sources`, `flows`, and `processes` can become `save_draft` or `delete` actions.
|
|
363
|
+
- `merge-support-aliases` requires top-level `target_mode: "owner_draft"` and accepts only two named batches, `time` and `length_time`. The scope must bind reviewed current-owner draft source and target FP/UG exact ids/versions to 52 `update_json_ordered` actions: 25 time rows (1 FP, 10 flows, 14 processes) and 27 length-time rows (1 FP, 13 flows, 13 processes). Process actions freeze every selected exchange index, internal id, flow id/version, direction, before hash, and both amount strings.
|
|
364
|
+
- The alias factors are exact decimal strings: `0.00011415525114155251` for time and `1000` for length-time. Planning requires exactly 20 and 39 selected exchanges, preserves exactly 309 other exchanges in the affected processes, and proves the fixed source-zero/target-reference postconditions. The transformation changes references and the selected `meanAmount`/`resultingAmount`; it does not delete the source FP/UG rows.
|
|
365
|
+
- Source alias support, target FP/UG, and every changed flow/process must all belong to the authenticated account at `state_code=0`. Public/shared, foreign-owner, mixed-visibility, non-draft, lifecyclemodel, and unsupported action/table rows remain protected.
|
|
366
|
+
- The CLI classifies and executes an operator-authored scope; it does not decide whether rows are semantically duplicates, canonical replacements, or safe business-level cleanup targets.
|
|
367
|
+
|
|
368
|
+
`plan` accepts the account scan only after its exact-count proof is complete, then writes the frozen `maintenance-scope.json`, `rls-visible-snapshot.json`, `protected-rows.jsonl`, `reference-impact-report.json`, `maintenance-plan.json`, and `dry-run-report.json`. The snapshot, dry-run report, and newly generated plan carry the aggregate completeness proof, so it is bound into the plan SHA-256. Alias plans additionally write `exchange-rewrite-plan.jsonl`, freeze current-owner state-0 target FP, target UG, and source UG snapshots for each batch, derive schema-valid desired payloads with matching embedded UUID/version, and include the exact closure, `modified_at`, hashes, conversion evidence, and postconditions in the approved plan. The plan SHA-256 is the approval identity; do not edit or recompute the plan after review.
|
|
369
|
+
|
|
370
|
+
`apply` is write-disabled unless all three commit guards are present: `--commit`, `--approve-plan <sha256>`, and `--confirm <current-account-email>`. Before approval is persisted or any write runs, it requires a fresh complete exact-count account scan and re-checks the whole plan for drift; the current completeness proof is recorded in `approval-record.json`. Ordinary draft updates/deletes use their platform paths. The ordered `time` plus `length_time` alias request is sent once to `cmd_dataset_alias_plan_guarded` with `target_visibility=owner_draft`; the CLI has neither a per-dimension fallback nor a 52-write sequential fallback. The RPC locks and validates the complete 52-row/59-exchange closure before both dimensions commit, so a second-dimension failure rolls back the first. It checks actor ownership, state 0, exact payload/timestamp locks and embedded UUID/version, rejects missing or phantom flow/exchange references, and returns one plan summary audit id plus both batch and per-row audit proofs. The CLI writes `alias-plan-progress.jsonl` together with plan-bound per-batch, per-row, and per-exchange ledgers. A lost response or incomplete derived ledger is repaired only by replaying the same whole plan and matching every returned plan and batch proof.
|
|
371
|
+
|
|
372
|
+
`verify` requires another complete exact-count account readback rather than trusting the apply report, records its completeness proof, and writes `readback-verify-report.json` in its own output directory. For alias plans it also requires both successful batch records, all 52 correlated row records, all 59 unique exchange records, unchanged support snapshots, and exact desired row payloads. It validates the RPC-returned audit ids against the local proof chain; it does not independently query `public.command_audit_log`.
|
|
373
|
+
|
|
374
|
+
`dataset maintenance clear-account` uses the same exact-count rule for its initial five-table snapshot, per-table commit checks, and a final fresh scan of all five tables. It reports `cleared_account` only when that final aggregate proof exists with `row_count=0`; if the final proof fails after deletions begin, it still writes a `completed_with_failures` audit report. If the initial scan cannot prove completeness, it writes no snapshot or approval artifact and performs zero deletes.
|
|
375
|
+
|
|
376
|
+
Foundry and skills may prepare the scope, invoke these commands, and retain their artifacts. They must not replace the CLI with direct SQL, service-role access, raw REST mutation, or private Supabase delete/update code.
|
|
320
377
|
|
|
321
378
|
## More Docs
|
|
322
379
|
|
package/dist/src/cli.js
CHANGED
|
@@ -54,6 +54,9 @@ import { runDatasetPatchApply, } from './lib/dataset-patch.js';
|
|
|
54
54
|
import { runDatasetSaveDraft, } from './lib/dataset-save-draft-run.js';
|
|
55
55
|
import { runDatasetClassificationApply, runDatasetClassificationAudit, runDatasetClassificationChildren, runDatasetClassificationPath, } from './lib/dataset-classification.js';
|
|
56
56
|
import { runDatasetMaintenanceClearAccount, } from './lib/dataset-maintenance-clear-account.js';
|
|
57
|
+
import { runDatasetMaintenancePlan } from './lib/dataset-maintenance-plan.js';
|
|
58
|
+
import { runDatasetMaintenanceApply } from './lib/dataset-maintenance-apply.js';
|
|
59
|
+
import { runDatasetMaintenanceVerify } from './lib/dataset-maintenance-verify.js';
|
|
57
60
|
import { runDatasetSourceUploadAttachments, } from './lib/dataset-source-upload-attachments.js';
|
|
58
61
|
function renderMainHelp(dotEnvStatus) {
|
|
59
62
|
return `TianGong LCA CLI
|
|
@@ -74,7 +77,7 @@ Implemented Commands:
|
|
|
74
77
|
doctor show environment diagnostics
|
|
75
78
|
search flow | process | lifecyclemodel
|
|
76
79
|
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
|
|
77
|
-
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
|
|
80
|
+
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
|
|
78
81
|
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
|
|
79
82
|
lifecyclemodel auto-build | validate-build | publish-build | save-draft | graph | build-resulting-process | publish-resulting-process | orchestrate
|
|
80
83
|
qa process | flow | lifecyclemodel
|
|
@@ -85,7 +88,6 @@ Implemented Commands:
|
|
|
85
88
|
Planned Surface (not implemented yet):
|
|
86
89
|
auth whoami | doctor-auth
|
|
87
90
|
job get | wait | logs
|
|
88
|
-
dataset maintenance plan | apply | verify
|
|
89
91
|
|
|
90
92
|
Planned commands currently print an explicit "not implemented yet" message and exit with code 2.
|
|
91
93
|
|
|
@@ -128,7 +130,7 @@ Examples:
|
|
|
128
130
|
tiangong-lca dataset evidence-search run --input ./evidence-search.request.json --results ./search-results.json --out-dir ./evidence-search
|
|
129
131
|
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
|
|
130
132
|
tiangong-lca dataset maintenance clear-account --out-dir /abs/path/to/account-clear --json
|
|
131
|
-
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation
|
|
133
|
+
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir /abs/path/to/dataset-maintenance
|
|
132
134
|
tiangong-lca lifecyclemodel auto-build --input ./lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run
|
|
133
135
|
tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run
|
|
134
136
|
tiangong-lca lifecyclemodel publish-build --run-dir /abs/path/to/lifecyclemodel-run
|
|
@@ -279,9 +281,9 @@ Implemented Subcommands:
|
|
|
279
281
|
references rewrite Rewrite flow references in local process and lifecyclemodel rows
|
|
280
282
|
references refresh-remote Refresh local TIDAS reference versions to latest reachable remote rows
|
|
281
283
|
maintenance clear-account Dry-run or clear current authenticated account-owned dataset rows through RLS
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
maintenance
|
|
284
|
+
maintenance plan Build an immutable, RLS-visible row-level maintenance plan
|
|
285
|
+
maintenance apply Execute an explicitly approved maintenance plan through current-user RLS
|
|
286
|
+
maintenance verify Read back affected rows and references against the immutable plan
|
|
285
287
|
|
|
286
288
|
Examples:
|
|
287
289
|
tiangong-lca dataset contract get --type process --include schema,methodology,ruleset --out-dir ./contract --help
|
|
@@ -307,25 +309,23 @@ Examples:
|
|
|
307
309
|
tiangong-lca dataset references rewrite --input ./rows.jsonl --from flow:<old-id>@<old-version> --to flow:<new-id>@<new-version> --out-dir ./dataset-rewrite --help
|
|
308
310
|
tiangong-lca dataset references refresh-remote --input ./rows.jsonl --out ./rows.refreshed.jsonl --out-dir ./dataset-reference-refresh --help
|
|
309
311
|
tiangong-lca dataset maintenance clear-account --out-dir ./account-clear --json --help
|
|
310
|
-
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation
|
|
312
|
+
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir ./dataset-maintenance --help
|
|
311
313
|
`.trim();
|
|
312
314
|
}
|
|
313
315
|
function renderDatasetMaintenanceHelp() {
|
|
314
316
|
return `Usage:
|
|
315
317
|
tiangong-lca dataset maintenance <clear-account|plan|apply|verify> [options]
|
|
316
318
|
|
|
317
|
-
|
|
318
|
-
clear-account is implemented for current authenticated account cleanup.
|
|
319
|
-
plan/apply/verify remain reserved for row-level RLS-scoped cleanup and redo workflows and currently exit with code 2 for executable actions.
|
|
320
|
-
|
|
321
|
-
Implemented Actions:
|
|
319
|
+
Actions:
|
|
322
320
|
clear-account Dry-run or delete current authenticated account-owned lifecyclemodels, processes, flows, sources, and contacts.
|
|
323
|
-
|
|
324
|
-
Planned Actions:
|
|
325
321
|
plan Build an immutable maintenance plan from a scope manifest, visible remote snapshot, dependency impact report, and intended operation.
|
|
326
322
|
apply Execute an approved plan through current-user RLS and platform dataset command paths; never bypass RLS or delete rows outside the visible scope.
|
|
327
323
|
verify Re-fetch affected rows and references, then prove that deleted, updated, skipped, protected, and redone rows match the plan.
|
|
328
324
|
|
|
325
|
+
Safety:
|
|
326
|
+
plan and verify are read-only.
|
|
327
|
+
apply is commit-only and requires --commit, the exact plan SHA-256 via --approve-plan, and the current account email via --confirm.
|
|
328
|
+
|
|
329
329
|
Required Artifact Contract:
|
|
330
330
|
- maintenance-plan.json
|
|
331
331
|
- rls-visible-snapshot.json
|
|
@@ -338,11 +338,66 @@ Required Artifact Contract:
|
|
|
338
338
|
Examples:
|
|
339
339
|
tiangong-lca dataset maintenance clear-account --out-dir ./account-clear --json
|
|
340
340
|
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
|
|
342
|
-
tiangong-lca dataset maintenance apply --plan ./dataset-maintenance/maintenance-plan.json --commit
|
|
341
|
+
tiangong-lca dataset maintenance plan --scope ./maintenance-scope.json --operation merge-support-aliases --out-dir ./dataset-maintenance
|
|
342
|
+
tiangong-lca dataset maintenance apply --plan ./dataset-maintenance/maintenance-plan.json --commit --approve-plan <sha256> --confirm user@example.com
|
|
343
343
|
tiangong-lca dataset maintenance verify --plan ./dataset-maintenance/maintenance-plan.json --out-dir ./dataset-maintenance/verify
|
|
344
344
|
`.trim();
|
|
345
345
|
}
|
|
346
|
+
function renderDatasetMaintenancePlanHelp() {
|
|
347
|
+
return `Usage:
|
|
348
|
+
tiangong-lca dataset maintenance plan --scope <file> --operation <operation> --out-dir <dir> [options]
|
|
349
|
+
|
|
350
|
+
Operations:
|
|
351
|
+
delete | retire | redo-import | repair-references | merge-support-aliases
|
|
352
|
+
|
|
353
|
+
Options:
|
|
354
|
+
--scope <file> Maintenance scope manifest
|
|
355
|
+
--operation <value> Intended row-level maintenance operation
|
|
356
|
+
--out-dir <dir> Artifact directory
|
|
357
|
+
--page-size <n> Requested snapshot page size, 1-5000 (default: 1000); server caps are followed using exact counts
|
|
358
|
+
--timeout-ms <n> Request timeout in milliseconds
|
|
359
|
+
--json Print compact JSON
|
|
360
|
+
-h, --help
|
|
361
|
+
|
|
362
|
+
Outputs written under --out-dir include the immutable maintenance-plan.json, visible snapshot,
|
|
363
|
+
protected-row ledger, reference-impact report, and dry-run report.
|
|
364
|
+
`.trim();
|
|
365
|
+
}
|
|
366
|
+
function renderDatasetMaintenanceApplyHelp() {
|
|
367
|
+
return `Usage:
|
|
368
|
+
tiangong-lca dataset maintenance apply --plan <file> --commit --approve-plan <sha256> --confirm <email> [options]
|
|
369
|
+
|
|
370
|
+
Behavior:
|
|
371
|
+
Commit-only. The command rejects dry-run mode, a missing --commit flag, a plan hash mismatch,
|
|
372
|
+
or a confirmation email that does not match the current authenticated account.
|
|
373
|
+
|
|
374
|
+
Options:
|
|
375
|
+
--plan <file> Immutable maintenance-plan.json
|
|
376
|
+
--commit Execute the approved plan
|
|
377
|
+
--approve-plan <sha256> Exact SHA-256 recorded for the plan
|
|
378
|
+
--confirm <email> Current authenticated account email
|
|
379
|
+
--timeout-ms <n> Request timeout in milliseconds
|
|
380
|
+
--json Print compact JSON
|
|
381
|
+
-h, --help
|
|
382
|
+
|
|
383
|
+
Outputs include approval-record.json and commit-report.json alongside an append-only action ledger.
|
|
384
|
+
`.trim();
|
|
385
|
+
}
|
|
386
|
+
function renderDatasetMaintenanceVerifyHelp() {
|
|
387
|
+
return `Usage:
|
|
388
|
+
tiangong-lca dataset maintenance verify --plan <file> [options]
|
|
389
|
+
|
|
390
|
+
Options:
|
|
391
|
+
--plan <file> Immutable maintenance-plan.json
|
|
392
|
+
--out-dir <dir> Optional verification artifact directory
|
|
393
|
+
--page-size <n> Requested readback page size, 1-5000 (default: 1000); server caps are followed using exact counts
|
|
394
|
+
--timeout-ms <n> Request timeout in milliseconds
|
|
395
|
+
--json Print compact JSON
|
|
396
|
+
-h, --help
|
|
397
|
+
|
|
398
|
+
Outputs include readback-verify-report.json with affected-row and reference checks.
|
|
399
|
+
`.trim();
|
|
400
|
+
}
|
|
346
401
|
function renderDatasetMaintenanceClearAccountHelp() {
|
|
347
402
|
return `Usage:
|
|
348
403
|
tiangong-lca dataset maintenance clear-account [options]
|
|
@@ -355,7 +410,7 @@ Behavior:
|
|
|
355
410
|
Options:
|
|
356
411
|
--state-code <n> Optional repeatable state_code filter. Omit to clear all state codes for the current account.
|
|
357
412
|
--out-dir <dir> Artifact directory (default: ./dataset-maintenance/clear-account)
|
|
358
|
-
--page-size <n>
|
|
413
|
+
--page-size <n> Requested snapshot page size, 1-5000 (default: 1000); server caps are followed using exact counts
|
|
359
414
|
--timeout-ms <n> Request timeout in milliseconds (default: 10000)
|
|
360
415
|
--commit Execute deletion. Without this flag the command only writes a dry-run report.
|
|
361
416
|
--dry-run Explicit dry-run mode
|
|
@@ -369,6 +424,8 @@ Outputs written under --out-dir:
|
|
|
369
424
|
- approval-record.json (commit only)
|
|
370
425
|
- commit-report.json (commit only)
|
|
371
426
|
- readback-verify-report.json (commit only)
|
|
427
|
+
|
|
428
|
+
Every snapshot/readback must prove complete exact-count pagination before an artifact or delete is accepted.
|
|
372
429
|
`.trim();
|
|
373
430
|
}
|
|
374
431
|
function renderDatasetContractHelp() {
|
|
@@ -2966,6 +3023,130 @@ function parseDatasetMaintenanceClearAccountFlags(args) {
|
|
|
2966
3023
|
confirm: typeof values.confirm === 'string' ? values.confirm : null,
|
|
2967
3024
|
};
|
|
2968
3025
|
}
|
|
3026
|
+
function parseDatasetMaintenancePositiveInteger(value, flagName) {
|
|
3027
|
+
if (typeof value !== 'string') {
|
|
3028
|
+
return undefined;
|
|
3029
|
+
}
|
|
3030
|
+
if (!/^\d+$/u.test(value.trim()) || Number.parseInt(value.trim(), 10) <= 0) {
|
|
3031
|
+
throw new CliError(`${flagName} must be a positive integer.`, {
|
|
3032
|
+
code: 'DATASET_MAINTENANCE_INTEGER_INVALID',
|
|
3033
|
+
exitCode: 2,
|
|
3034
|
+
details: { flag: flagName, value },
|
|
3035
|
+
});
|
|
3036
|
+
}
|
|
3037
|
+
return Number.parseInt(value.trim(), 10);
|
|
3038
|
+
}
|
|
3039
|
+
function parseDatasetMaintenancePlanFlags(args) {
|
|
3040
|
+
let values;
|
|
3041
|
+
try {
|
|
3042
|
+
({ values } = parseArgs({
|
|
3043
|
+
args,
|
|
3044
|
+
allowPositionals: false,
|
|
3045
|
+
strict: true,
|
|
3046
|
+
options: {
|
|
3047
|
+
help: { type: 'boolean', short: 'h' },
|
|
3048
|
+
json: { type: 'boolean' },
|
|
3049
|
+
scope: { type: 'string' },
|
|
3050
|
+
operation: { type: 'string' },
|
|
3051
|
+
'out-dir': { type: 'string' },
|
|
3052
|
+
'page-size': { type: 'string' },
|
|
3053
|
+
'timeout-ms': { type: 'string' },
|
|
3054
|
+
},
|
|
3055
|
+
}));
|
|
3056
|
+
}
|
|
3057
|
+
catch (error) {
|
|
3058
|
+
throw new CliError(String(error), {
|
|
3059
|
+
code: 'INVALID_ARGS',
|
|
3060
|
+
exitCode: 2,
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
const rawOperation = typeof values.operation === 'string' ? values.operation : null;
|
|
3064
|
+
if (rawOperation !== null &&
|
|
3065
|
+
!['delete', 'retire', 'redo-import', 'repair-references', 'merge-support-aliases'].includes(rawOperation)) {
|
|
3066
|
+
throw new CliError("--operation must be 'delete', 'retire', 'redo-import', 'repair-references', or 'merge-support-aliases'.", {
|
|
3067
|
+
code: 'DATASET_MAINTENANCE_OPERATION_INVALID',
|
|
3068
|
+
exitCode: 2,
|
|
3069
|
+
details: rawOperation,
|
|
3070
|
+
});
|
|
3071
|
+
}
|
|
3072
|
+
return {
|
|
3073
|
+
help: Boolean(values.help),
|
|
3074
|
+
json: Boolean(values.json),
|
|
3075
|
+
scopePath: typeof values.scope === 'string' ? values.scope : '',
|
|
3076
|
+
operation: rawOperation,
|
|
3077
|
+
outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
|
|
3078
|
+
pageSize: parseDatasetMaintenancePositiveInteger(values['page-size'], '--page-size'),
|
|
3079
|
+
timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
function parseDatasetMaintenanceApplyFlags(args) {
|
|
3083
|
+
let values;
|
|
3084
|
+
try {
|
|
3085
|
+
({ values } = parseArgs({
|
|
3086
|
+
args,
|
|
3087
|
+
allowPositionals: false,
|
|
3088
|
+
strict: true,
|
|
3089
|
+
options: {
|
|
3090
|
+
help: { type: 'boolean', short: 'h' },
|
|
3091
|
+
json: { type: 'boolean' },
|
|
3092
|
+
plan: { type: 'string' },
|
|
3093
|
+
commit: { type: 'boolean' },
|
|
3094
|
+
'approve-plan': { type: 'string' },
|
|
3095
|
+
confirm: { type: 'string' },
|
|
3096
|
+
'timeout-ms': { type: 'string' },
|
|
3097
|
+
'dry-run': { type: 'boolean' },
|
|
3098
|
+
},
|
|
3099
|
+
}));
|
|
3100
|
+
}
|
|
3101
|
+
catch (error) {
|
|
3102
|
+
throw new CliError(String(error), {
|
|
3103
|
+
code: 'INVALID_ARGS',
|
|
3104
|
+
exitCode: 2,
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
return {
|
|
3108
|
+
help: Boolean(values.help),
|
|
3109
|
+
json: Boolean(values.json),
|
|
3110
|
+
planPath: typeof values.plan === 'string' ? values.plan : '',
|
|
3111
|
+
commit: Boolean(values.commit),
|
|
3112
|
+
approvePlan: typeof values['approve-plan'] === 'string' ? values['approve-plan'] : '',
|
|
3113
|
+
confirm: typeof values.confirm === 'string' ? values.confirm : '',
|
|
3114
|
+
timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
|
|
3115
|
+
dryRun: Boolean(values['dry-run']),
|
|
3116
|
+
};
|
|
3117
|
+
}
|
|
3118
|
+
function parseDatasetMaintenanceVerifyFlags(args) {
|
|
3119
|
+
let values;
|
|
3120
|
+
try {
|
|
3121
|
+
({ values } = parseArgs({
|
|
3122
|
+
args,
|
|
3123
|
+
allowPositionals: false,
|
|
3124
|
+
strict: true,
|
|
3125
|
+
options: {
|
|
3126
|
+
help: { type: 'boolean', short: 'h' },
|
|
3127
|
+
json: { type: 'boolean' },
|
|
3128
|
+
plan: { type: 'string' },
|
|
3129
|
+
'out-dir': { type: 'string' },
|
|
3130
|
+
'page-size': { type: 'string' },
|
|
3131
|
+
'timeout-ms': { type: 'string' },
|
|
3132
|
+
},
|
|
3133
|
+
}));
|
|
3134
|
+
}
|
|
3135
|
+
catch (error) {
|
|
3136
|
+
throw new CliError(String(error), {
|
|
3137
|
+
code: 'INVALID_ARGS',
|
|
3138
|
+
exitCode: 2,
|
|
3139
|
+
});
|
|
3140
|
+
}
|
|
3141
|
+
return {
|
|
3142
|
+
help: Boolean(values.help),
|
|
3143
|
+
json: Boolean(values.json),
|
|
3144
|
+
planPath: typeof values.plan === 'string' ? values.plan : '',
|
|
3145
|
+
outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : undefined,
|
|
3146
|
+
pageSize: parseDatasetMaintenancePositiveInteger(values['page-size'], '--page-size'),
|
|
3147
|
+
timeoutMs: parseDatasetMaintenancePositiveInteger(values['timeout-ms'], '--timeout-ms'),
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
2969
3150
|
function parseIdentityPreflightFlags(args) {
|
|
2970
3151
|
let values;
|
|
2971
3152
|
try {
|
|
@@ -4711,6 +4892,9 @@ export async function executeCli(argv, deps) {
|
|
|
4711
4892
|
const datasetClassificationAuditImpl = deps.runDatasetClassificationAuditImpl ?? runDatasetClassificationAudit;
|
|
4712
4893
|
const datasetClassificationApplyImpl = deps.runDatasetClassificationApplyImpl ?? runDatasetClassificationApply;
|
|
4713
4894
|
const datasetMaintenanceClearAccountImpl = deps.runDatasetMaintenanceClearAccountImpl ?? runDatasetMaintenanceClearAccount;
|
|
4895
|
+
const datasetMaintenancePlanImpl = deps.runDatasetMaintenancePlanImpl ?? runDatasetMaintenancePlan;
|
|
4896
|
+
const datasetMaintenanceApplyImpl = deps.runDatasetMaintenanceApplyImpl ?? runDatasetMaintenanceApply;
|
|
4897
|
+
const datasetMaintenanceVerifyImpl = deps.runDatasetMaintenanceVerifyImpl ?? runDatasetMaintenanceVerify;
|
|
4714
4898
|
const datasetSourceUploadAttachmentsImpl = deps.runDatasetSourceUploadAttachmentsImpl ?? runDatasetSourceUploadAttachments;
|
|
4715
4899
|
if (flags.version) {
|
|
4716
4900
|
return { exitCode: 0, stdout: `${loadCliPackageVersion(import.meta.url)}\n`, stderr: '' };
|
|
@@ -5320,13 +5504,141 @@ export async function executeCli(argv, deps) {
|
|
|
5320
5504
|
stderr: '',
|
|
5321
5505
|
};
|
|
5322
5506
|
}
|
|
5323
|
-
if (
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5507
|
+
if (action === 'plan') {
|
|
5508
|
+
const datasetFlags = parseDatasetMaintenancePlanFlags(commandArgs.slice(1));
|
|
5509
|
+
if (datasetFlags.help) {
|
|
5510
|
+
return {
|
|
5511
|
+
exitCode: 0,
|
|
5512
|
+
stdout: `${renderDatasetMaintenancePlanHelp()}\n`,
|
|
5513
|
+
stderr: '',
|
|
5514
|
+
};
|
|
5515
|
+
}
|
|
5516
|
+
if (!datasetFlags.scopePath) {
|
|
5517
|
+
throw new CliError('dataset maintenance plan requires --scope.', {
|
|
5518
|
+
code: 'DATASET_MAINTENANCE_SCOPE_REQUIRED',
|
|
5519
|
+
exitCode: 2,
|
|
5520
|
+
});
|
|
5521
|
+
}
|
|
5522
|
+
if (!datasetFlags.operation) {
|
|
5523
|
+
throw new CliError('dataset maintenance plan requires --operation.', {
|
|
5524
|
+
code: 'DATASET_MAINTENANCE_OPERATION_REQUIRED',
|
|
5525
|
+
exitCode: 2,
|
|
5526
|
+
});
|
|
5527
|
+
}
|
|
5528
|
+
if (!datasetFlags.outDir) {
|
|
5529
|
+
throw new CliError('dataset maintenance plan requires --out-dir.', {
|
|
5530
|
+
code: 'DATASET_MAINTENANCE_OUT_DIR_REQUIRED',
|
|
5531
|
+
exitCode: 2,
|
|
5532
|
+
});
|
|
5533
|
+
}
|
|
5534
|
+
const report = await datasetMaintenancePlanImpl({
|
|
5535
|
+
scopePath: datasetFlags.scopePath,
|
|
5536
|
+
operation: datasetFlags.operation,
|
|
5537
|
+
outDir: datasetFlags.outDir,
|
|
5538
|
+
pageSize: datasetFlags.pageSize,
|
|
5539
|
+
timeoutMs: datasetFlags.timeoutMs,
|
|
5540
|
+
env: deps.env,
|
|
5541
|
+
fetchImpl: deps.fetchImpl,
|
|
5327
5542
|
});
|
|
5543
|
+
return {
|
|
5544
|
+
exitCode: report.status === 'blocked' ? 1 : 0,
|
|
5545
|
+
stdout: stringifyJson(report, datasetFlags.json),
|
|
5546
|
+
stderr: '',
|
|
5547
|
+
};
|
|
5328
5548
|
}
|
|
5329
|
-
|
|
5549
|
+
if (action === 'apply') {
|
|
5550
|
+
const datasetFlags = parseDatasetMaintenanceApplyFlags(commandArgs.slice(1));
|
|
5551
|
+
if (datasetFlags.help) {
|
|
5552
|
+
return {
|
|
5553
|
+
exitCode: 0,
|
|
5554
|
+
stdout: `${renderDatasetMaintenanceApplyHelp()}\n`,
|
|
5555
|
+
stderr: '',
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
if (!datasetFlags.planPath) {
|
|
5559
|
+
throw new CliError('dataset maintenance apply requires --plan.', {
|
|
5560
|
+
code: 'DATASET_MAINTENANCE_PLAN_REQUIRED',
|
|
5561
|
+
exitCode: 2,
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5564
|
+
if (datasetFlags.commit && datasetFlags.dryRun) {
|
|
5565
|
+
throw new CliError('Cannot pass both --commit and --dry-run.', {
|
|
5566
|
+
code: 'DATASET_MAINTENANCE_APPLY_MODE_CONFLICT',
|
|
5567
|
+
exitCode: 2,
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
if (datasetFlags.dryRun) {
|
|
5571
|
+
throw new CliError('dataset maintenance apply is commit-only; use dataset maintenance plan for a dry run.', {
|
|
5572
|
+
code: 'DATASET_MAINTENANCE_APPLY_COMMIT_ONLY',
|
|
5573
|
+
exitCode: 2,
|
|
5574
|
+
});
|
|
5575
|
+
}
|
|
5576
|
+
if (!datasetFlags.commit) {
|
|
5577
|
+
throw new CliError('dataset maintenance apply requires --commit.', {
|
|
5578
|
+
code: 'DATASET_MAINTENANCE_APPLY_COMMIT_REQUIRED',
|
|
5579
|
+
exitCode: 2,
|
|
5580
|
+
});
|
|
5581
|
+
}
|
|
5582
|
+
if (!datasetFlags.approvePlan) {
|
|
5583
|
+
throw new CliError('dataset maintenance apply requires --approve-plan <sha256>.', {
|
|
5584
|
+
code: 'DATASET_MAINTENANCE_APPROVAL_REQUIRED',
|
|
5585
|
+
exitCode: 2,
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5588
|
+
if (!datasetFlags.confirm) {
|
|
5589
|
+
throw new CliError('dataset maintenance apply requires --confirm <email>.', {
|
|
5590
|
+
code: 'DATASET_MAINTENANCE_CONFIRM_REQUIRED',
|
|
5591
|
+
exitCode: 2,
|
|
5592
|
+
});
|
|
5593
|
+
}
|
|
5594
|
+
const report = await datasetMaintenanceApplyImpl({
|
|
5595
|
+
planPath: datasetFlags.planPath,
|
|
5596
|
+
commit: datasetFlags.commit,
|
|
5597
|
+
approvePlan: datasetFlags.approvePlan,
|
|
5598
|
+
confirm: datasetFlags.confirm,
|
|
5599
|
+
timeoutMs: datasetFlags.timeoutMs,
|
|
5600
|
+
env: deps.env,
|
|
5601
|
+
fetchImpl: deps.fetchImpl,
|
|
5602
|
+
});
|
|
5603
|
+
return {
|
|
5604
|
+
exitCode: report.status === 'completed_with_failures' ? 1 : 0,
|
|
5605
|
+
stdout: stringifyJson(report, datasetFlags.json),
|
|
5606
|
+
stderr: '',
|
|
5607
|
+
};
|
|
5608
|
+
}
|
|
5609
|
+
if (action === 'verify') {
|
|
5610
|
+
const datasetFlags = parseDatasetMaintenanceVerifyFlags(commandArgs.slice(1));
|
|
5611
|
+
if (datasetFlags.help) {
|
|
5612
|
+
return {
|
|
5613
|
+
exitCode: 0,
|
|
5614
|
+
stdout: `${renderDatasetMaintenanceVerifyHelp()}\n`,
|
|
5615
|
+
stderr: '',
|
|
5616
|
+
};
|
|
5617
|
+
}
|
|
5618
|
+
if (!datasetFlags.planPath) {
|
|
5619
|
+
throw new CliError('dataset maintenance verify requires --plan.', {
|
|
5620
|
+
code: 'DATASET_MAINTENANCE_PLAN_REQUIRED',
|
|
5621
|
+
exitCode: 2,
|
|
5622
|
+
});
|
|
5623
|
+
}
|
|
5624
|
+
const report = await datasetMaintenanceVerifyImpl({
|
|
5625
|
+
planPath: datasetFlags.planPath,
|
|
5626
|
+
outDir: datasetFlags.outDir,
|
|
5627
|
+
pageSize: datasetFlags.pageSize,
|
|
5628
|
+
timeoutMs: datasetFlags.timeoutMs,
|
|
5629
|
+
env: deps.env,
|
|
5630
|
+
fetchImpl: deps.fetchImpl,
|
|
5631
|
+
});
|
|
5632
|
+
return {
|
|
5633
|
+
exitCode: report.status === 'failed' ? 1 : 0,
|
|
5634
|
+
stdout: stringifyJson(report, datasetFlags.json),
|
|
5635
|
+
stderr: '',
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
throw new CliError("dataset maintenance action must be 'clear-account', 'plan', 'apply', or 'verify'.", {
|
|
5639
|
+
code: 'DATASET_MAINTENANCE_ACTION_INVALID',
|
|
5640
|
+
exitCode: 2,
|
|
5641
|
+
});
|
|
5330
5642
|
}
|
|
5331
5643
|
if (command === 'lifecyclemodel' && !subcommand) {
|
|
5332
5644
|
return { exitCode: 0, stdout: `${renderLifecyclemodelHelp()}\n`, stderr: '' };
|