@tiangong-lca/cli 0.0.1

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 (96) hide show
  1. package/README.md +368 -0
  2. package/bin/tiangong.d.ts +1 -0
  3. package/bin/tiangong.js +32 -0
  4. package/dist/scripts/assert-full-coverage.js +77 -0
  5. package/dist/scripts/assert-full-coverage.js.map +1 -0
  6. package/dist/src/cli.js +2740 -0
  7. package/dist/src/cli.js.map +1 -0
  8. package/dist/src/lib/artifacts.js +86 -0
  9. package/dist/src/lib/artifacts.js.map +1 -0
  10. package/dist/src/lib/dotenv.js +36 -0
  11. package/dist/src/lib/dotenv.js.map +1 -0
  12. package/dist/src/lib/env.js +139 -0
  13. package/dist/src/lib/env.js.map +1 -0
  14. package/dist/src/lib/errors.js +38 -0
  15. package/dist/src/lib/errors.js.map +1 -0
  16. package/dist/src/lib/flow-build-alias-map.js +299 -0
  17. package/dist/src/lib/flow-build-alias-map.js.map +1 -0
  18. package/dist/src/lib/flow-get.js +81 -0
  19. package/dist/src/lib/flow-get.js.map +1 -0
  20. package/dist/src/lib/flow-governance.js +164 -0
  21. package/dist/src/lib/flow-governance.js.map +1 -0
  22. package/dist/src/lib/flow-list.js +187 -0
  23. package/dist/src/lib/flow-list.js.map +1 -0
  24. package/dist/src/lib/flow-publish-reviewed-data.js +808 -0
  25. package/dist/src/lib/flow-publish-reviewed-data.js.map +1 -0
  26. package/dist/src/lib/flow-publish-version.js +541 -0
  27. package/dist/src/lib/flow-publish-version.js.map +1 -0
  28. package/dist/src/lib/flow-read.js +310 -0
  29. package/dist/src/lib/flow-read.js.map +1 -0
  30. package/dist/src/lib/flow-regen-product.js +1396 -0
  31. package/dist/src/lib/flow-regen-product.js.map +1 -0
  32. package/dist/src/lib/flow-remediate.js +821 -0
  33. package/dist/src/lib/flow-remediate.js.map +1 -0
  34. package/dist/src/lib/http.js +45 -0
  35. package/dist/src/lib/http.js.map +1 -0
  36. package/dist/src/lib/io.js +30 -0
  37. package/dist/src/lib/io.js.map +1 -0
  38. package/dist/src/lib/kb-search.js +83 -0
  39. package/dist/src/lib/kb-search.js.map +1 -0
  40. package/dist/src/lib/lifecyclemodel-auto-build.js +1328 -0
  41. package/dist/src/lib/lifecyclemodel-auto-build.js.map +1 -0
  42. package/dist/src/lib/lifecyclemodel-orchestrate.js +1556 -0
  43. package/dist/src/lib/lifecyclemodel-orchestrate.js.map +1 -0
  44. package/dist/src/lib/lifecyclemodel-publish-build.js +296 -0
  45. package/dist/src/lib/lifecyclemodel-publish-build.js.map +1 -0
  46. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js +101 -0
  47. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js.map +1 -0
  48. package/dist/src/lib/lifecyclemodel-resulting-process.js +1152 -0
  49. package/dist/src/lib/lifecyclemodel-resulting-process.js.map +1 -0
  50. package/dist/src/lib/lifecyclemodel-validate-build.js +238 -0
  51. package/dist/src/lib/lifecyclemodel-validate-build.js.map +1 -0
  52. package/dist/src/lib/llm.js +383 -0
  53. package/dist/src/lib/llm.js.map +1 -0
  54. package/dist/src/lib/process-auto-build.js +717 -0
  55. package/dist/src/lib/process-auto-build.js.map +1 -0
  56. package/dist/src/lib/process-batch-build.js +409 -0
  57. package/dist/src/lib/process-batch-build.js.map +1 -0
  58. package/dist/src/lib/process-get.js +66 -0
  59. package/dist/src/lib/process-get.js.map +1 -0
  60. package/dist/src/lib/process-publish-build.js +451 -0
  61. package/dist/src/lib/process-publish-build.js.map +1 -0
  62. package/dist/src/lib/process-resume-build.js +364 -0
  63. package/dist/src/lib/process-resume-build.js.map +1 -0
  64. package/dist/src/lib/publish.js +562 -0
  65. package/dist/src/lib/publish.js.map +1 -0
  66. package/dist/src/lib/remote.js +88 -0
  67. package/dist/src/lib/remote.js.map +1 -0
  68. package/dist/src/lib/review-flow.js +1110 -0
  69. package/dist/src/lib/review-flow.js.map +1 -0
  70. package/dist/src/lib/review-lifecyclemodel.js +727 -0
  71. package/dist/src/lib/review-lifecyclemodel.js.map +1 -0
  72. package/dist/src/lib/review-process.js +702 -0
  73. package/dist/src/lib/review-process.js.map +1 -0
  74. package/dist/src/lib/run.js +106 -0
  75. package/dist/src/lib/run.js.map +1 -0
  76. package/dist/src/lib/state-lock.js +150 -0
  77. package/dist/src/lib/state-lock.js.map +1 -0
  78. package/dist/src/lib/supabase-client.js +269 -0
  79. package/dist/src/lib/supabase-client.js.map +1 -0
  80. package/dist/src/lib/supabase-json-ordered-write.js +186 -0
  81. package/dist/src/lib/supabase-json-ordered-write.js.map +1 -0
  82. package/dist/src/lib/supabase-rest.js +138 -0
  83. package/dist/src/lib/supabase-rest.js.map +1 -0
  84. package/dist/src/lib/supabase-session.js +424 -0
  85. package/dist/src/lib/supabase-session.js.map +1 -0
  86. package/dist/src/lib/tidas-sdk-package-validator.js +493 -0
  87. package/dist/src/lib/tidas-sdk-package-validator.js.map +1 -0
  88. package/dist/src/lib/unstructured.js +203 -0
  89. package/dist/src/lib/unstructured.js.map +1 -0
  90. package/dist/src/lib/user-api-key.js +75 -0
  91. package/dist/src/lib/user-api-key.js.map +1 -0
  92. package/dist/src/lib/validation.js +191 -0
  93. package/dist/src/lib/validation.js.map +1 -0
  94. package/dist/src/main.js +38 -0
  95. package/dist/src/main.js.map +1 -0
  96. package/package.json +81 -0
@@ -0,0 +1,717 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { writeJsonArtifact } from './artifacts.js';
4
+ import { CliError } from './errors.js';
5
+ import { readJsonInput } from './io.js';
6
+ import { buildRunManifest, buildUtcTimestamp, ensureRunLayout, sanitizeRunToken, writeLatestRunId, } from './run.js';
7
+ import { withStateFileLock } from './state-lock.js';
8
+ const DEFAULT_SOURCE_POLICY = {
9
+ step1_route: {
10
+ preferred: ['user_bundle', 'kb_bundle'],
11
+ fallback: 'expert_judgement',
12
+ },
13
+ step2_process_split: {
14
+ preferred: ['user_bundle.process_split', 'si_bundle', 'kb_bundle.process_split'],
15
+ fallback: 'expert_judgement',
16
+ },
17
+ step3b_exchange_values: {
18
+ preferred: ['user_bundle.exchange_values', 'si_bundle', 'kb_bundle.exchange_values'],
19
+ require_numeric_evidence: true,
20
+ allow_estimation: true,
21
+ },
22
+ };
23
+ const PROCESS_AUTO_BUILD_STAGES = [
24
+ {
25
+ id: '01_route',
26
+ title: 'Route planning',
27
+ output: 'stage_outputs/01_route/route_plan.json',
28
+ createsCandidatesDir: true,
29
+ modules: ['kb-search', 'llm'],
30
+ },
31
+ {
32
+ id: '02_process_split',
33
+ title: 'Process chain split',
34
+ output: 'stage_outputs/02_process_split/process_chain.json',
35
+ createsCandidatesDir: true,
36
+ modules: ['llm'],
37
+ },
38
+ {
39
+ id: '03_exchange_plan',
40
+ title: 'Exchange planning',
41
+ output: 'stage_outputs/03_exchange_plan/exchange_plan.json',
42
+ createsCandidatesDir: false,
43
+ modules: ['llm'],
44
+ },
45
+ {
46
+ id: '04_exchange_values',
47
+ title: 'Exchange value extraction',
48
+ output: 'stage_outputs/04_exchange_values/exchange_values.json',
49
+ createsCandidatesDir: true,
50
+ modules: ['unstructured', 'kb-search', 'llm'],
51
+ },
52
+ {
53
+ id: '05_chain_review',
54
+ title: 'Chain review',
55
+ output: 'stage_outputs/05_chain_review/chain_review.json',
56
+ createsCandidatesDir: false,
57
+ modules: ['llm'],
58
+ },
59
+ {
60
+ id: '06_flow_match',
61
+ title: 'Flow matching',
62
+ output: 'stage_outputs/06_flow_match/flow_match_bundle.json',
63
+ createsCandidatesDir: false,
64
+ modules: ['search:flow', 'llm'],
65
+ },
66
+ {
67
+ id: '07_source_build',
68
+ title: 'Source dataset build',
69
+ output: 'stage_outputs/07_source_build/source_bundle.json',
70
+ createsCandidatesDir: false,
71
+ modules: ['validation'],
72
+ },
73
+ {
74
+ id: '08_process_build',
75
+ title: 'Process dataset build',
76
+ output: 'stage_outputs/08_process_build/process_bundle.json',
77
+ createsCandidatesDir: false,
78
+ modules: ['validation', 'llm'],
79
+ },
80
+ {
81
+ id: '09_qa',
82
+ title: 'QA and balance review',
83
+ output: 'stage_outputs/09_qa/qa_bundle.json',
84
+ createsCandidatesDir: false,
85
+ modules: ['validation', 'llm'],
86
+ },
87
+ {
88
+ id: '10_publish',
89
+ title: 'Publish handoff',
90
+ output: 'stage_outputs/10_publish/publish_bundle.json',
91
+ createsCandidatesDir: false,
92
+ modules: ['publish'],
93
+ },
94
+ ];
95
+ function isRecord(value) {
96
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
97
+ }
98
+ function nonEmptyString(value) {
99
+ if (typeof value !== 'string') {
100
+ return null;
101
+ }
102
+ const normalized = value.trim();
103
+ return normalized || null;
104
+ }
105
+ function extractText(value) {
106
+ const direct = nonEmptyString(value);
107
+ if (direct) {
108
+ return direct;
109
+ }
110
+ if (Array.isArray(value)) {
111
+ for (const item of value) {
112
+ const extracted = extractText(item);
113
+ if (extracted) {
114
+ return extracted;
115
+ }
116
+ }
117
+ return null;
118
+ }
119
+ if (!isRecord(value)) {
120
+ return null;
121
+ }
122
+ if ('#text' in value) {
123
+ return nonEmptyString(value['#text']);
124
+ }
125
+ return null;
126
+ }
127
+ function requiredRequestObject(input) {
128
+ if (!isRecord(input)) {
129
+ throw new CliError('process auto-build request must be a JSON object.', {
130
+ code: 'PROCESS_AUTO_BUILD_REQUEST_INVALID',
131
+ exitCode: 2,
132
+ });
133
+ }
134
+ return input;
135
+ }
136
+ function requiredRequestString(input, key) {
137
+ const value = nonEmptyString(input[key]);
138
+ if (!value) {
139
+ throw new CliError(`process auto-build request is missing '${key}'.`, {
140
+ code: 'PROCESS_AUTO_BUILD_REQUEST_INVALID',
141
+ exitCode: 2,
142
+ details: { key },
143
+ });
144
+ }
145
+ return value;
146
+ }
147
+ function normalizeOperation(value) {
148
+ const normalized = nonEmptyString(value);
149
+ if (!normalized) {
150
+ return 'produce';
151
+ }
152
+ if (normalized === 'produce' || normalized === 'treat') {
153
+ return normalized;
154
+ }
155
+ throw new CliError("process auto-build operation must be 'produce' or 'treat'.", {
156
+ code: 'PROCESS_AUTO_BUILD_OPERATION_INVALID',
157
+ exitCode: 2,
158
+ details: normalized,
159
+ });
160
+ }
161
+ function normalizePreferredList(value, fallback) {
162
+ if (!Array.isArray(value)) {
163
+ return [...fallback];
164
+ }
165
+ const normalized = value
166
+ .map((entry) => nonEmptyString(entry))
167
+ .filter((entry) => Boolean(entry));
168
+ return normalized.length ? normalized : [...fallback];
169
+ }
170
+ function normalizeBoolean(value, fallback) {
171
+ return typeof value === 'boolean' ? value : fallback;
172
+ }
173
+ function buildDefaultSourcePolicy() {
174
+ return {
175
+ step1_route: {
176
+ preferred: [...DEFAULT_SOURCE_POLICY.step1_route.preferred],
177
+ fallback: DEFAULT_SOURCE_POLICY.step1_route.fallback,
178
+ },
179
+ step2_process_split: {
180
+ preferred: [...DEFAULT_SOURCE_POLICY.step2_process_split.preferred],
181
+ fallback: DEFAULT_SOURCE_POLICY.step2_process_split.fallback,
182
+ },
183
+ step3b_exchange_values: {
184
+ preferred: [...DEFAULT_SOURCE_POLICY.step3b_exchange_values.preferred],
185
+ require_numeric_evidence: DEFAULT_SOURCE_POLICY.step3b_exchange_values.require_numeric_evidence,
186
+ allow_estimation: DEFAULT_SOURCE_POLICY.step3b_exchange_values.allow_estimation,
187
+ },
188
+ };
189
+ }
190
+ function normalizeSourcePolicy(value) {
191
+ if (!isRecord(value)) {
192
+ return buildDefaultSourcePolicy();
193
+ }
194
+ const defaults = buildDefaultSourcePolicy();
195
+ const step1Route = isRecord(value.step1_route) ? value.step1_route : {};
196
+ const step2ProcessSplit = isRecord(value.step2_process_split) ? value.step2_process_split : {};
197
+ const step3ExchangeValues = isRecord(value.step3b_exchange_values)
198
+ ? value.step3b_exchange_values
199
+ : {};
200
+ return {
201
+ step1_route: {
202
+ preferred: normalizePreferredList(step1Route.preferred, defaults.step1_route.preferred),
203
+ fallback: nonEmptyString(step1Route.fallback) ?? DEFAULT_SOURCE_POLICY.step1_route.fallback,
204
+ },
205
+ step2_process_split: {
206
+ preferred: normalizePreferredList(step2ProcessSplit.preferred, defaults.step2_process_split.preferred),
207
+ fallback: nonEmptyString(step2ProcessSplit.fallback) ??
208
+ DEFAULT_SOURCE_POLICY.step2_process_split.fallback,
209
+ },
210
+ step3b_exchange_values: {
211
+ preferred: normalizePreferredList(step3ExchangeValues.preferred, defaults.step3b_exchange_values.preferred),
212
+ require_numeric_evidence: normalizeBoolean(step3ExchangeValues.require_numeric_evidence, DEFAULT_SOURCE_POLICY.step3b_exchange_values.require_numeric_evidence),
213
+ allow_estimation: normalizeBoolean(step3ExchangeValues.allow_estimation, DEFAULT_SOURCE_POLICY.step3b_exchange_values.allow_estimation),
214
+ },
215
+ };
216
+ }
217
+ function extractFlowDatasetRoot(payload) {
218
+ if (!isRecord(payload)) {
219
+ throw new CliError('process auto-build flow file must contain a JSON object.', {
220
+ code: 'PROCESS_AUTO_BUILD_FLOW_INVALID',
221
+ exitCode: 2,
222
+ });
223
+ }
224
+ if (isRecord(payload.flowDataSet)) {
225
+ return {
226
+ flowPayload: payload,
227
+ flowDataset: payload.flowDataSet,
228
+ wrapper: 'flowDataSet',
229
+ };
230
+ }
231
+ return {
232
+ flowPayload: payload,
233
+ flowDataset: payload,
234
+ wrapper: 'direct',
235
+ };
236
+ }
237
+ function extractFlowSummary(flowDataset, wrapper) {
238
+ const flowInformation = isRecord(flowDataset.flowInformation) ? flowDataset.flowInformation : {};
239
+ const dataSetInformation = isRecord(flowInformation.dataSetInformation)
240
+ ? flowInformation.dataSetInformation
241
+ : {};
242
+ const administrativeInformation = isRecord(flowDataset.administrativeInformation)
243
+ ? flowDataset.administrativeInformation
244
+ : {};
245
+ const publicationAndOwnership = isRecord(administrativeInformation.publicationAndOwnership)
246
+ ? administrativeInformation.publicationAndOwnership
247
+ : {};
248
+ const name = isRecord(dataSetInformation.name) ? dataSetInformation.name : {};
249
+ return {
250
+ wrapper,
251
+ uuid: nonEmptyString(dataSetInformation['common:UUID']) ??
252
+ nonEmptyString(flowDataset['@id']) ??
253
+ null,
254
+ version: nonEmptyString(publicationAndOwnership['common:dataSetVersion']) ??
255
+ nonEmptyString(flowDataset['@version']) ??
256
+ null,
257
+ base_name: extractText(name.baseName),
258
+ permanent_uri: nonEmptyString(publicationAndOwnership['common:permanentDataSetURI']) ?? null,
259
+ };
260
+ }
261
+ function runIdToken(value, fallback) {
262
+ return sanitizeRunToken(value ?? '', fallback);
263
+ }
264
+ function buildProcessAutoBuildRunId(flowFilePath, operation, summary, now = new Date()) {
265
+ const stem = path.basename(flowFilePath, path.extname(flowFilePath));
266
+ const parts = stem.split('_');
267
+ const flowCode = runIdToken(parts[0] || summary.base_name || summary.uuid, 'flow');
268
+ const flowUuidShort = runIdToken(parts[1] || summary.uuid, 'unknown').slice(0, 8);
269
+ const operationToken = operation === 'treat' ? 'treat' : 'produce';
270
+ return `pfw_${flowCode}_${flowUuidShort}_${operationToken}_${buildUtcTimestamp(now)}`;
271
+ }
272
+ function resolveRunRoot(requestDir, runId, outDirOverride, requestRunRoot) {
273
+ const override = nonEmptyString(outDirOverride);
274
+ if (override) {
275
+ return path.resolve(requestDir, override);
276
+ }
277
+ const requestValue = nonEmptyString(requestRunRoot);
278
+ if (requestValue) {
279
+ return path.resolve(requestDir, requestValue);
280
+ }
281
+ return path.join(requestDir, 'artifacts', 'process_from_flow', runId);
282
+ }
283
+ function normalizeSourceInputType(value) {
284
+ const normalized = nonEmptyString(value);
285
+ if (normalized === 'local_file' || normalized === 'local_text') {
286
+ return normalized;
287
+ }
288
+ throw new CliError("process auto-build source_inputs[].type must be 'local_file' or 'local_text'.", {
289
+ code: 'PROCESS_AUTO_BUILD_SOURCE_INVALID',
290
+ exitCode: 2,
291
+ details: { value },
292
+ });
293
+ }
294
+ function normalizeIntendedRoles(value) {
295
+ if (!Array.isArray(value)) {
296
+ return [];
297
+ }
298
+ return value
299
+ .map((entry) => nonEmptyString(entry))
300
+ .filter((entry) => Boolean(entry));
301
+ }
302
+ function normalizeSourceInputs(value, requestDir, evidenceIncomingDir) {
303
+ if (value === undefined) {
304
+ return [];
305
+ }
306
+ if (!Array.isArray(value)) {
307
+ throw new CliError('process auto-build source_inputs must be an array when provided.', {
308
+ code: 'PROCESS_AUTO_BUILD_SOURCE_INVALID',
309
+ exitCode: 2,
310
+ });
311
+ }
312
+ const seenIds = new Set();
313
+ return value.map((entry, index) => {
314
+ if (!isRecord(entry)) {
315
+ throw new CliError('process auto-build source_inputs entries must be objects.', {
316
+ code: 'PROCESS_AUTO_BUILD_SOURCE_INVALID',
317
+ exitCode: 2,
318
+ details: { index },
319
+ });
320
+ }
321
+ const sourceId = requiredRequestString(entry, 'source_id');
322
+ if (seenIds.has(sourceId)) {
323
+ throw new CliError(`Duplicate process auto-build source_id: ${sourceId}`, {
324
+ code: 'PROCESS_AUTO_BUILD_SOURCE_DUPLICATE',
325
+ exitCode: 2,
326
+ });
327
+ }
328
+ seenIds.add(sourceId);
329
+ const sourcePath = path.resolve(requestDir, requiredRequestString(entry, 'path'));
330
+ if (!existsSync(sourcePath)) {
331
+ throw new CliError(`process auto-build source input not found: ${sourcePath}`, {
332
+ code: 'PROCESS_AUTO_BUILD_SOURCE_NOT_FOUND',
333
+ exitCode: 2,
334
+ });
335
+ }
336
+ const extension = path.extname(sourcePath);
337
+ const artifactFileName = `${String(index + 1).padStart(2, '0')}_${runIdToken(sourceId, 'source')}${extension}`;
338
+ return {
339
+ source_id: sourceId,
340
+ type: normalizeSourceInputType(entry.type),
341
+ source_path: sourcePath,
342
+ artifact_file_name: artifactFileName,
343
+ artifact_path: path.join(evidenceIncomingDir, artifactFileName),
344
+ intended_roles: normalizeIntendedRoles(entry.intended_roles),
345
+ };
346
+ });
347
+ }
348
+ function buildLayout(runRoot, runId) {
349
+ const collectionDir = path.dirname(runRoot);
350
+ const layout = {
351
+ namespace: 'process_from_flow',
352
+ runId,
353
+ artifactsRoot: path.dirname(collectionDir),
354
+ collectionDir,
355
+ runRoot,
356
+ cacheDir: path.join(runRoot, 'cache'),
357
+ inputsDir: path.join(runRoot, 'input'),
358
+ outputsDir: path.join(runRoot, 'exports'),
359
+ reportsDir: path.join(runRoot, 'reports'),
360
+ logsDir: path.join(runRoot, 'logs'),
361
+ manifestsDir: path.join(runRoot, 'manifests'),
362
+ latestRunIdPath: path.join(collectionDir, '.latest_run_id'),
363
+ };
364
+ return {
365
+ ...layout,
366
+ requestDir: path.join(runRoot, 'request'),
367
+ evidenceDir: path.join(runRoot, 'evidence'),
368
+ evidenceIncomingDir: path.join(runRoot, 'evidence', 'incoming'),
369
+ evidenceNormalizedDir: path.join(runRoot, 'evidence', 'normalized'),
370
+ evidenceTextDir: path.join(runRoot, 'evidence', 'text'),
371
+ evidenceStructuredDir: path.join(runRoot, 'evidence', 'structured'),
372
+ stageOutputsDir: path.join(runRoot, 'stage_outputs'),
373
+ reviewsDir: path.join(runRoot, 'reviews'),
374
+ requestSnapshotPath: path.join(runRoot, 'request', 'pff-request.json'),
375
+ normalizedRequestPath: path.join(runRoot, 'request', 'request.normalized.json'),
376
+ sourcePolicyPath: path.join(runRoot, 'request', 'source-policy.json'),
377
+ flowSummaryPath: path.join(runRoot, 'manifests', 'flow-summary.json'),
378
+ inputManifestPath: path.join(runRoot, 'input', 'input_manifest.json'),
379
+ assemblyPlanPath: path.join(runRoot, 'manifests', 'assembly-plan.json'),
380
+ lineageManifestPath: path.join(runRoot, 'manifests', 'lineage-manifest.json'),
381
+ invocationIndexPath: path.join(runRoot, 'manifests', 'invocation-index.json'),
382
+ runManifestPath: path.join(runRoot, 'manifests', 'run-manifest.json'),
383
+ reportPath: path.join(runRoot, 'reports', 'process-auto-build-report.json'),
384
+ statePath: path.join(runRoot, 'cache', 'process_from_flow_state.json'),
385
+ handoffSummaryPath: path.join(runRoot, 'cache', 'agent_handoff_summary.json'),
386
+ processExportsDir: path.join(runRoot, 'exports', 'processes'),
387
+ sourceExportsDir: path.join(runRoot, 'exports', 'sources'),
388
+ };
389
+ }
390
+ function ensureEmptyRunRoot(runRoot) {
391
+ if (!existsSync(runRoot)) {
392
+ return;
393
+ }
394
+ const entries = readdirSync(runRoot);
395
+ if (entries.length > 0) {
396
+ throw new CliError(`process auto-build run root already exists and is not empty: ${runRoot}`, {
397
+ code: 'PROCESS_AUTO_BUILD_RUN_EXISTS',
398
+ exitCode: 2,
399
+ });
400
+ }
401
+ }
402
+ function ensureProcessAutoBuildLayout(layout) {
403
+ ensureRunLayout(layout);
404
+ [
405
+ layout.requestDir,
406
+ layout.evidenceDir,
407
+ layout.evidenceIncomingDir,
408
+ layout.evidenceNormalizedDir,
409
+ layout.evidenceTextDir,
410
+ layout.evidenceStructuredDir,
411
+ layout.stageOutputsDir,
412
+ layout.reviewsDir,
413
+ layout.processExportsDir,
414
+ layout.sourceExportsDir,
415
+ ].forEach((dirPath) => {
416
+ mkdirSync(dirPath, { recursive: true });
417
+ });
418
+ for (const stage of PROCESS_AUTO_BUILD_STAGES) {
419
+ const stageDir = path.join(layout.stageOutputsDir, stage.id);
420
+ mkdirSync(stageDir, { recursive: true });
421
+ if (stage.createsCandidatesDir) {
422
+ mkdirSync(path.join(stageDir, 'candidates'), { recursive: true });
423
+ }
424
+ }
425
+ }
426
+ function copyArtifactFile(sourcePath, targetPath, code) {
427
+ mkdirSync(path.dirname(targetPath), { recursive: true });
428
+ try {
429
+ copyFileSync(sourcePath, targetPath);
430
+ }
431
+ catch (error) {
432
+ throw new CliError(`Failed to copy artifact file: ${sourcePath}`, {
433
+ code,
434
+ exitCode: 1,
435
+ details: String(error),
436
+ });
437
+ }
438
+ }
439
+ function buildAssemblyPlan(runRoot) {
440
+ return {
441
+ schema_version: 1,
442
+ stages: PROCESS_AUTO_BUILD_STAGES.map((stage) => ({
443
+ id: stage.id,
444
+ title: stage.title,
445
+ status: 'pending',
446
+ modules: stage.modules,
447
+ output: stage.output,
448
+ candidates_dir: stage.createsCandidatesDir
449
+ ? path.posix.join(path.posix.dirname(stage.output), 'candidates')
450
+ : null,
451
+ output_abs: path.join(runRoot, stage.output),
452
+ })),
453
+ };
454
+ }
455
+ function buildLineageManifest(normalized) {
456
+ return {
457
+ schema_version: 1,
458
+ request_id: normalized.request_id,
459
+ run_id: normalized.run_id,
460
+ flow: {
461
+ source_path: normalized.flow_file,
462
+ uuid: normalized.flow_summary.uuid,
463
+ version: normalized.flow_summary.version,
464
+ base_name: normalized.flow_summary.base_name,
465
+ },
466
+ source_inputs: normalized.source_inputs.map((source) => ({
467
+ source_id: source.source_id,
468
+ type: source.type,
469
+ source_path: source.source_path,
470
+ artifact_path: source.artifact_path,
471
+ intended_roles: source.intended_roles,
472
+ })),
473
+ };
474
+ }
475
+ function buildInvocationIndex(normalized, options, reportPath, now) {
476
+ const command = ['process', 'auto-build', '--input', options.inputPath];
477
+ if (options.outDir) {
478
+ command.push('--out-dir', options.outDir);
479
+ }
480
+ return {
481
+ schema_version: 1,
482
+ invocations: [
483
+ {
484
+ command,
485
+ cwd: options.cwd ?? process.cwd(),
486
+ created_at: now.toISOString(),
487
+ request_path: normalized.request_path,
488
+ report_path: reportPath,
489
+ },
490
+ ],
491
+ };
492
+ }
493
+ function buildStepMarkers(now) {
494
+ const completedAt = now.toISOString();
495
+ const markers = {
496
+ intake_prepared: {
497
+ status: 'completed',
498
+ completed_at: completedAt,
499
+ },
500
+ };
501
+ PROCESS_AUTO_BUILD_STAGES.forEach((stage) => {
502
+ markers[stage.id] = {
503
+ status: 'pending',
504
+ };
505
+ });
506
+ return markers;
507
+ }
508
+ function buildInitialState(normalized, flowArtifactPath, now) {
509
+ return {
510
+ schema_version: 1,
511
+ build_status: 'intake_prepared',
512
+ next_stage: PROCESS_AUTO_BUILD_STAGES[0]?.id ?? null,
513
+ run_id: normalized.run_id,
514
+ request_id: normalized.request_id,
515
+ flow_path: normalized.flow_file,
516
+ flow_artifact_path: flowArtifactPath,
517
+ flow_dataset: normalized.flow_dataset,
518
+ flow_summary: normalized.flow_summary,
519
+ operation: normalized.operation,
520
+ source_inputs: normalized.source_inputs,
521
+ source_policy: normalized.source_policy,
522
+ scientific_references: {
523
+ source_inputs: normalized.source_inputs.map((source) => ({
524
+ source_id: source.source_id,
525
+ type: source.type,
526
+ intended_roles: source.intended_roles,
527
+ source_path: source.source_path,
528
+ })),
529
+ },
530
+ processes: [],
531
+ process_exchanges: [],
532
+ matched_process_exchanges: [],
533
+ process_datasets: [],
534
+ source_datasets: [],
535
+ coverage_metrics: {},
536
+ coverage_history: [],
537
+ step_markers: buildStepMarkers(now),
538
+ };
539
+ }
540
+ function buildNextActions(layout, normalized) {
541
+ return [
542
+ `inspect: ${layout.normalizedRequestPath}`,
543
+ `inspect: ${layout.statePath}`,
544
+ `inspect: ${layout.assemblyPlanPath}`,
545
+ `future: tiangong process resume-build --run-id ${normalized.run_id}`,
546
+ `future: tiangong process publish-build --run-id ${normalized.run_id}`,
547
+ ];
548
+ }
549
+ function buildAgentHandoffSummary(normalized, layout, flowArtifactPath, now) {
550
+ return {
551
+ schema_version: 1,
552
+ generated_at_utc: now.toISOString(),
553
+ run_id: normalized.run_id,
554
+ command: 'process auto-build',
555
+ flow_path: normalized.flow_file,
556
+ operation: normalized.operation,
557
+ stop_after: null,
558
+ process_count: 0,
559
+ matched_exchange_count: 0,
560
+ process_dataset_count: 0,
561
+ source_dataset_count: 0,
562
+ remaining_placeholder_refs: 0,
563
+ placeholder_examples: [],
564
+ publish_summary: {},
565
+ artifacts: {
566
+ state_path: layout.statePath,
567
+ timing_report: null,
568
+ publish_summary: null,
569
+ llm_cost_report: null,
570
+ process_update_report: null,
571
+ flow_auto_build_manifest: null,
572
+ request_snapshot: layout.requestSnapshotPath,
573
+ normalized_request: layout.normalizedRequestPath,
574
+ assembly_plan: layout.assemblyPlanPath,
575
+ flow_copy: flowArtifactPath,
576
+ },
577
+ next_actions: buildNextActions(layout, normalized),
578
+ extra: {
579
+ status: 'prepared_local_process_auto_build_run',
580
+ request_id: normalized.request_id,
581
+ source_input_count: normalized.source_inputs.length,
582
+ },
583
+ };
584
+ }
585
+ function buildReport(normalized, layout, flowArtifactPath, now) {
586
+ return {
587
+ schema_version: 1,
588
+ generated_at_utc: now.toISOString(),
589
+ status: 'prepared_local_process_auto_build_run',
590
+ request_path: normalized.request_path,
591
+ request_id: normalized.request_id,
592
+ run_id: normalized.run_id,
593
+ run_root: normalized.run_root,
594
+ operation: normalized.operation,
595
+ flow: {
596
+ source_path: normalized.flow_file,
597
+ artifact_path: flowArtifactPath,
598
+ wrapper: normalized.flow_summary.wrapper,
599
+ uuid: normalized.flow_summary.uuid,
600
+ version: normalized.flow_summary.version,
601
+ base_name: normalized.flow_summary.base_name,
602
+ },
603
+ source_input_count: normalized.source_inputs.length,
604
+ stage_count: PROCESS_AUTO_BUILD_STAGES.length,
605
+ files: {
606
+ request_snapshot: layout.requestSnapshotPath,
607
+ normalized_request: layout.normalizedRequestPath,
608
+ source_policy: layout.sourcePolicyPath,
609
+ flow_summary: layout.flowSummaryPath,
610
+ input_manifest: layout.inputManifestPath,
611
+ assembly_plan: layout.assemblyPlanPath,
612
+ lineage_manifest: layout.lineageManifestPath,
613
+ invocation_index: layout.invocationIndexPath,
614
+ run_manifest: layout.runManifestPath,
615
+ state: layout.statePath,
616
+ handoff_summary: layout.handoffSummaryPath,
617
+ report: layout.reportPath,
618
+ },
619
+ next_actions: buildNextActions(layout, normalized),
620
+ };
621
+ }
622
+ export function normalizeProcessAutoBuildRequest(input, options) {
623
+ const request = requiredRequestObject(input);
624
+ const requestDir = path.dirname(path.resolve(options.inputPath));
625
+ const operation = normalizeOperation(request.operation);
626
+ const flowFile = path.resolve(requestDir, requiredRequestString(request, 'flow_file'));
627
+ const flowPayload = readJsonInput(flowFile);
628
+ const { flowDataset, wrapper } = extractFlowDatasetRoot(flowPayload);
629
+ const flowSummary = extractFlowSummary(flowDataset, wrapper);
630
+ const runId = nonEmptyString(options.runIdOverride) ??
631
+ nonEmptyString(request.run_id) ??
632
+ buildProcessAutoBuildRunId(flowFile, operation, flowSummary, options.now);
633
+ const runRoot = resolveRunRoot(requestDir, runId, options.outDir, request.workspace_run_root);
634
+ const layout = buildLayout(runRoot, runId);
635
+ return {
636
+ schema_version: 1,
637
+ request_path: path.resolve(options.inputPath),
638
+ request_id: nonEmptyString(options.requestIdOverride) ??
639
+ nonEmptyString(request.request_id) ??
640
+ `pff-${runId}`,
641
+ flow_file: flowFile,
642
+ flow_summary: flowSummary,
643
+ flow_dataset: flowDataset,
644
+ operation,
645
+ run_id: runId,
646
+ run_root: runRoot,
647
+ source_inputs: normalizeSourceInputs(request.source_inputs, requestDir, layout.evidenceIncomingDir),
648
+ source_policy: normalizeSourcePolicy(request.source_policy),
649
+ };
650
+ }
651
+ export async function runProcessAutoBuild(options) {
652
+ const input = options.inputValue ?? readJsonInput(options.inputPath);
653
+ const now = options.now ?? new Date();
654
+ const normalized = normalizeProcessAutoBuildRequest(input, {
655
+ inputPath: options.inputPath,
656
+ outDir: options.outDir,
657
+ now,
658
+ requestIdOverride: options.requestIdOverride,
659
+ runIdOverride: options.runIdOverride,
660
+ });
661
+ const layout = buildLayout(normalized.run_root, normalized.run_id);
662
+ const flowArtifactPath = path.join(layout.inputsDir, path.basename(normalized.flow_file));
663
+ ensureEmptyRunRoot(layout.runRoot);
664
+ ensureProcessAutoBuildLayout(layout);
665
+ copyArtifactFile(normalized.flow_file, flowArtifactPath, 'PROCESS_AUTO_BUILD_FLOW_COPY_FAILED');
666
+ normalized.source_inputs.forEach((source) => {
667
+ copyArtifactFile(source.source_path, source.artifact_path, 'PROCESS_AUTO_BUILD_SOURCE_COPY_FAILED');
668
+ });
669
+ const request = requiredRequestObject(input);
670
+ writeJsonArtifact(layout.requestSnapshotPath, request);
671
+ writeJsonArtifact(layout.normalizedRequestPath, normalized);
672
+ writeJsonArtifact(layout.sourcePolicyPath, normalized.source_policy);
673
+ writeJsonArtifact(layout.flowSummaryPath, normalized.flow_summary);
674
+ writeJsonArtifact(layout.inputManifestPath, {
675
+ run_id: normalized.run_id,
676
+ flow_path: normalized.flow_file,
677
+ flow_artifact_path: flowArtifactPath,
678
+ operation: normalized.operation,
679
+ });
680
+ writeJsonArtifact(layout.assemblyPlanPath, buildAssemblyPlan(layout.runRoot));
681
+ writeJsonArtifact(layout.lineageManifestPath, buildLineageManifest(normalized));
682
+ writeJsonArtifact(layout.invocationIndexPath, buildInvocationIndex(normalized, options, layout.reportPath, now));
683
+ writeJsonArtifact(layout.runManifestPath, buildRunManifest({
684
+ layout,
685
+ command: options.outDir
686
+ ? ['process', 'auto-build', '--input', options.inputPath, '--out-dir', options.outDir]
687
+ : ['process', 'auto-build', '--input', options.inputPath],
688
+ cwd: options.cwd,
689
+ createdAt: now,
690
+ }));
691
+ const state = buildInitialState(normalized, flowArtifactPath, now);
692
+ await withStateFileLock(layout.statePath, { reason: 'process-auto-build.initial_state', now }, () => writeJsonArtifact(layout.statePath, state));
693
+ const handoff = buildAgentHandoffSummary(normalized, layout, flowArtifactPath, now);
694
+ writeJsonArtifact(layout.handoffSummaryPath, handoff);
695
+ writeLatestRunId(layout, normalized.run_id);
696
+ const report = buildReport(normalized, layout, flowArtifactPath, now);
697
+ writeJsonArtifact(layout.reportPath, report);
698
+ return report;
699
+ }
700
+ // Exposed for deterministic unit coverage of process auto-build normalization and scaffold helpers.
701
+ export const __testInternals = {
702
+ PROCESS_AUTO_BUILD_STAGES,
703
+ extractText,
704
+ normalizeOperation,
705
+ normalizeSourcePolicy,
706
+ extractFlowDatasetRoot,
707
+ extractFlowSummary,
708
+ buildProcessAutoBuildRunId,
709
+ buildLayout,
710
+ buildAssemblyPlan,
711
+ buildLineageManifest,
712
+ buildInvocationIndex,
713
+ buildInitialState,
714
+ buildAgentHandoffSummary,
715
+ buildReport,
716
+ };
717
+ //# sourceMappingURL=process-auto-build.js.map