@tiangong-lca/cli 0.0.18 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,8 +17,8 @@ checkPaths:
17
17
  - bin/**
18
18
  - src/cli.ts
19
19
  - src/main.ts
20
- lastReviewedAt: 2026-06-21
21
- lastReviewedCommit: 44fa7e3d01a9cb144184b0047770f82f0006cace
20
+ lastReviewedAt: 2026-06-29
21
+ lastReviewedCommit: 695e6d6fe718cb92d499f3ce8be2dc24c3f6ce29
22
22
  ---
23
23
 
24
24
  # TianGong LCA CLI
@@ -1414,18 +1414,38 @@
1414
1414
  "description": "container tag for the specification of allocations if process has more than one reference product. Use only for multifunctional processes.",
1415
1415
  "properties": {
1416
1416
  "allocation": {
1417
- "type": "object",
1418
1417
  "description": "specifies one allocation of this exchange (see the attributes of this tag below)",
1419
- "properties": {
1420
- "@internalReferenceToCoProduct": {
1421
- "$ref": "tidas_data_types.json#/$defs/Int6",
1422
- "description": "Reference to one of the co-products. The applied allocation approach(es), details and and explanations are documented in the fields \"LCI method approaches\" and \"Deviations from LCI method approaches / explanations\". [Notes: Applicable only to multifunctional processes. The documented allocated fractions are only applicable when using the data set for attributional modelling and are to be ignored for consequential modeling.]"
1418
+ "anyOf": [
1419
+ {
1420
+ "type": "object",
1421
+ "properties": {
1422
+ "@internalReferenceToCoProduct": {
1423
+ "$ref": "tidas_data_types.json#/$defs/Int6",
1424
+ "description": "Reference to one of the co-products. The applied allocation approach(es), details and and explanations are documented in the fields \"LCI method approaches\" and \"Deviations from LCI method approaches / explanations\". [Notes: Applicable only to multifunctional processes. The documented allocated fractions are only applicable when using the data set for attributional modelling and are to be ignored for consequential modeling.]"
1425
+ },
1426
+ "@allocatedFraction": {
1427
+ "$ref": "tidas_data_types.json#/$defs/Perc",
1428
+ "description": "Fraction (expressed in %) of this Input or Output flow that is foreseen to be allocated to this co-product (recommended allocation). The numbers across the co-products should sum up to 100%."
1429
+ }
1430
+ }
1423
1431
  },
1424
- "@allocatedFraction": {
1425
- "$ref": "tidas_data_types.json#/$defs/Perc",
1426
- "description": "Fraction (expressed in %) of this Input or Output flow that is foreseen to be allocated to this co-product (recommended allocation). The numbers across the co-products should sum up to 100%."
1432
+ {
1433
+ "type": "array",
1434
+ "items": {
1435
+ "type": "object",
1436
+ "properties": {
1437
+ "@internalReferenceToCoProduct": {
1438
+ "$ref": "tidas_data_types.json#/$defs/Int6",
1439
+ "description": "Reference to one of the co-products. The applied allocation approach(es), details and and explanations are documented in the fields \"LCI method approaches\" and \"Deviations from LCI method approaches / explanations\". [Notes: Applicable only to multifunctional processes. The documented allocated fractions are only applicable when using the data set for attributional modelling and are to be ignored for consequential modeling.]"
1440
+ },
1441
+ "@allocatedFraction": {
1442
+ "$ref": "tidas_data_types.json#/$defs/Perc",
1443
+ "description": "Fraction (expressed in %) of this Input or Output flow that is foreseen to be allocated to this co-product (recommended allocation). The numbers across the co-products should sum up to 100%."
1444
+ }
1445
+ }
1446
+ }
1427
1447
  }
1428
- }
1448
+ ]
1429
1449
  }
1430
1450
  }
1431
1451
  },
package/dist/src/cli.js CHANGED
@@ -54,6 +54,7 @@ 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 { runDatasetSourceUploadAttachments, } from './lib/dataset-source-upload-attachments.js';
57
58
  function renderMainHelp(dotEnvStatus) {
58
59
  return `TianGong LCA CLI
59
60
 
@@ -73,7 +74,7 @@ Implemented Commands:
73
74
  doctor show environment diagnostics
74
75
  search flow | process | lifecyclemodel
75
76
  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
76
- dataset contract get | context-pack | classification children/path/audit/apply | curation-queue build/next/verify | import-lca convert | author | patch apply | save-draft | validate | verify-remote | bilingual extract/apply/validate | evidence-search plan/run | references rewrite/refresh-remote | maintenance clear-account
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
77
78
  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
78
79
  lifecyclemodel auto-build | validate-build | publish-build | save-draft | graph | build-resulting-process | publish-resulting-process | orchestrate
79
80
  qa process | flow | lifecyclemodel
@@ -268,6 +269,7 @@ Implemented Subcommands:
268
269
  author Extract source evidence and prepare TIDAS context packs for AI authoring
269
270
  patch apply Apply AI-authored structured dataset patches deterministically
270
271
  save-draft Save contact/source/support or other canonical dataset rows through the platform dataset command path
272
+ source upload-attachments Upload source referenceToDigitalFile binaries to storage and rewrite their @uri
271
273
  validate Validate local flow / process / lifecyclemodel rows with the TIDAS SDK
272
274
  verify-remote Verify dataset roots and TIDAS references against remote published versions
273
275
  bilingual extract Extract bilingual translation units from local rows
@@ -518,7 +520,7 @@ function renderDatasetImportLcaHelp() {
518
520
  Options:
519
521
  --input <path> Source file, directory, or package to import
520
522
  --output-dir <dir> Output directory for generated package and reports
521
- --from-format <format> auto, ecospold1, ecospold2, openlca-jsonld, openlca-process-xlsx, simapro-csv
523
+ --from-format <format> auto, ecospold1, ecospold2, openlca-jsonld, openlca-process-xlsx, simapro-csv, ilcd
522
524
  --target <target> tidas | ilcd | both (default: tidas)
523
525
  --report <file> Conversion report path (default: <output-dir>/conversion-report.json)
524
526
  --mapping-dir <dir> Optional custom mapping/reference data directory
@@ -644,6 +646,33 @@ Contract:
644
646
  Process and lifecyclemodel imports may still use their dedicated save-draft commands when the workflow needs their specialized reports.
645
647
  `.trim();
646
648
  }
649
+ function renderDatasetSourceUploadAttachmentsHelp() {
650
+ return `Usage:
651
+ tiangong-lca dataset source upload-attachments --input <file|dir> --external-docs-dir <dir> [options]
652
+
653
+ Uploads the binary documents referenced by source datasets via referenceToDigitalFile
654
+ (e.g. "../external_docs/pef_method.pdf") to a Supabase Storage bucket using the current
655
+ authenticated account session, then rewrites each source's referenceToDigitalFile @uri to
656
+ "../<bucket>/<key>". Plain http(s) URIs are left untouched; backslash paths and filename
657
+ case differences are normalized; each physical file is uploaded once (deduped).
658
+
659
+ Options:
660
+ --input <file|dir> Source TIDAS rows (.jsonl, .json, or a directory of *.json)
661
+ --external-docs-dir <dir> Directory holding the referenced binaries
662
+ --bucket <name> Storage bucket (default: external_docs)
663
+ --out-dir <dir> Artifact directory
664
+ --commit Upload binaries (default: dry-run plan only)
665
+ --dry-run Plan without uploading (default)
666
+ --verify After upload, create a signed URL to confirm each object resolves
667
+ --timeout-ms <n> Per-request timeout (default: 30000)
668
+ --json Print compact JSON
669
+ -h, --help
670
+
671
+ Outputs written under --out-dir:
672
+ - attachments-report.json
673
+ - rewritten-sources.jsonl
674
+ `.trim();
675
+ }
647
676
  function renderDatasetValidateHelp() {
648
677
  return `Usage:
649
678
  tiangong-lca dataset validate --input <file> [options]
@@ -2096,6 +2125,62 @@ function parseDatasetSaveDraftFlags(args) {
2096
2125
  allowReferenceOnlySupport: Boolean(values['allow-account-local-support']),
2097
2126
  };
2098
2127
  }
2128
+ function parseDatasetSourceUploadAttachmentsFlags(args) {
2129
+ let values;
2130
+ try {
2131
+ ({ values } = parseArgs({
2132
+ args,
2133
+ allowPositionals: false,
2134
+ strict: true,
2135
+ options: {
2136
+ help: { type: 'boolean', short: 'h' },
2137
+ json: { type: 'boolean' },
2138
+ input: { type: 'string' },
2139
+ 'external-docs-dir': { type: 'string' },
2140
+ bucket: { type: 'string' },
2141
+ 'out-dir': { type: 'string' },
2142
+ commit: { type: 'boolean' },
2143
+ 'dry-run': { type: 'boolean' },
2144
+ verify: { type: 'boolean' },
2145
+ 'timeout-ms': { type: 'string' },
2146
+ },
2147
+ }));
2148
+ }
2149
+ catch (error) {
2150
+ throw new CliError(String(error), {
2151
+ code: 'INVALID_ARGS',
2152
+ exitCode: 2,
2153
+ });
2154
+ }
2155
+ if (values.commit && values['dry-run']) {
2156
+ throw new CliError('Cannot pass both --commit and --dry-run.', {
2157
+ code: 'INVALID_DATASET_SOURCE_UPLOAD_MODE',
2158
+ exitCode: 2,
2159
+ });
2160
+ }
2161
+ let timeoutMs;
2162
+ if (typeof values['timeout-ms'] === 'string') {
2163
+ const parsed = Number.parseInt(values['timeout-ms'], 10);
2164
+ if (!Number.isInteger(parsed)) {
2165
+ throw new CliError('--timeout-ms must be an integer.', {
2166
+ code: 'INVALID_ARGS',
2167
+ exitCode: 2,
2168
+ });
2169
+ }
2170
+ timeoutMs = parsed;
2171
+ }
2172
+ return {
2173
+ help: Boolean(values.help),
2174
+ json: Boolean(values.json),
2175
+ inputPath: typeof values.input === 'string' ? values.input : '',
2176
+ externalDocsDir: typeof values['external-docs-dir'] === 'string' ? values['external-docs-dir'] : '',
2177
+ bucket: typeof values.bucket === 'string' ? values.bucket : null,
2178
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2179
+ commit: Boolean(values.commit),
2180
+ verify: Boolean(values.verify),
2181
+ timeoutMs,
2182
+ };
2183
+ }
2099
2184
  function parseDatasetContractFlags(args) {
2100
2185
  let values;
2101
2186
  try {
@@ -4626,6 +4711,7 @@ export async function executeCli(argv, deps) {
4626
4711
  const datasetClassificationAuditImpl = deps.runDatasetClassificationAuditImpl ?? runDatasetClassificationAudit;
4627
4712
  const datasetClassificationApplyImpl = deps.runDatasetClassificationApplyImpl ?? runDatasetClassificationApply;
4628
4713
  const datasetMaintenanceClearAccountImpl = deps.runDatasetMaintenanceClearAccountImpl ?? runDatasetMaintenanceClearAccount;
4714
+ const datasetSourceUploadAttachmentsImpl = deps.runDatasetSourceUploadAttachmentsImpl ?? runDatasetSourceUploadAttachments;
4629
4715
  if (flags.version) {
4630
4716
  return { exitCode: 0, stdout: `${loadCliPackageVersion(import.meta.url)}\n`, stderr: '' };
4631
4717
  }
@@ -4947,6 +5033,58 @@ export async function executeCli(argv, deps) {
4947
5033
  stderr: '',
4948
5034
  };
4949
5035
  }
5036
+ if (command === 'dataset' && subcommand === 'source') {
5037
+ const action = commandArgs[0] ?? '';
5038
+ if (!action || action === '--help' || action === '-h') {
5039
+ return {
5040
+ exitCode: 0,
5041
+ stdout: `${renderDatasetSourceUploadAttachmentsHelp()}\n`,
5042
+ stderr: '',
5043
+ };
5044
+ }
5045
+ if (action !== 'upload-attachments') {
5046
+ throw new CliError("dataset source action must be 'upload-attachments'.", {
5047
+ code: 'DATASET_SOURCE_ACTION_INVALID',
5048
+ exitCode: 2,
5049
+ });
5050
+ }
5051
+ const datasetFlags = parseDatasetSourceUploadAttachmentsFlags(commandArgs.slice(1));
5052
+ if (datasetFlags.help) {
5053
+ return {
5054
+ exitCode: 0,
5055
+ stdout: `${renderDatasetSourceUploadAttachmentsHelp()}\n`,
5056
+ stderr: '',
5057
+ };
5058
+ }
5059
+ if (!datasetFlags.inputPath) {
5060
+ throw new CliError('dataset source upload-attachments requires --input.', {
5061
+ code: 'DATASET_SOURCE_UPLOAD_INPUT_REQUIRED',
5062
+ exitCode: 2,
5063
+ });
5064
+ }
5065
+ if (!datasetFlags.externalDocsDir) {
5066
+ throw new CliError('dataset source upload-attachments requires --external-docs-dir.', {
5067
+ code: 'DATASET_SOURCE_UPLOAD_EXTERNAL_DOCS_DIR_REQUIRED',
5068
+ exitCode: 2,
5069
+ });
5070
+ }
5071
+ const report = await datasetSourceUploadAttachmentsImpl({
5072
+ inputPath: datasetFlags.inputPath,
5073
+ externalDocsDir: datasetFlags.externalDocsDir,
5074
+ bucket: datasetFlags.bucket,
5075
+ outDir: datasetFlags.outDir,
5076
+ commit: datasetFlags.commit,
5077
+ verify: datasetFlags.verify,
5078
+ timeoutMs: datasetFlags.timeoutMs,
5079
+ env: deps.env,
5080
+ fetchImpl: deps.fetchImpl,
5081
+ });
5082
+ return {
5083
+ exitCode: report.status === 'completed_with_failures' ? 1 : 0,
5084
+ stdout: stringifyJson(report, datasetFlags.json),
5085
+ stderr: '',
5086
+ };
5087
+ }
4950
5088
  if (command === 'dataset' && subcommand === 'save-draft') {
4951
5089
  const datasetFlags = parseDatasetSaveDraftFlags(commandArgs);
4952
5090
  if (datasetFlags.help) {