@tiangong-lca/cli 0.0.23 → 0.0.25
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 +48 -8
- package/dist/src/cli.js +213 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-alias-request.js +99 -0
- package/dist/src/lib/dataset-maintenance-alias-request.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-apply.js +247 -337
- package/dist/src/lib/dataset-maintenance-apply.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-contract.js +118 -16
- package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-derivatives.js +265 -0
- package/dist/src/lib/dataset-maintenance-derivatives.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-plan.js +38 -6
- package/dist/src/lib/dataset-maintenance-plan.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-protected-contract.js +918 -0
- package/dist/src/lib/dataset-maintenance-protected-contract.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-protected-run.js +771 -0
- package/dist/src/lib/dataset-maintenance-protected-run.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-protected-verify.js +435 -0
- package/dist/src/lib/dataset-maintenance-protected-verify.js.map +1 -0
- package/dist/src/lib/dataset-maintenance-remote.js +140 -1
- package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
- package/dist/src/lib/dataset-maintenance-verify.js +309 -3
- package/dist/src/lib/dataset-maintenance-verify.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ export const MAINTENANCE_SCAN_TABLES = [
|
|
|
11
11
|
'lifecyclemodels',
|
|
12
12
|
...MAINTENANCE_SUPPORT_TABLES,
|
|
13
13
|
];
|
|
14
|
+
export const DERIVATIVE_REBUILD_COMPONENTS = ['extracted_md', 'embedding_ft'];
|
|
14
15
|
function token(value) {
|
|
15
16
|
if (typeof value !== 'string') {
|
|
16
17
|
return null;
|
|
@@ -159,16 +160,19 @@ function parseAction(value, index, accountUserId, operation) {
|
|
|
159
160
|
}
|
|
160
161
|
const action = requireToken(value.action, `actions[${index}].action`);
|
|
161
162
|
const table = requireToken(value.table, `actions[${index}].table`);
|
|
162
|
-
if (!['save_draft', 'delete', 'update_json_ordered'].includes(action)) {
|
|
163
|
+
if (!['save_draft', 'delete', 'update_json_ordered', 'rebuild_derivatives'].includes(action)) {
|
|
163
164
|
throw new CliError(`Unsupported maintenance action: ${action}`, {
|
|
164
165
|
code: 'DATASET_MAINTENANCE_ACTION_UNSUPPORTED',
|
|
165
166
|
exitCode: 2,
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
const aliasAction = action === 'update_json_ordered';
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
const derivativeAction = action === 'rebuild_derivatives';
|
|
171
|
+
const allowedTable = derivativeAction
|
|
172
|
+
? table === 'processes'
|
|
173
|
+
: aliasAction
|
|
174
|
+
? ['flowproperties', 'flows', 'processes'].includes(table)
|
|
175
|
+
: MAINTENANCE_MUTABLE_TABLES.includes(table);
|
|
172
176
|
if (!allowedTable) {
|
|
173
177
|
throw new CliError(`Maintenance cannot mutate protected or unsupported dataset table: ${table}`, {
|
|
174
178
|
code: 'DATASET_MAINTENANCE_TABLE_PROTECTED',
|
|
@@ -176,7 +180,9 @@ function parseAction(value, index, accountUserId, operation) {
|
|
|
176
180
|
});
|
|
177
181
|
}
|
|
178
182
|
if ((operation === 'merge-support-aliases' && !aliasAction) ||
|
|
179
|
-
(operation
|
|
183
|
+
(operation === 'rebuild-derivatives' && !derivativeAction) ||
|
|
184
|
+
(!['merge-support-aliases', 'rebuild-derivatives'].includes(operation) &&
|
|
185
|
+
(aliasAction || derivativeAction))) {
|
|
180
186
|
throw new CliError(`Maintenance operation ${operation} cannot contain ${action} actions.`, {
|
|
181
187
|
code: 'DATASET_MAINTENANCE_OPERATION_ACTION_MISMATCH',
|
|
182
188
|
exitCode: 2,
|
|
@@ -240,6 +246,23 @@ function parseAction(value, index, accountUserId, operation) {
|
|
|
240
246
|
exitCode: 2,
|
|
241
247
|
});
|
|
242
248
|
}
|
|
249
|
+
const components = Array.isArray(value.components)
|
|
250
|
+
? value.components.map((component, componentIndex) => requireToken(component, `actions[${index}].components[${componentIndex}]`))
|
|
251
|
+
: [];
|
|
252
|
+
if (derivativeAction &&
|
|
253
|
+
(components.length !== DERIVATIVE_REBUILD_COMPONENTS.length ||
|
|
254
|
+
components.some((component, componentIndex) => component !== DERIVATIVE_REBUILD_COMPONENTS[componentIndex]))) {
|
|
255
|
+
throw new CliError(`Derivative rebuild action ${index} requires components in exact order: ${DERIVATIVE_REBUILD_COMPONENTS.join(', ')}.`, {
|
|
256
|
+
code: 'DATASET_MAINTENANCE_DERIVATIVE_COMPONENTS_INVALID',
|
|
257
|
+
exitCode: 2,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (!derivativeAction && 'components' in value) {
|
|
261
|
+
throw new CliError(`Maintenance non-derivative action ${index} cannot include components.`, {
|
|
262
|
+
code: 'DATASET_MAINTENANCE_DERIVATIVE_COMPONENTS_INVALID',
|
|
263
|
+
exitCode: 2,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
243
266
|
const exchangeKeys = new Set(exchangeInstances.map((entry) => `${entry.exchange_index}\u0000${entry.data_set_internal_id}\u0000${entry.flow_id}`));
|
|
244
267
|
if (exchangeKeys.size !== exchangeInstances.length) {
|
|
245
268
|
throw new CliError(`Alias action ${index} contains duplicate exchange instances.`, {
|
|
@@ -269,6 +292,7 @@ function parseAction(value, index, accountUserId, operation) {
|
|
|
269
292
|
...(exchangeInstances.length ? { exchange_instances: exchangeInstances } : {}),
|
|
270
293
|
...(desiredPayloadPath ? { desired_payload_path: desiredPayloadPath } : {}),
|
|
271
294
|
...(expectedBeforeSha256 ? { expected_before_sha256: expectedBeforeSha256 } : {}),
|
|
295
|
+
...(derivativeAction ? { components: [...DERIVATIVE_REBUILD_COMPONENTS] } : {}),
|
|
272
296
|
};
|
|
273
297
|
}
|
|
274
298
|
export function parseMaintenanceScope(value, expectedOperation) {
|
|
@@ -279,7 +303,14 @@ export function parseMaintenanceScope(value, expectedOperation) {
|
|
|
279
303
|
});
|
|
280
304
|
}
|
|
281
305
|
const operation = requireToken(value.operation, 'operation');
|
|
282
|
-
if (![
|
|
306
|
+
if (![
|
|
307
|
+
'delete',
|
|
308
|
+
'retire',
|
|
309
|
+
'redo-import',
|
|
310
|
+
'repair-references',
|
|
311
|
+
'merge-support-aliases',
|
|
312
|
+
'rebuild-derivatives',
|
|
313
|
+
].includes(operation)) {
|
|
283
314
|
throw new CliError(`Unsupported maintenance operation: ${operation}`, {
|
|
284
315
|
code: 'DATASET_MAINTENANCE_OPERATION_UNSUPPORTED',
|
|
285
316
|
exitCode: 2,
|
|
@@ -300,9 +331,10 @@ export function parseMaintenanceScope(value, expectedOperation) {
|
|
|
300
331
|
}
|
|
301
332
|
const actions = value.actions.map((entry, index) => parseAction(entry, index, userId, operation));
|
|
302
333
|
const targetMode = token(value.target_mode);
|
|
303
|
-
if ((
|
|
304
|
-
|
|
305
|
-
|
|
334
|
+
if ((['merge-support-aliases', 'rebuild-derivatives'].includes(operation) &&
|
|
335
|
+
targetMode !== 'owner_draft') ||
|
|
336
|
+
(!['merge-support-aliases', 'rebuild-derivatives'].includes(operation) && targetMode !== null)) {
|
|
337
|
+
throw new CliError('merge-support-aliases and rebuild-derivatives require target_mode=owner_draft; other operations forbid target_mode.', {
|
|
306
338
|
code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',
|
|
307
339
|
exitCode: 2,
|
|
308
340
|
details: { operation, target_mode: targetMode },
|
|
@@ -318,6 +350,12 @@ export function parseMaintenanceScope(value, expectedOperation) {
|
|
|
318
350
|
exitCode: 2,
|
|
319
351
|
});
|
|
320
352
|
}
|
|
353
|
+
if (operation === 'rebuild-derivatives' && actions.length !== 1) {
|
|
354
|
+
throw new CliError('rebuild-derivatives requires exactly one action.', {
|
|
355
|
+
code: 'DATASET_MAINTENANCE_DERIVATIVE_ACTION_COUNT_INVALID',
|
|
356
|
+
exitCode: 2,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
321
359
|
if (aliasBatches.length) {
|
|
322
360
|
const batchIds = new Set(aliasBatches.map((batch) => batch.batch_id));
|
|
323
361
|
const dimensions = new Set(aliasBatches.map((batch) => batch.dimension));
|
|
@@ -474,6 +512,32 @@ export function computePlanSha256(plan) {
|
|
|
474
512
|
const body = { ...plan, plan_sha256: '' };
|
|
475
513
|
return sha256Json(body);
|
|
476
514
|
}
|
|
515
|
+
function validDerivativeSnapshot(value, action) {
|
|
516
|
+
if (!isJsonObject(value))
|
|
517
|
+
return false;
|
|
518
|
+
const requiredHashes = [
|
|
519
|
+
value.json_sha256,
|
|
520
|
+
value.json_ordered_sha256,
|
|
521
|
+
value.extracted_text_sha256,
|
|
522
|
+
value.snapshot_sha256,
|
|
523
|
+
];
|
|
524
|
+
const nullableHashes = [value.extracted_md_sha256, value.embedding_ft_sha256];
|
|
525
|
+
return Boolean(value.schema_version === 'dataset-derivative-snapshot.v1' &&
|
|
526
|
+
value.table === 'processes' &&
|
|
527
|
+
value.id === action.id &&
|
|
528
|
+
value.version === action.version &&
|
|
529
|
+
value.user_id === action.expected_user_id &&
|
|
530
|
+
value.state_code === 0 &&
|
|
531
|
+
value.modified_at === action.before?.modified_at &&
|
|
532
|
+
typeof value.modified_at === 'string' &&
|
|
533
|
+
Number.isFinite(Date.parse(value.modified_at)) &&
|
|
534
|
+
requiredHashes.every((hash) => typeof hash === 'string' && /^[a-f0-9]{64}$/u.test(hash)) &&
|
|
535
|
+
nullableHashes.every((hash) => hash === null || (typeof hash === 'string' && /^[a-f0-9]{64}$/u.test(hash))) &&
|
|
536
|
+
value.json_sha256 === value.json_ordered_sha256 &&
|
|
537
|
+
(value.embedding_ft_at === null ||
|
|
538
|
+
(typeof value.embedding_ft_at === 'string' &&
|
|
539
|
+
Number.isFinite(Date.parse(value.embedding_ft_at)))));
|
|
540
|
+
}
|
|
477
541
|
export function parseMaintenancePlan(value) {
|
|
478
542
|
if (!isJsonObject(value) ||
|
|
479
543
|
value.schema_version !== 1 ||
|
|
@@ -593,13 +657,23 @@ export function parseMaintenancePlan(value) {
|
|
|
593
657
|
? 'save_before_snapshot'
|
|
594
658
|
: action.action === 'delete'
|
|
595
659
|
? 'restore_deleted_before_snapshot'
|
|
596
|
-
: '
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
action.rollback.
|
|
602
|
-
|
|
660
|
+
: action.action === 'update_json_ordered'
|
|
661
|
+
? 'restore_atomic_alias_before_snapshot'
|
|
662
|
+
: 'none_derivative_only';
|
|
663
|
+
const derivativeRollback = action.action === 'rebuild_derivatives';
|
|
664
|
+
const rollbackMatches = derivativeRollback
|
|
665
|
+
? action.rollback.strategy === expectedRollbackStrategy &&
|
|
666
|
+
action.rollback.before_payload_sha256 === null &&
|
|
667
|
+
action.rollback.before_payload === null &&
|
|
668
|
+
action.rollback.model_id === null &&
|
|
669
|
+
action.rollback.rule_verification === null
|
|
670
|
+
: action.rollback.strategy === expectedRollbackStrategy &&
|
|
671
|
+
action.rollback.before_payload_sha256 === before.payload_sha256 &&
|
|
672
|
+
isJsonObject(action.rollback.before_payload) &&
|
|
673
|
+
sha256Json(action.rollback.before_payload) === before.payload_sha256 &&
|
|
674
|
+
action.rollback.model_id === before.model_id &&
|
|
675
|
+
action.rollback.rule_verification === before.rule_verification;
|
|
676
|
+
if (!rollbackMatches) {
|
|
603
677
|
throw new CliError('Ready maintenance plan action rollback snapshot is invalid.', {
|
|
604
678
|
code: 'DATASET_MAINTENANCE_PLAN_INVALID',
|
|
605
679
|
exitCode: 2,
|
|
@@ -629,6 +703,8 @@ export function parseMaintenancePlan(value) {
|
|
|
629
703
|
plan.summary.delete === plan.actions.filter((action) => action.action === 'delete').length &&
|
|
630
704
|
(plan.summary.update_json_ordered ?? 0) ===
|
|
631
705
|
plan.actions.filter((action) => action.action === 'update_json_ordered').length &&
|
|
706
|
+
(plan.summary.rebuild_derivatives ?? 0) ===
|
|
707
|
+
plan.actions.filter((action) => action.action === 'rebuild_derivatives').length &&
|
|
632
708
|
(plan.summary.atomic_batches ?? 0) === (plan.alias_batches?.length ?? 0) &&
|
|
633
709
|
(plan.summary.scaled_exchanges ?? 0) ===
|
|
634
710
|
(plan.alias_batches?.reduce((sum, batch) => sum + batch.summary.exchanges, 0) ?? 0) &&
|
|
@@ -839,6 +915,32 @@ export function parseMaintenancePlan(value) {
|
|
|
839
915
|
});
|
|
840
916
|
}
|
|
841
917
|
}
|
|
918
|
+
if (plan.operation === 'rebuild-derivatives') {
|
|
919
|
+
const action = plan.actions[0];
|
|
920
|
+
const validDerivativePlan = Boolean(plan.target_mode === 'owner_draft' &&
|
|
921
|
+
plan.actions.length === 1 &&
|
|
922
|
+
!plan.alias_batches &&
|
|
923
|
+
plan.artifacts.derivative_baseline === 'derivative-baseline.json' &&
|
|
924
|
+
action?.action === 'rebuild_derivatives' &&
|
|
925
|
+
action.table === 'processes' &&
|
|
926
|
+
sha256Json(action.components) === sha256Json(DERIVATIVE_REBUILD_COMPONENTS) &&
|
|
927
|
+
action.desired_payload === null &&
|
|
928
|
+
action.rollback.strategy === 'none_derivative_only' &&
|
|
929
|
+
(action.status === 'blocked' || validDerivativeSnapshot(action.derivative_before, action)));
|
|
930
|
+
if (!validDerivativePlan) {
|
|
931
|
+
throw new CliError('Maintenance derivative rebuild plan contract is inconsistent.', {
|
|
932
|
+
code: 'DATASET_MAINTENANCE_PLAN_INVALID',
|
|
933
|
+
exitCode: 2,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else if (plan.actions.some((action) => action.derivative_before !== undefined) ||
|
|
938
|
+
plan.artifacts.derivative_baseline !== undefined) {
|
|
939
|
+
throw new CliError('Non-derivative maintenance plan contains derivative-only fields.', {
|
|
940
|
+
code: 'DATASET_MAINTENANCE_PLAN_INVALID',
|
|
941
|
+
exitCode: 2,
|
|
942
|
+
});
|
|
943
|
+
}
|
|
842
944
|
return plan;
|
|
843
945
|
}
|
|
844
946
|
//# sourceMappingURL=dataset-maintenance-contract.js.map
|