@tradejs/cli 3.1.27-beta.249 → 3.1.27-beta.250
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
|
@@ -34,8 +34,8 @@ __export(runtimeEvidence_exports, {
|
|
|
34
34
|
runtimeEvidence: () => runtimeEvidence
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(runtimeEvidence_exports);
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
38
|
+
var import_node_path4 = __toESM(require("path"));
|
|
39
39
|
var import_args = __toESM(require("args"));
|
|
40
40
|
var import_chalk = __toESM(require("chalk"));
|
|
41
41
|
|
|
@@ -877,6 +877,11 @@ var discoverRuntimeEvidenceBundles = async (rootDir) => {
|
|
|
877
877
|
return bundles.sort();
|
|
878
878
|
};
|
|
879
879
|
|
|
880
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
881
|
+
var import_node_crypto3 = require("crypto");
|
|
882
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
883
|
+
var import_node_path3 = __toESM(require("path"));
|
|
884
|
+
|
|
880
885
|
// src/lib/runtimeEvidenceProducer.ts
|
|
881
886
|
var import_node_crypto2 = require("crypto");
|
|
882
887
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
@@ -936,6 +941,77 @@ var resolveRuntimeEvidenceProducer = async ({
|
|
|
936
941
|
};
|
|
937
942
|
};
|
|
938
943
|
|
|
944
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
945
|
+
var SNAPSHOT_FILE = "snapshot.json";
|
|
946
|
+
var MANIFEST_FILE = "manifest.json";
|
|
947
|
+
var COMPLETE_FILE = ".complete";
|
|
948
|
+
var isRecord3 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
949
|
+
var sha2563 = (value) => (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
|
|
950
|
+
var parseManifest2 = (value) => {
|
|
951
|
+
const manifest = isRecord3(value) ? value : null;
|
|
952
|
+
const payload = isRecord3(manifest?.payload) ? manifest.payload : null;
|
|
953
|
+
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") {
|
|
954
|
+
throw new Error("Invalid runtime evidence composition snapshot manifest");
|
|
955
|
+
}
|
|
956
|
+
return manifest;
|
|
957
|
+
};
|
|
958
|
+
var verifyRuntimeEvidenceCompositionSnapshot = async (bundleDir) => {
|
|
959
|
+
const [manifestText, payload, completeText] = await Promise.all([
|
|
960
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, MANIFEST_FILE), "utf8"),
|
|
961
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, SNAPSHOT_FILE)),
|
|
962
|
+
import_promises3.default.readFile(import_node_path3.default.join(bundleDir, COMPLETE_FILE), "utf8")
|
|
963
|
+
]);
|
|
964
|
+
const manifest = parseManifest2(JSON.parse(manifestText));
|
|
965
|
+
if (completeText.trim() !== manifest.deploymentCompositionId) {
|
|
966
|
+
throw new Error(`Invalid completion marker in ${bundleDir}`);
|
|
967
|
+
}
|
|
968
|
+
if (payload.byteLength !== manifest.payload.bytes || sha2563(payload) !== manifest.payload.sha256) {
|
|
969
|
+
throw new Error(
|
|
970
|
+
`Runtime evidence snapshot checksum mismatch in ${bundleDir}`
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
const value = JSON.parse(payload.toString("utf8"));
|
|
974
|
+
const record = isRecord3(value) ? value : null;
|
|
975
|
+
if (record?.schemaVersion !== 1 || record.reportType !== "runtime-evidence-composition-snapshot" || record.userName !== manifest.userName) {
|
|
976
|
+
throw new Error(
|
|
977
|
+
`Invalid runtime evidence snapshot payload in ${bundleDir}`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
const deployment = parseRuntimeEvidenceDeploymentSnapshot(record.deployment);
|
|
981
|
+
const producer = parseRuntimeEvidenceProducer(record.producer);
|
|
982
|
+
if (deployment.id !== manifest.deploymentId || deployment.deploymentCompositionId !== manifest.deploymentCompositionId) {
|
|
983
|
+
throw new Error(
|
|
984
|
+
`Runtime evidence snapshot identity mismatch in ${bundleDir}`
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
return { bundleDir, deployment, producer };
|
|
988
|
+
};
|
|
989
|
+
var discoverRuntimeEvidenceCompositionSnapshots = async (evidenceRoot) => {
|
|
990
|
+
const rootDir = import_node_path3.default.join(evidenceRoot, "snapshots");
|
|
991
|
+
const bundleDirs = [];
|
|
992
|
+
let deployments;
|
|
993
|
+
try {
|
|
994
|
+
deployments = await import_promises3.default.readdir(rootDir, { withFileTypes: true });
|
|
995
|
+
} catch (error) {
|
|
996
|
+
if (error.code === "ENOENT") return [];
|
|
997
|
+
throw error;
|
|
998
|
+
}
|
|
999
|
+
for (const deployment of deployments.filter((entry) => entry.isDirectory())) {
|
|
1000
|
+
const deploymentDir = import_node_path3.default.join(rootDir, deployment.name);
|
|
1001
|
+
const compositions = await import_promises3.default.readdir(deploymentDir, {
|
|
1002
|
+
withFileTypes: true
|
|
1003
|
+
});
|
|
1004
|
+
bundleDirs.push(
|
|
1005
|
+
...compositions.filter(
|
|
1006
|
+
(entry) => entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.includes(".tmp-")
|
|
1007
|
+
).map((entry) => import_node_path3.default.join(deploymentDir, entry.name))
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
return Promise.all(
|
|
1011
|
+
bundleDirs.sort().map(verifyRuntimeEvidenceCompositionSnapshot)
|
|
1012
|
+
);
|
|
1013
|
+
};
|
|
1014
|
+
|
|
939
1015
|
// src/lib/runtimeEvidenceCompositions.ts
|
|
940
1016
|
var stableSnapshotIdentity = (deployment) => JSON.stringify({ ...deployment, tickers: void 0 });
|
|
941
1017
|
var producerIdentity = (producer) => JSON.stringify(producer);
|
|
@@ -955,6 +1031,14 @@ var loadRuntimeEvidenceCompositionSnapshots = async ({
|
|
|
955
1031
|
}
|
|
956
1032
|
snapshots.set(compositionId, snapshot);
|
|
957
1033
|
};
|
|
1034
|
+
const compositionSnapshots = await discoverRuntimeEvidenceCompositionSnapshots(publishRoot);
|
|
1035
|
+
for (const snapshot of compositionSnapshots) {
|
|
1036
|
+
if (snapshot.deployment.id !== currentDeployment.id) continue;
|
|
1037
|
+
register({
|
|
1038
|
+
deployment: { ...snapshot.deployment, tickers: void 0 },
|
|
1039
|
+
producer: snapshot.producer
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
958
1042
|
const bundleDirs = await discoverRuntimeEvidenceBundles(publishRoot);
|
|
959
1043
|
for (const bundleDir of bundleDirs) {
|
|
960
1044
|
const bundle = await verifyRuntimeEvidenceBundle(bundleDir);
|
|
@@ -1070,7 +1154,7 @@ var splitRuntimeEvidenceByComposition = ({
|
|
|
1070
1154
|
};
|
|
1071
1155
|
|
|
1072
1156
|
// src/lib/runtimeLineage.ts
|
|
1073
|
-
var
|
|
1157
|
+
var import_node_crypto4 = require("crypto");
|
|
1074
1158
|
var normalizeForStableJson = (value) => {
|
|
1075
1159
|
if (Array.isArray(value)) {
|
|
1076
1160
|
return value.map(normalizeForStableJson);
|
|
@@ -1082,7 +1166,7 @@ var normalizeForStableJson = (value) => {
|
|
|
1082
1166
|
}
|
|
1083
1167
|
return value;
|
|
1084
1168
|
};
|
|
1085
|
-
var fingerprintRuntimeValue = (value) => (0,
|
|
1169
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
1086
1170
|
var runtimeLineageKey = (lineage) => [
|
|
1087
1171
|
"v3",
|
|
1088
1172
|
lineage.deploymentCompositionId,
|
|
@@ -1191,7 +1275,7 @@ var runtimeEvidence = async () => {
|
|
|
1191
1275
|
deploymentId
|
|
1192
1276
|
});
|
|
1193
1277
|
const snapshots = publishDir && producer ? await loadRuntimeEvidenceCompositionSnapshots({
|
|
1194
|
-
publishRoot:
|
|
1278
|
+
publishRoot: import_node_path4.default.resolve(projectRoot, publishDir),
|
|
1195
1279
|
currentDeployment: deploymentSnapshot,
|
|
1196
1280
|
currentProducer: producer
|
|
1197
1281
|
}) : /* @__PURE__ */ new Map([
|
|
@@ -1264,14 +1348,14 @@ var runtimeEvidence = async () => {
|
|
|
1264
1348
|
throw new Error("Runtime evidence window has no versioned runtime rows");
|
|
1265
1349
|
}
|
|
1266
1350
|
const artifact = current.artifact;
|
|
1267
|
-
const outPath =
|
|
1268
|
-
await
|
|
1269
|
-
await
|
|
1351
|
+
const outPath = import_node_path4.default.resolve(projectRoot, String(flags.out));
|
|
1352
|
+
await import_promises4.default.mkdir(import_node_path4.default.dirname(outPath), { recursive: true });
|
|
1353
|
+
await import_promises4.default.writeFile(outPath, `${JSON.stringify(artifact, null, 2)}
|
|
1270
1354
|
`, "utf8");
|
|
1271
1355
|
const published = publishDir ? await Promise.all(
|
|
1272
1356
|
artifacts.map(
|
|
1273
1357
|
({ artifact: artifact2, composition }) => publishRuntimeEvidenceBundle({
|
|
1274
|
-
publishRoot:
|
|
1358
|
+
publishRoot: import_node_path4.default.resolve(projectRoot, publishDir),
|
|
1275
1359
|
deploymentId,
|
|
1276
1360
|
userName: String(flags.user),
|
|
1277
1361
|
startTime,
|
package/dist/scripts/signals.js
CHANGED
|
@@ -51,7 +51,7 @@ var import_constants3 = require("@tradejs/core/constants");
|
|
|
51
51
|
var import_time3 = require("@tradejs/core/time");
|
|
52
52
|
var import_logger4 = require("@tradejs/infra/logger");
|
|
53
53
|
var import_runtimeHeartbeats = require("@tradejs/infra/runtimeHeartbeats");
|
|
54
|
-
var
|
|
54
|
+
var import_runtimeStrategies3 = require("@tradejs/node/runtimeStrategies");
|
|
55
55
|
|
|
56
56
|
// src/lib/marketData/windows.ts
|
|
57
57
|
var import_indicators = require("@tradejs/core/indicators");
|
|
@@ -2150,27 +2150,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
2150
2150
|
totalCredits: 0,
|
|
2151
2151
|
byPath: /* @__PURE__ */ new Map()
|
|
2152
2152
|
});
|
|
2153
|
-
var getCoinMarketCapPathLabel = (
|
|
2154
|
-
if (
|
|
2155
|
-
if (
|
|
2156
|
-
if (
|
|
2157
|
-
if (
|
|
2158
|
-
if (
|
|
2159
|
-
if (
|
|
2160
|
-
return
|
|
2161
|
-
};
|
|
2162
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
2153
|
+
var getCoinMarketCapPathLabel = (path3) => {
|
|
2154
|
+
if (path3.includes("global-metrics")) return "global";
|
|
2155
|
+
if (path3.includes("cryptocurrency/quotes")) return "reference";
|
|
2156
|
+
if (path3.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
2157
|
+
if (path3.includes("fear-and-greed")) return "fearGreed";
|
|
2158
|
+
if (path3.includes("cmc100")) return "cmc100";
|
|
2159
|
+
if (path3.includes("cmc20")) return "cmc20";
|
|
2160
|
+
return path3;
|
|
2161
|
+
};
|
|
2162
|
+
var recordCoinMarketCapFetchStats = (stats, path3, creditCount) => {
|
|
2163
2163
|
if (!stats) return;
|
|
2164
|
-
const existing = stats.byPath.get(
|
|
2164
|
+
const existing = stats.byPath.get(path3) ?? { requests: 0, credits: 0 };
|
|
2165
2165
|
existing.requests += 1;
|
|
2166
2166
|
existing.credits += creditCount ?? 0;
|
|
2167
2167
|
stats.requests += 1;
|
|
2168
2168
|
stats.totalCredits += creditCount ?? 0;
|
|
2169
|
-
stats.byPath.set(
|
|
2169
|
+
stats.byPath.set(path3, existing);
|
|
2170
2170
|
};
|
|
2171
2171
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
2172
2172
|
const byPath = [...stats.byPath.entries()].map(
|
|
2173
|
-
([
|
|
2173
|
+
([path3, item]) => `${getCoinMarketCapPathLabel(path3)}=${item.requests}req/${item.credits}cr`
|
|
2174
2174
|
).join(" ");
|
|
2175
2175
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
2176
2176
|
};
|
|
@@ -4931,6 +4931,368 @@ var buildConfiguredSignalsScopes = ({
|
|
|
4931
4931
|
}));
|
|
4932
4932
|
};
|
|
4933
4933
|
|
|
4934
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
4935
|
+
var import_node_crypto3 = require("crypto");
|
|
4936
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
4937
|
+
var import_node_path2 = __toESM(require("path"));
|
|
4938
|
+
|
|
4939
|
+
// src/lib/runtimeEvidenceDeployment.ts
|
|
4940
|
+
var import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
|
|
4941
|
+
var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
4942
|
+
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
4943
|
+
var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
|
|
4944
|
+
([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
|
|
4945
|
+
);
|
|
4946
|
+
var INTERVALS = /* @__PURE__ */ new Set([
|
|
4947
|
+
"1",
|
|
4948
|
+
"3",
|
|
4949
|
+
"5",
|
|
4950
|
+
"15",
|
|
4951
|
+
"30",
|
|
4952
|
+
"60",
|
|
4953
|
+
"120",
|
|
4954
|
+
"240",
|
|
4955
|
+
"360",
|
|
4956
|
+
"720",
|
|
4957
|
+
"D",
|
|
4958
|
+
"W",
|
|
4959
|
+
"M"
|
|
4960
|
+
]);
|
|
4961
|
+
var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
|
|
4962
|
+
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));
|
|
4963
|
+
var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
|
|
4964
|
+
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)) {
|
|
4965
|
+
throw new Error(
|
|
4966
|
+
"Runtime evidence deployment snapshot is missing or invalid"
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4969
|
+
const strategies = value.strategies.map((candidate) => {
|
|
4970
|
+
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)) {
|
|
4971
|
+
throw new Error(
|
|
4972
|
+
"Runtime evidence deployment strategy snapshot is invalid"
|
|
4973
|
+
);
|
|
4974
|
+
}
|
|
4975
|
+
if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
|
|
4976
|
+
throw new Error(
|
|
4977
|
+
`Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
|
|
4978
|
+
);
|
|
4979
|
+
}
|
|
4980
|
+
if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
|
|
4981
|
+
throw new Error(
|
|
4982
|
+
`Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
|
|
4983
|
+
);
|
|
4984
|
+
}
|
|
4985
|
+
return candidate;
|
|
4986
|
+
});
|
|
4987
|
+
const identities = /* @__PURE__ */ new Set();
|
|
4988
|
+
for (const strategy of strategies) {
|
|
4989
|
+
if (identities.has(strategy.strategyName)) {
|
|
4990
|
+
throw new Error(
|
|
4991
|
+
`Duplicate runtime evidence strategy: ${strategy.strategyName}`
|
|
4992
|
+
);
|
|
4993
|
+
}
|
|
4994
|
+
identities.add(strategy.strategyName);
|
|
4995
|
+
}
|
|
4996
|
+
return {
|
|
4997
|
+
...value,
|
|
4998
|
+
strategies
|
|
4999
|
+
};
|
|
5000
|
+
};
|
|
5001
|
+
var resolveRuntimeEvidenceDeploymentSnapshot = async ({
|
|
5002
|
+
userName,
|
|
5003
|
+
projectRoot,
|
|
5004
|
+
deploymentId
|
|
5005
|
+
}) => {
|
|
5006
|
+
const [deployment, strategies] = await Promise.all([
|
|
5007
|
+
(0, import_runtimeStrategies2.getRuntimeDeployment)({ userName, projectRoot, deploymentId }),
|
|
5008
|
+
(0, import_runtimeStrategies2.loadResolvedRuntimeStrategies)({ userName, projectRoot, deploymentId })
|
|
5009
|
+
]);
|
|
5010
|
+
if (!deployment) {
|
|
5011
|
+
throw new Error(`Runtime deployment not found: ${deploymentId}`);
|
|
5012
|
+
}
|
|
5013
|
+
return {
|
|
5014
|
+
schemaVersion: 2,
|
|
5015
|
+
id: deployment.id,
|
|
5016
|
+
deploymentCompositionId: deployment.deploymentCompositionId,
|
|
5017
|
+
label: deployment.label,
|
|
5018
|
+
connectorName: deployment.connectorName,
|
|
5019
|
+
provider: deployment.provider,
|
|
5020
|
+
accountId: deployment.accountId,
|
|
5021
|
+
enabled: deployment.enabled,
|
|
5022
|
+
strategies: strategies.map(
|
|
5023
|
+
({
|
|
5024
|
+
strategyName,
|
|
5025
|
+
strategyRevision,
|
|
5026
|
+
enabled,
|
|
5027
|
+
controlState,
|
|
5028
|
+
interval,
|
|
5029
|
+
universe,
|
|
5030
|
+
accountId,
|
|
5031
|
+
strategyPackage,
|
|
5032
|
+
strategyPackageVersion,
|
|
5033
|
+
strategyDependencyVersions,
|
|
5034
|
+
runtimePackageVersion,
|
|
5035
|
+
strategyConfig,
|
|
5036
|
+
selection
|
|
5037
|
+
}) => ({
|
|
5038
|
+
strategyName,
|
|
5039
|
+
strategyRevision,
|
|
5040
|
+
enabled,
|
|
5041
|
+
controlState,
|
|
5042
|
+
interval,
|
|
5043
|
+
universe,
|
|
5044
|
+
...accountId ? { accountId } : {},
|
|
5045
|
+
strategyPackage,
|
|
5046
|
+
strategyPackageVersion,
|
|
5047
|
+
strategyDependencyVersions,
|
|
5048
|
+
runtimePackageVersion,
|
|
5049
|
+
strategyConfig,
|
|
5050
|
+
...selection ? { selection } : {}
|
|
5051
|
+
})
|
|
5052
|
+
),
|
|
5053
|
+
...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
|
|
5054
|
+
...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
|
|
5055
|
+
};
|
|
5056
|
+
};
|
|
5057
|
+
|
|
5058
|
+
// src/lib/runtimeEvidenceProducer.ts
|
|
5059
|
+
var import_node_crypto2 = require("crypto");
|
|
5060
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
5061
|
+
var import_node_path = __toESM(require("path"));
|
|
5062
|
+
var isRecord2 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
5063
|
+
var sha256 = (value) => (0, import_node_crypto2.createHash)("sha256").update(value).digest("hex");
|
|
5064
|
+
var parseRuntimeEvidenceProducer = (value) => {
|
|
5065
|
+
const producer = isRecord2(value) ? value : null;
|
|
5066
|
+
const runtimePackageManifest = isRecord2(producer?.runtimePackageManifest) ? producer.runtimePackageManifest : null;
|
|
5067
|
+
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)) {
|
|
5068
|
+
throw new Error("Runtime evidence producer image identity is invalid");
|
|
5069
|
+
}
|
|
5070
|
+
return producer;
|
|
5071
|
+
};
|
|
5072
|
+
var resolveRuntimeEvidenceProducer = async ({
|
|
5073
|
+
projectRoot,
|
|
5074
|
+
required
|
|
5075
|
+
}) => {
|
|
5076
|
+
const projectSha = String(process.env.TRADEJS_PROJECT_SHA ?? "").trim();
|
|
5077
|
+
const imageDigest = String(
|
|
5078
|
+
process.env.TRADEJS_PROJECT_IMAGE_DIGEST ?? ""
|
|
5079
|
+
).trim();
|
|
5080
|
+
if (!projectSha && !imageDigest && !required) {
|
|
5081
|
+
return null;
|
|
5082
|
+
}
|
|
5083
|
+
if (!/^[a-f0-9]{40}$/.test(projectSha)) {
|
|
5084
|
+
throw new Error(
|
|
5085
|
+
"TRADEJS_PROJECT_SHA must be a full lowercase Git SHA before publishing runtime evidence"
|
|
5086
|
+
);
|
|
5087
|
+
}
|
|
5088
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(imageDigest)) {
|
|
5089
|
+
throw new Error(
|
|
5090
|
+
"TRADEJS_PROJECT_IMAGE_DIGEST must be an immutable sha256 digest before publishing runtime evidence"
|
|
5091
|
+
);
|
|
5092
|
+
}
|
|
5093
|
+
const manifestPath = import_node_path.default.join(projectRoot, "runtime-package-manifest.json");
|
|
5094
|
+
let manifest;
|
|
5095
|
+
try {
|
|
5096
|
+
manifest = await import_promises.default.readFile(manifestPath);
|
|
5097
|
+
} catch (error) {
|
|
5098
|
+
if (!required && error.code === "ENOENT") {
|
|
5099
|
+
return null;
|
|
5100
|
+
}
|
|
5101
|
+
throw new Error(
|
|
5102
|
+
`Runtime package manifest is required for runtime evidence: ${manifestPath}`,
|
|
5103
|
+
{ cause: error }
|
|
5104
|
+
);
|
|
5105
|
+
}
|
|
5106
|
+
return {
|
|
5107
|
+
schemaVersion: 1,
|
|
5108
|
+
projectSha,
|
|
5109
|
+
imageDigest,
|
|
5110
|
+
runtimePackageManifest: {
|
|
5111
|
+
file: "runtime-package-manifest.json",
|
|
5112
|
+
sha256: sha256(manifest)
|
|
5113
|
+
}
|
|
5114
|
+
};
|
|
5115
|
+
};
|
|
5116
|
+
|
|
5117
|
+
// src/lib/runtimeEvidenceCompositionSnapshots.ts
|
|
5118
|
+
var SNAPSHOT_FILE = "snapshot.json";
|
|
5119
|
+
var MANIFEST_FILE = "manifest.json";
|
|
5120
|
+
var COMPLETE_FILE = ".complete";
|
|
5121
|
+
var createRuntimeEvidenceCompositionSnapshotRecorder = ({
|
|
5122
|
+
persist,
|
|
5123
|
+
onError
|
|
5124
|
+
}) => {
|
|
5125
|
+
const completed = /* @__PURE__ */ new Set();
|
|
5126
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
5127
|
+
return {
|
|
5128
|
+
observe(compositionId) {
|
|
5129
|
+
if (completed.has(compositionId) || inFlight.has(compositionId)) return;
|
|
5130
|
+
inFlight.add(compositionId);
|
|
5131
|
+
void Promise.resolve().then(() => persist(compositionId)).then(() => completed.add(compositionId)).catch((error) => onError(compositionId, error)).finally(() => inFlight.delete(compositionId));
|
|
5132
|
+
}
|
|
5133
|
+
};
|
|
5134
|
+
};
|
|
5135
|
+
var isRecord3 = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
5136
|
+
var sha2562 = (value) => (0, import_node_crypto3.createHash)("sha256").update(value).digest("hex");
|
|
5137
|
+
var safeSegment = (value, label) => {
|
|
5138
|
+
const normalized = value.trim();
|
|
5139
|
+
if (!normalized || !/^[a-zA-Z0-9._:-]+$/.test(normalized)) {
|
|
5140
|
+
throw new Error(`Invalid runtime evidence snapshot ${label}`);
|
|
5141
|
+
}
|
|
5142
|
+
return normalized;
|
|
5143
|
+
};
|
|
5144
|
+
var isExistingDestinationError = (error) => {
|
|
5145
|
+
const code = error.code;
|
|
5146
|
+
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
5147
|
+
};
|
|
5148
|
+
var parseManifest = (value) => {
|
|
5149
|
+
const manifest = isRecord3(value) ? value : null;
|
|
5150
|
+
const payload = isRecord3(manifest?.payload) ? manifest.payload : null;
|
|
5151
|
+
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") {
|
|
5152
|
+
throw new Error("Invalid runtime evidence composition snapshot manifest");
|
|
5153
|
+
}
|
|
5154
|
+
return manifest;
|
|
5155
|
+
};
|
|
5156
|
+
var verifyRuntimeEvidenceCompositionSnapshot = async (bundleDir) => {
|
|
5157
|
+
const [manifestText, payload, completeText] = await Promise.all([
|
|
5158
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, MANIFEST_FILE), "utf8"),
|
|
5159
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, SNAPSHOT_FILE)),
|
|
5160
|
+
import_promises2.default.readFile(import_node_path2.default.join(bundleDir, COMPLETE_FILE), "utf8")
|
|
5161
|
+
]);
|
|
5162
|
+
const manifest = parseManifest(JSON.parse(manifestText));
|
|
5163
|
+
if (completeText.trim() !== manifest.deploymentCompositionId) {
|
|
5164
|
+
throw new Error(`Invalid completion marker in ${bundleDir}`);
|
|
5165
|
+
}
|
|
5166
|
+
if (payload.byteLength !== manifest.payload.bytes || sha2562(payload) !== manifest.payload.sha256) {
|
|
5167
|
+
throw new Error(
|
|
5168
|
+
`Runtime evidence snapshot checksum mismatch in ${bundleDir}`
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
const value = JSON.parse(payload.toString("utf8"));
|
|
5172
|
+
const record = isRecord3(value) ? value : null;
|
|
5173
|
+
if (record?.schemaVersion !== 1 || record.reportType !== "runtime-evidence-composition-snapshot" || record.userName !== manifest.userName) {
|
|
5174
|
+
throw new Error(
|
|
5175
|
+
`Invalid runtime evidence snapshot payload in ${bundleDir}`
|
|
5176
|
+
);
|
|
5177
|
+
}
|
|
5178
|
+
const deployment = parseRuntimeEvidenceDeploymentSnapshot(record.deployment);
|
|
5179
|
+
const producer = parseRuntimeEvidenceProducer(record.producer);
|
|
5180
|
+
if (deployment.id !== manifest.deploymentId || deployment.deploymentCompositionId !== manifest.deploymentCompositionId) {
|
|
5181
|
+
throw new Error(
|
|
5182
|
+
`Runtime evidence snapshot identity mismatch in ${bundleDir}`
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
return { bundleDir, deployment, producer };
|
|
5186
|
+
};
|
|
5187
|
+
var sameSnapshot = (snapshot, deployment, producer) => JSON.stringify(snapshot.deployment) === JSON.stringify(deployment) && JSON.stringify(snapshot.producer) === JSON.stringify(producer);
|
|
5188
|
+
var publishRuntimeEvidenceCompositionSnapshot = async ({
|
|
5189
|
+
evidenceRoot,
|
|
5190
|
+
userName,
|
|
5191
|
+
deployment,
|
|
5192
|
+
producer
|
|
5193
|
+
}) => {
|
|
5194
|
+
const deploymentId = safeSegment(deployment.id, "deployment id");
|
|
5195
|
+
const compositionId = safeSegment(
|
|
5196
|
+
deployment.deploymentCompositionId,
|
|
5197
|
+
"composition id"
|
|
5198
|
+
);
|
|
5199
|
+
const createdAt = Date.now();
|
|
5200
|
+
const snapshot = {
|
|
5201
|
+
schemaVersion: 1,
|
|
5202
|
+
reportType: "runtime-evidence-composition-snapshot",
|
|
5203
|
+
createdAt,
|
|
5204
|
+
userName,
|
|
5205
|
+
deployment,
|
|
5206
|
+
producer
|
|
5207
|
+
};
|
|
5208
|
+
const payload = `${JSON.stringify(snapshot, null, 2)}
|
|
5209
|
+
`;
|
|
5210
|
+
const manifest = {
|
|
5211
|
+
schemaVersion: 1,
|
|
5212
|
+
reportType: "runtime-evidence-composition-snapshot",
|
|
5213
|
+
createdAt,
|
|
5214
|
+
userName,
|
|
5215
|
+
deploymentId,
|
|
5216
|
+
deploymentCompositionId: compositionId,
|
|
5217
|
+
payload: {
|
|
5218
|
+
file: SNAPSHOT_FILE,
|
|
5219
|
+
sha256: sha2562(payload),
|
|
5220
|
+
bytes: Buffer.byteLength(payload)
|
|
5221
|
+
}
|
|
5222
|
+
};
|
|
5223
|
+
const snapshotsRoot = import_node_path2.default.join(evidenceRoot, "snapshots", deploymentId);
|
|
5224
|
+
const bundleDir = import_node_path2.default.join(snapshotsRoot, compositionId);
|
|
5225
|
+
const temporaryDir = import_node_path2.default.join(
|
|
5226
|
+
evidenceRoot,
|
|
5227
|
+
"incoming",
|
|
5228
|
+
`snapshot-${compositionId}.tmp-${(0, import_node_crypto3.randomUUID)()}`
|
|
5229
|
+
);
|
|
5230
|
+
await import_promises2.default.mkdir(temporaryDir, { recursive: true });
|
|
5231
|
+
try {
|
|
5232
|
+
await Promise.all([
|
|
5233
|
+
import_promises2.default.writeFile(import_node_path2.default.join(temporaryDir, SNAPSHOT_FILE), payload, "utf8"),
|
|
5234
|
+
import_promises2.default.writeFile(
|
|
5235
|
+
import_node_path2.default.join(temporaryDir, MANIFEST_FILE),
|
|
5236
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
5237
|
+
`,
|
|
5238
|
+
"utf8"
|
|
5239
|
+
)
|
|
5240
|
+
]);
|
|
5241
|
+
await import_promises2.default.writeFile(
|
|
5242
|
+
import_node_path2.default.join(temporaryDir, COMPLETE_FILE),
|
|
5243
|
+
`${compositionId}
|
|
5244
|
+
`,
|
|
5245
|
+
"utf8"
|
|
5246
|
+
);
|
|
5247
|
+
await import_promises2.default.mkdir(snapshotsRoot, { recursive: true });
|
|
5248
|
+
try {
|
|
5249
|
+
await import_promises2.default.rename(temporaryDir, bundleDir);
|
|
5250
|
+
} catch (error) {
|
|
5251
|
+
if (!isExistingDestinationError(error)) throw error;
|
|
5252
|
+
await import_promises2.default.rm(temporaryDir, { recursive: true, force: true });
|
|
5253
|
+
}
|
|
5254
|
+
} catch (error) {
|
|
5255
|
+
await import_promises2.default.rm(temporaryDir, { recursive: true, force: true });
|
|
5256
|
+
throw error;
|
|
5257
|
+
}
|
|
5258
|
+
const verified = await verifyRuntimeEvidenceCompositionSnapshot(bundleDir);
|
|
5259
|
+
if (!sameSnapshot(verified, deployment, producer)) {
|
|
5260
|
+
throw new Error(
|
|
5261
|
+
`Conflicting runtime evidence snapshot for composition ${compositionId}`
|
|
5262
|
+
);
|
|
5263
|
+
}
|
|
5264
|
+
return verified;
|
|
5265
|
+
};
|
|
5266
|
+
var captureRuntimeEvidenceCompositionSnapshot = async ({
|
|
5267
|
+
projectRoot,
|
|
5268
|
+
userName,
|
|
5269
|
+
deploymentId,
|
|
5270
|
+
expectedCompositionId
|
|
5271
|
+
}) => {
|
|
5272
|
+
const [deployment, producer] = await Promise.all([
|
|
5273
|
+
resolveRuntimeEvidenceDeploymentSnapshot({
|
|
5274
|
+
userName,
|
|
5275
|
+
projectRoot,
|
|
5276
|
+
deploymentId
|
|
5277
|
+
}),
|
|
5278
|
+
resolveRuntimeEvidenceProducer({ projectRoot, required: true })
|
|
5279
|
+
]);
|
|
5280
|
+
if (!producer) {
|
|
5281
|
+
throw new Error("Runtime evidence producer identity is required");
|
|
5282
|
+
}
|
|
5283
|
+
if (deployment.deploymentCompositionId !== expectedCompositionId) {
|
|
5284
|
+
throw new Error(
|
|
5285
|
+
`Runtime deployment composition changed while capturing snapshot: expected ${expectedCompositionId}, received ${deployment.deploymentCompositionId}`
|
|
5286
|
+
);
|
|
5287
|
+
}
|
|
5288
|
+
await publishRuntimeEvidenceCompositionSnapshot({
|
|
5289
|
+
evidenceRoot: import_node_path2.default.join(projectRoot, "data", "runtime-evidence"),
|
|
5290
|
+
userName,
|
|
5291
|
+
deployment,
|
|
5292
|
+
producer
|
|
5293
|
+
});
|
|
5294
|
+
};
|
|
5295
|
+
|
|
4934
5296
|
// src/lib/signals/runner.ts
|
|
4935
5297
|
var SLOW_SIGNALS_WARNING_MS = 10 * 6e4;
|
|
4936
5298
|
var formatDuration2 = (durationMs) => `${(durationMs / 1e3).toFixed(1)}s`;
|
|
@@ -4952,6 +5314,19 @@ var resolveNonNegativeInteger = (value, fallback) => {
|
|
|
4952
5314
|
var createSignalsRunner = (config2) => {
|
|
4953
5315
|
const interval = config2.interval;
|
|
4954
5316
|
const projectRoot = config2.projectRoot?.trim() || String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
5317
|
+
const compositionSnapshotRecorder = process.env.TRADEJS_PROJECT_SHA && process.env.TRADEJS_PROJECT_IMAGE_DIGEST && config2.deploymentId ? createRuntimeEvidenceCompositionSnapshotRecorder({
|
|
5318
|
+
persist: (compositionId) => captureRuntimeEvidenceCompositionSnapshot({
|
|
5319
|
+
projectRoot,
|
|
5320
|
+
userName: config2.userName,
|
|
5321
|
+
deploymentId: config2.deploymentId,
|
|
5322
|
+
expectedCompositionId: compositionId
|
|
5323
|
+
}),
|
|
5324
|
+
onError: (compositionId, error) => import_logger4.logger.warn(
|
|
5325
|
+
"runtime evidence snapshot unavailable; trading continues and the next cycle will retry (composition=%s): %s",
|
|
5326
|
+
compositionId,
|
|
5327
|
+
error instanceof Error ? error.message : String(error)
|
|
5328
|
+
)
|
|
5329
|
+
}) : null;
|
|
4955
5330
|
const resolveSignalsDaemonMaxLiveBars = (runtimeInterval = interval) => {
|
|
4956
5331
|
const intervalMinutes = Number(runtimeInterval);
|
|
4957
5332
|
const preloadBars = Math.ceil(
|
|
@@ -4993,7 +5368,7 @@ var createSignalsRunner = (config2) => {
|
|
|
4993
5368
|
return connectorName;
|
|
4994
5369
|
};
|
|
4995
5370
|
const createSignalsSession2 = async (lifecycle, scope = {}) => {
|
|
4996
|
-
const deployment = config2.deploymentId ? await (0,
|
|
5371
|
+
const deployment = config2.deploymentId ? await (0, import_runtimeStrategies3.getRuntimeDeployment)({
|
|
4997
5372
|
userName: config2.userName,
|
|
4998
5373
|
projectRoot,
|
|
4999
5374
|
deploymentId: config2.deploymentId
|
|
@@ -5494,7 +5869,7 @@ var createSignalsRunner = (config2) => {
|
|
|
5494
5869
|
if (!config2.deploymentId) {
|
|
5495
5870
|
throw new Error("Runtime deployment id is required");
|
|
5496
5871
|
}
|
|
5497
|
-
const configuredDeployment = await (0,
|
|
5872
|
+
const configuredDeployment = await (0, import_runtimeStrategies3.getRuntimeDeployment)({
|
|
5498
5873
|
userName: config2.userName,
|
|
5499
5874
|
projectRoot,
|
|
5500
5875
|
deploymentId: config2.deploymentId
|
|
@@ -5528,7 +5903,7 @@ var createSignalsRunner = (config2) => {
|
|
|
5528
5903
|
throw new Error("Runtime deployment id is required");
|
|
5529
5904
|
}
|
|
5530
5905
|
const deploymentId = config2.deploymentId;
|
|
5531
|
-
const initialDeployment = await (0,
|
|
5906
|
+
const initialDeployment = await (0, import_runtimeStrategies3.getRuntimeDeployment)({
|
|
5532
5907
|
userName: config2.userName,
|
|
5533
5908
|
projectRoot,
|
|
5534
5909
|
deploymentId
|
|
@@ -5553,7 +5928,7 @@ var createSignalsRunner = (config2) => {
|
|
|
5553
5928
|
settleDelayMs: resolveNonNegativeInteger(config2.settleDelayMs, 5e3),
|
|
5554
5929
|
signal: abortController.signal,
|
|
5555
5930
|
runCycle: async () => {
|
|
5556
|
-
const currentDeployment = await (0,
|
|
5931
|
+
const currentDeployment = await (0, import_runtimeStrategies3.getRuntimeDeployment)({
|
|
5557
5932
|
userName: config2.userName,
|
|
5558
5933
|
projectRoot,
|
|
5559
5934
|
deploymentId
|
|
@@ -5561,6 +5936,9 @@ var createSignalsRunner = (config2) => {
|
|
|
5561
5936
|
if (!currentDeployment) {
|
|
5562
5937
|
throw new Error(`Runtime deployment not found: ${deploymentId}`);
|
|
5563
5938
|
}
|
|
5939
|
+
compositionSnapshotRecorder?.observe(
|
|
5940
|
+
currentDeployment.deploymentCompositionId
|
|
5941
|
+
);
|
|
5564
5942
|
const scopes = await loadConfiguredSignalsScopes(currentDeployment);
|
|
5565
5943
|
for (const [key, staleSession] of sessions) {
|
|
5566
5944
|
if (scopes.has(key)) continue;
|