@tradejs/cli 3.1.27-beta.249 → 3.1.27
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/dist/cli.js +366 -125
- package/dist/lib/runtimeEvidenceCompositionSnapshots.js +432 -0
- package/dist/lib/runtimeEvidenceCompositions.js +84 -0
- package/dist/scripts/runtimeEvidence.js +93 -9
- package/dist/scripts/signals.js +396 -18
- package/dist/scripts/signalsDaemon.js +396 -18
- package/package.json +7 -7
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
31
|
+
var runtimeEvidenceCompositionSnapshots_exports = {};
|
|
32
|
+
__export(runtimeEvidenceCompositionSnapshots_exports, {
|
|
33
|
+
captureRuntimeEvidenceCompositionSnapshot: () => captureRuntimeEvidenceCompositionSnapshot,
|
|
34
|
+
createRuntimeEvidenceCompositionSnapshotRecorder: () => createRuntimeEvidenceCompositionSnapshotRecorder,
|
|
35
|
+
discoverRuntimeEvidenceCompositionSnapshots: () => discoverRuntimeEvidenceCompositionSnapshots,
|
|
36
|
+
publishRuntimeEvidenceCompositionSnapshot: () => publishRuntimeEvidenceCompositionSnapshot,
|
|
37
|
+
verifyRuntimeEvidenceCompositionSnapshot: () => verifyRuntimeEvidenceCompositionSnapshot
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(runtimeEvidenceCompositionSnapshots_exports);
|
|
40
|
+
var import_node_crypto2 = require("crypto");
|
|
41
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
42
|
+
var import_node_path2 = __toESM(require("path"));
|
|
43
|
+
|
|
44
|
+
// src/lib/runtimeEvidenceDeployment.ts
|
|
45
|
+
var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
|
|
46
|
+
var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
47
|
+
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
48
|
+
var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
|
|
49
|
+
([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
|
|
50
|
+
);
|
|
51
|
+
var INTERVALS = /* @__PURE__ */ new Set([
|
|
52
|
+
"1",
|
|
53
|
+
"3",
|
|
54
|
+
"5",
|
|
55
|
+
"15",
|
|
56
|
+
"30",
|
|
57
|
+
"60",
|
|
58
|
+
"120",
|
|
59
|
+
"240",
|
|
60
|
+
"360",
|
|
61
|
+
"720",
|
|
62
|
+
"D",
|
|
63
|
+
"W",
|
|
64
|
+
"M"
|
|
65
|
+
]);
|
|
66
|
+
var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
|
|
67
|
+
var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
|
|
68
|
+
var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
|
|
69
|
+
if (!isRecord(value) || value.schemaVersion !== 2 || !isNonEmptyString(value.id) || !isNonEmptyString(value.deploymentCompositionId) || !/^dc1:[a-f0-9]{16}$/.test(value.deploymentCompositionId) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Runtime evidence deployment snapshot is missing or invalid"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const strategies = value.strategies.map((candidate) => {
|
|
75
|
+
if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !isNonEmptyString(candidate.strategyRevision) || !/^sr1:[a-f0-9]{16}$/.test(candidate.strategyRevision) || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isPackageVersionMap(candidate.strategyDependencyVersions) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"Runtime evidence deployment strategy snapshot is invalid"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return candidate;
|
|
91
|
+
});
|
|
92
|
+
const identities = /* @__PURE__ */ new Set();
|
|
93
|
+
for (const strategy of strategies) {
|
|
94
|
+
if (identities.has(strategy.strategyName)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Duplicate runtime evidence strategy: ${strategy.strategyName}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
identities.add(strategy.strategyName);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
...value,
|
|
103
|
+
strategies
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
var resolveRuntimeEvidenceDeploymentSnapshot = async ({
|
|
107
|
+
userName,
|
|
108
|
+
projectRoot,
|
|
109
|
+
deploymentId
|
|
110
|
+
}) => {
|
|
111
|
+
const [deployment, strategies] = await Promise.all([
|
|
112
|
+
(0, import_runtimeStrategies.getRuntimeDeployment)({ userName, projectRoot, deploymentId }),
|
|
113
|
+
(0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({ userName, projectRoot, deploymentId })
|
|
114
|
+
]);
|
|
115
|
+
if (!deployment) {
|
|
116
|
+
throw new Error(`Runtime deployment not found: ${deploymentId}`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
schemaVersion: 2,
|
|
120
|
+
id: deployment.id,
|
|
121
|
+
deploymentCompositionId: deployment.deploymentCompositionId,
|
|
122
|
+
label: deployment.label,
|
|
123
|
+
connectorName: deployment.connectorName,
|
|
124
|
+
provider: deployment.provider,
|
|
125
|
+
accountId: deployment.accountId,
|
|
126
|
+
enabled: deployment.enabled,
|
|
127
|
+
strategies: strategies.map(
|
|
128
|
+
({
|
|
129
|
+
strategyName,
|
|
130
|
+
strategyRevision,
|
|
131
|
+
enabled,
|
|
132
|
+
controlState,
|
|
133
|
+
interval,
|
|
134
|
+
universe,
|
|
135
|
+
accountId,
|
|
136
|
+
strategyPackage,
|
|
137
|
+
strategyPackageVersion,
|
|
138
|
+
strategyDependencyVersions,
|
|
139
|
+
runtimePackageVersion,
|
|
140
|
+
strategyConfig,
|
|
141
|
+
selection
|
|
142
|
+
}) => ({
|
|
143
|
+
strategyName,
|
|
144
|
+
strategyRevision,
|
|
145
|
+
enabled,
|
|
146
|
+
controlState,
|
|
147
|
+
interval,
|
|
148
|
+
universe,
|
|
149
|
+
...accountId ? { accountId } : {},
|
|
150
|
+
strategyPackage,
|
|
151
|
+
strategyPackageVersion,
|
|
152
|
+
strategyDependencyVersions,
|
|
153
|
+
runtimePackageVersion,
|
|
154
|
+
strategyConfig,
|
|
155
|
+
...selection ? { selection } : {}
|
|
156
|
+
})
|
|
157
|
+
),
|
|
158
|
+
...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
|
|
159
|
+
...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/lib/runtimeEvidenceProducer.ts
|
|
164
|
+
var import_node_crypto = require("crypto");
|
|
165
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
166
|
+
var import_node_path = __toESM(require("path"));
|
|
167
|
+
var isRecord2 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
168
|
+
var sha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
169
|
+
var parseRuntimeEvidenceProducer = (value) => {
|
|
170
|
+
const producer = isRecord2(value) ? value : null;
|
|
171
|
+
const runtimePackageManifest = isRecord2(producer?.runtimePackageManifest) ? producer.runtimePackageManifest : null;
|
|
172
|
+
if (producer?.schemaVersion !== 1 || typeof producer.projectSha !== "string" || !/^[a-f0-9]{40}$/.test(producer.projectSha) || typeof producer.imageDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(producer.imageDigest) || runtimePackageManifest?.file !== "runtime-package-manifest.json" || typeof runtimePackageManifest.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(runtimePackageManifest.sha256)) {
|
|
173
|
+
throw new Error("Runtime evidence producer image identity is invalid");
|
|
174
|
+
}
|
|
175
|
+
return producer;
|
|
176
|
+
};
|
|
177
|
+
var resolveRuntimeEvidenceProducer = async ({
|
|
178
|
+
projectRoot,
|
|
179
|
+
required
|
|
180
|
+
}) => {
|
|
181
|
+
const projectSha = String(process.env.TRADEJS_PROJECT_SHA ?? "").trim();
|
|
182
|
+
const imageDigest = String(
|
|
183
|
+
process.env.TRADEJS_PROJECT_IMAGE_DIGEST ?? ""
|
|
184
|
+
).trim();
|
|
185
|
+
if (!projectSha && !imageDigest && !required) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
if (!/^[a-f0-9]{40}$/.test(projectSha)) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
"TRADEJS_PROJECT_SHA must be a full lowercase Git SHA before publishing runtime evidence"
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(imageDigest)) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
"TRADEJS_PROJECT_IMAGE_DIGEST must be an immutable sha256 digest before publishing runtime evidence"
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const manifestPath = import_node_path.default.join(projectRoot, "runtime-package-manifest.json");
|
|
199
|
+
let manifest;
|
|
200
|
+
try {
|
|
201
|
+
manifest = await import_promises.default.readFile(manifestPath);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (!required && error.code === "ENOENT") {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Runtime package manifest is required for runtime evidence: ${manifestPath}`,
|
|
208
|
+
{ cause: error }
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
schemaVersion: 1,
|
|
213
|
+
projectSha,
|
|
214
|
+
imageDigest,
|
|
215
|
+
runtimePackageManifest: {
|
|
216
|
+
file: "runtime-package-manifest.json",
|
|
217
|
+
sha256: sha256(manifest)
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
223
|
+
var SNAPSHOT_FILE = "snapshot.json";
|
|
224
|
+
var MANIFEST_FILE = "manifest.json";
|
|
225
|
+
var COMPLETE_FILE = ".complete";
|
|
226
|
+
var createRuntimeEvidenceCompositionSnapshotRecorder = ({
|
|
227
|
+
persist,
|
|
228
|
+
onError
|
|
229
|
+
}) => {
|
|
230
|
+
const completed = /* @__PURE__ */ new Set();
|
|
231
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
232
|
+
return {
|
|
233
|
+
observe(compositionId) {
|
|
234
|
+
if (completed.has(compositionId) || inFlight.has(compositionId)) return;
|
|
235
|
+
inFlight.add(compositionId);
|
|
236
|
+
void Promise.resolve().then(() => persist(compositionId)).then(() => completed.add(compositionId)).catch((error) => onError(compositionId, error)).finally(() => inFlight.delete(compositionId));
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
var isRecord3 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
241
|
+
var sha2562 = (value) => (0, import_node_crypto2.createHash)("sha256").update(value).digest("hex");
|
|
242
|
+
var safeSegment = (value, label) => {
|
|
243
|
+
const normalized = value.trim();
|
|
244
|
+
if (!normalized || !/^[a-zA-Z0-9._:-]+$/.test(normalized)) {
|
|
245
|
+
throw new Error(`Invalid runtime evidence snapshot ${label}`);
|
|
246
|
+
}
|
|
247
|
+
return normalized;
|
|
248
|
+
};
|
|
249
|
+
var isExistingDestinationError = (error) => {
|
|
250
|
+
const code = error.code;
|
|
251
|
+
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
252
|
+
};
|
|
253
|
+
var parseManifest = (value) => {
|
|
254
|
+
const manifest = isRecord3(value) ? value : null;
|
|
255
|
+
const payload = isRecord3(manifest?.payload) ? manifest.payload : null;
|
|
256
|
+
if (manifest?.schemaVersion !== 1 || manifest.reportType !== "runtime-evidence-composition-snapshot" || typeof manifest.createdAt !== "number" || typeof manifest.userName !== "string" || typeof manifest.deploymentId !== "string" || typeof manifest.deploymentCompositionId !== "string" || payload?.file !== SNAPSHOT_FILE || typeof payload.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(payload.sha256) || typeof payload.bytes !== "number") {
|
|
257
|
+
throw new Error("Invalid runtime evidence composition snapshot manifest");
|
|
258
|
+
}
|
|
259
|
+
return manifest;
|
|
260
|
+
};
|
|
261
|
+
var verifyRuntimeEvidenceCompositionSnapshot = async (bundleDir) => {
|
|
262
|
+
const [manifestText, payload, completeText] = await Promise.all([
|
|
263
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, MANIFEST_FILE), "utf8"),
|
|
264
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, SNAPSHOT_FILE)),
|
|
265
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, COMPLETE_FILE), "utf8")
|
|
266
|
+
]);
|
|
267
|
+
const manifest = parseManifest(JSON.parse(manifestText));
|
|
268
|
+
if (completeText.trim() !== manifest.deploymentCompositionId) {
|
|
269
|
+
throw new Error(`Invalid completion marker in ${bundleDir}`);
|
|
270
|
+
}
|
|
271
|
+
if (payload.byteLength !== manifest.payload.bytes || sha2562(payload) !== manifest.payload.sha256) {
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Runtime evidence snapshot checksum mismatch in ${bundleDir}`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
const value = JSON.parse(payload.toString("utf8"));
|
|
277
|
+
const record = isRecord3(value) ? value : null;
|
|
278
|
+
if (record?.schemaVersion !== 1 || record.reportType !== "runtime-evidence-composition-snapshot" || record.userName !== manifest.userName) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Invalid runtime evidence snapshot payload in ${bundleDir}`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const deployment = parseRuntimeEvidenceDeploymentSnapshot(record.deployment);
|
|
284
|
+
const producer = parseRuntimeEvidenceProducer(record.producer);
|
|
285
|
+
if (deployment.id !== manifest.deploymentId || deployment.deploymentCompositionId !== manifest.deploymentCompositionId) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`Runtime evidence snapshot identity mismatch in ${bundleDir}`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return { bundleDir, deployment, producer };
|
|
291
|
+
};
|
|
292
|
+
var sameSnapshot = (snapshot, deployment, producer) => JSON.stringify(snapshot.deployment) === JSON.stringify(deployment) && JSON.stringify(snapshot.producer) === JSON.stringify(producer);
|
|
293
|
+
var publishRuntimeEvidenceCompositionSnapshot = async ({
|
|
294
|
+
evidenceRoot,
|
|
295
|
+
userName,
|
|
296
|
+
deployment,
|
|
297
|
+
producer
|
|
298
|
+
}) => {
|
|
299
|
+
const deploymentId = safeSegment(deployment.id, "deployment id");
|
|
300
|
+
const compositionId = safeSegment(
|
|
301
|
+
deployment.deploymentCompositionId,
|
|
302
|
+
"composition id"
|
|
303
|
+
);
|
|
304
|
+
const createdAt = Date.now();
|
|
305
|
+
const snapshot = {
|
|
306
|
+
schemaVersion: 1,
|
|
307
|
+
reportType: "runtime-evidence-composition-snapshot",
|
|
308
|
+
createdAt,
|
|
309
|
+
userName,
|
|
310
|
+
deployment,
|
|
311
|
+
producer
|
|
312
|
+
};
|
|
313
|
+
const payload = `${JSON.stringify(snapshot, null, 2)}
|
|
314
|
+
`;
|
|
315
|
+
const manifest = {
|
|
316
|
+
schemaVersion: 1,
|
|
317
|
+
reportType: "runtime-evidence-composition-snapshot",
|
|
318
|
+
createdAt,
|
|
319
|
+
userName,
|
|
320
|
+
deploymentId,
|
|
321
|
+
deploymentCompositionId: compositionId,
|
|
322
|
+
payload: {
|
|
323
|
+
file: SNAPSHOT_FILE,
|
|
324
|
+
sha256: sha2562(payload),
|
|
325
|
+
bytes: Buffer.byteLength(payload)
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
const snapshotsRoot = import_node_path2.default.join(evidenceRoot, "snapshots", deploymentId);
|
|
329
|
+
const bundleDir = import_node_path2.default.join(snapshotsRoot, compositionId);
|
|
330
|
+
const temporaryDir = import_node_path2.default.join(
|
|
331
|
+
evidenceRoot,
|
|
332
|
+
"incoming",
|
|
333
|
+
`snapshot-${compositionId}.tmp-${(0, import_node_crypto2.randomUUID)()}`
|
|
334
|
+
);
|
|
335
|
+
await import_promises2.default.mkdir(temporaryDir, { recursive: true });
|
|
336
|
+
try {
|
|
337
|
+
await Promise.all([
|
|
338
|
+
import_promises2.default.writeFile(import_node_path2.default.join(temporaryDir, SNAPSHOT_FILE), payload, "utf8"),
|
|
339
|
+
import_promises2.default.writeFile(
|
|
340
|
+
import_node_path2.default.join(temporaryDir, MANIFEST_FILE),
|
|
341
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
342
|
+
`,
|
|
343
|
+
"utf8"
|
|
344
|
+
)
|
|
345
|
+
]);
|
|
346
|
+
await import_promises2.default.writeFile(
|
|
347
|
+
import_node_path2.default.join(temporaryDir, COMPLETE_FILE),
|
|
348
|
+
`${compositionId}
|
|
349
|
+
`,
|
|
350
|
+
"utf8"
|
|
351
|
+
);
|
|
352
|
+
await import_promises2.default.mkdir(snapshotsRoot, { recursive: true });
|
|
353
|
+
try {
|
|
354
|
+
await import_promises2.default.rename(temporaryDir, bundleDir);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (!isExistingDestinationError(error)) throw error;
|
|
357
|
+
await import_promises2.default.rm(temporaryDir, { recursive: true, force: true });
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
await import_promises2.default.rm(temporaryDir, { recursive: true, force: true });
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
const verified = await verifyRuntimeEvidenceCompositionSnapshot(bundleDir);
|
|
364
|
+
if (!sameSnapshot(verified, deployment, producer)) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Conflicting runtime evidence snapshot for composition ${compositionId}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
return verified;
|
|
370
|
+
};
|
|
371
|
+
var captureRuntimeEvidenceCompositionSnapshot = async ({
|
|
372
|
+
projectRoot,
|
|
373
|
+
userName,
|
|
374
|
+
deploymentId,
|
|
375
|
+
expectedCompositionId
|
|
376
|
+
}) => {
|
|
377
|
+
const [deployment, producer] = await Promise.all([
|
|
378
|
+
resolveRuntimeEvidenceDeploymentSnapshot({
|
|
379
|
+
userName,
|
|
380
|
+
projectRoot,
|
|
381
|
+
deploymentId
|
|
382
|
+
}),
|
|
383
|
+
resolveRuntimeEvidenceProducer({ projectRoot, required: true })
|
|
384
|
+
]);
|
|
385
|
+
if (!producer) {
|
|
386
|
+
throw new Error("Runtime evidence producer identity is required");
|
|
387
|
+
}
|
|
388
|
+
if (deployment.deploymentCompositionId !== expectedCompositionId) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`Runtime deployment composition changed while capturing snapshot: expected ${expectedCompositionId}, received ${deployment.deploymentCompositionId}`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
await publishRuntimeEvidenceCompositionSnapshot({
|
|
394
|
+
evidenceRoot: import_node_path2.default.join(projectRoot, "data", "runtime-evidence"),
|
|
395
|
+
userName,
|
|
396
|
+
deployment,
|
|
397
|
+
producer
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
var discoverRuntimeEvidenceCompositionSnapshots = async (evidenceRoot) => {
|
|
401
|
+
const rootDir = import_node_path2.default.join(evidenceRoot, "snapshots");
|
|
402
|
+
const bundleDirs = [];
|
|
403
|
+
let deployments;
|
|
404
|
+
try {
|
|
405
|
+
deployments = await import_promises2.default.readdir(rootDir, { withFileTypes: true });
|
|
406
|
+
} catch (error) {
|
|
407
|
+
if (error.code === "ENOENT") return [];
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
410
|
+
for (const deployment of deployments.filter((entry) => entry.isDirectory())) {
|
|
411
|
+
const deploymentDir = import_node_path2.default.join(rootDir, deployment.name);
|
|
412
|
+
const compositions = await import_promises2.default.readdir(deploymentDir, {
|
|
413
|
+
withFileTypes: true
|
|
414
|
+
});
|
|
415
|
+
bundleDirs.push(
|
|
416
|
+
...compositions.filter(
|
|
417
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.includes(".tmp-")
|
|
418
|
+
).map((entry) => import_node_path2.default.join(deploymentDir, entry.name))
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
return Promise.all(
|
|
422
|
+
bundleDirs.sort().map(verifyRuntimeEvidenceCompositionSnapshot)
|
|
423
|
+
);
|
|
424
|
+
};
|
|
425
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
426
|
+
0 && (module.exports = {
|
|
427
|
+
captureRuntimeEvidenceCompositionSnapshot,
|
|
428
|
+
createRuntimeEvidenceCompositionSnapshotRecorder,
|
|
429
|
+
discoverRuntimeEvidenceCompositionSnapshots,
|
|
430
|
+
publishRuntimeEvidenceCompositionSnapshot,
|
|
431
|
+
verifyRuntimeEvidenceCompositionSnapshot
|
|
432
|
+
});
|
|
@@ -311,6 +311,11 @@ var discoverRuntimeEvidenceBundles = async (rootDir) => {
|
|
|
311
311
|
return bundles.sort();
|
|
312
312
|
};
|
|
313
313
|
|
|
314
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
315
|
+
var import_node_crypto3 = require("crypto");
|
|
316
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
317
|
+
var import_node_path3 = __toESM(require("path"));
|
|
318
|
+
|
|
314
319
|
// src/lib/runtimeEvidenceProducer.ts
|
|
315
320
|
var import_node_crypto2 = require("crypto");
|
|
316
321
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
@@ -325,6 +330,77 @@ var parseRuntimeEvidenceProducer = (value) => {
|
|
|
325
330
|
return producer;
|
|
326
331
|
};
|
|
327
332
|
|
|
333
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
334
|
+
var SNAPSHOT_FILE = "snapshot.json";
|
|
335
|
+
var MANIFEST_FILE = "manifest.json";
|
|
336
|
+
var COMPLETE_FILE = ".complete";
|
|
337
|
+
var isRecord3 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
338
|
+
var sha2562 = (value) => (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
|
|
339
|
+
var parseManifest2 = (value) => {
|
|
340
|
+
const manifest = isRecord3(value) ? value : null;
|
|
341
|
+
const payload = isRecord3(manifest?.payload) ? manifest.payload : null;
|
|
342
|
+
if (manifest?.schemaVersion !== 1 || manifest.reportType !== "runtime-evidence-composition-snapshot" || typeof manifest.createdAt !== "number" || typeof manifest.userName !== "string" || typeof manifest.deploymentId !== "string" || typeof manifest.deploymentCompositionId !== "string" || payload?.file !== SNAPSHOT_FILE || typeof payload.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(payload.sha256) || typeof payload.bytes !== "number") {
|
|
343
|
+
throw new Error("Invalid runtime evidence composition snapshot manifest");
|
|
344
|
+
}
|
|
345
|
+
return manifest;
|
|
346
|
+
};
|
|
347
|
+
var verifyRuntimeEvidenceCompositionSnapshot = async (bundleDir) => {
|
|
348
|
+
const [manifestText, payload, completeText] = await Promise.all([
|
|
349
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, MANIFEST_FILE), "utf8"),
|
|
350
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, SNAPSHOT_FILE)),
|
|
351
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, COMPLETE_FILE), "utf8")
|
|
352
|
+
]);
|
|
353
|
+
const manifest = parseManifest2(JSON.parse(manifestText));
|
|
354
|
+
if (completeText.trim() !== manifest.deploymentCompositionId) {
|
|
355
|
+
throw new Error(`Invalid completion marker in ${bundleDir}`);
|
|
356
|
+
}
|
|
357
|
+
if (payload.byteLength !== manifest.payload.bytes || sha2562(payload) !== manifest.payload.sha256) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`Runtime evidence snapshot checksum mismatch in ${bundleDir}`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
const value = JSON.parse(payload.toString("utf8"));
|
|
363
|
+
const record = isRecord3(value) ? value : null;
|
|
364
|
+
if (record?.schemaVersion !== 1 || record.reportType !== "runtime-evidence-composition-snapshot" || record.userName !== manifest.userName) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Invalid runtime evidence snapshot payload in ${bundleDir}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
const deployment = parseRuntimeEvidenceDeploymentSnapshot(record.deployment);
|
|
370
|
+
const producer = parseRuntimeEvidenceProducer(record.producer);
|
|
371
|
+
if (deployment.id !== manifest.deploymentId || deployment.deploymentCompositionId !== manifest.deploymentCompositionId) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`Runtime evidence snapshot identity mismatch in ${bundleDir}`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return { bundleDir, deployment, producer };
|
|
377
|
+
};
|
|
378
|
+
var discoverRuntimeEvidenceCompositionSnapshots = async (evidenceRoot) => {
|
|
379
|
+
const rootDir = import_node_path3.default.join(evidenceRoot, "snapshots");
|
|
380
|
+
const bundleDirs = [];
|
|
381
|
+
let deployments;
|
|
382
|
+
try {
|
|
383
|
+
deployments = await import_promises3.default.readdir(rootDir, { withFileTypes: true });
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (error.code === "ENOENT") return [];
|
|
386
|
+
throw error;
|
|
387
|
+
}
|
|
388
|
+
for (const deployment of deployments.filter((entry) => entry.isDirectory())) {
|
|
389
|
+
const deploymentDir = import_node_path3.default.join(rootDir, deployment.name);
|
|
390
|
+
const compositions = await import_promises3.default.readdir(deploymentDir, {
|
|
391
|
+
withFileTypes: true
|
|
392
|
+
});
|
|
393
|
+
bundleDirs.push(
|
|
394
|
+
...compositions.filter(
|
|
395
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.includes(".tmp-")
|
|
396
|
+
).map((entry) => import_node_path3.default.join(deploymentDir, entry.name))
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return Promise.all(
|
|
400
|
+
bundleDirs.sort().map(verifyRuntimeEvidenceCompositionSnapshot)
|
|
401
|
+
);
|
|
402
|
+
};
|
|
403
|
+
|
|
328
404
|
// src/lib/runtimeEvidenceCompositions.ts
|
|
329
405
|
var stableSnapshotIdentity = (deployment) => JSON.stringify({ ...deployment, tickers: void 0 });
|
|
330
406
|
var producerIdentity = (producer) => JSON.stringify(producer);
|
|
@@ -344,6 +420,14 @@ var loadRuntimeEvidenceCompositionSnapshots = async ({
|
|
|
344
420
|
}
|
|
345
421
|
snapshots.set(compositionId, snapshot);
|
|
346
422
|
};
|
|
423
|
+
const compositionSnapshots = await discoverRuntimeEvidenceCompositionSnapshots(publishRoot);
|
|
424
|
+
for (const snapshot of compositionSnapshots) {
|
|
425
|
+
if (snapshot.deployment.id !== currentDeployment.id) continue;
|
|
426
|
+
register({
|
|
427
|
+
deployment: { ...snapshot.deployment, tickers: void 0 },
|
|
428
|
+
producer: snapshot.producer
|
|
429
|
+
});
|
|
430
|
+
}
|
|
347
431
|
const bundleDirs = await discoverRuntimeEvidenceBundles(publishRoot);
|
|
348
432
|
for (const bundleDir of bundleDirs) {
|
|
349
433
|
const bundle = await verifyRuntimeEvidenceBundle(bundleDir);
|