@tradejs/cli 2.0.15 → 2.0.16

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.
@@ -34,7 +34,8 @@ __export(aiTrainResearch_exports, {
34
34
  fingerprintResearchValue: () => fingerprintResearchValue,
35
35
  summarizeAiTrainCoverage: () => summarizeAiTrainCoverage,
36
36
  summarizeAiTrainRejectReasons: () => summarizeAiTrainRejectReasons,
37
- summarizeAiTrainTerminalWindows: () => summarizeAiTrainTerminalWindows
37
+ summarizeAiTrainTerminalWindows: () => summarizeAiTrainTerminalWindows,
38
+ writeAiTrainResearchSnapshot: () => writeAiTrainResearchSnapshot
38
39
  });
39
40
  module.exports = __toCommonJS(aiTrainResearch_exports);
40
41
  var import_node_child_process = require("child_process");
@@ -350,6 +351,20 @@ var DERIVATIVES_CONTEXT_DATA_MODEL = {
350
351
  hyperliquidWhaleMinCoveragePct: 0.8,
351
352
  hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
352
353
  };
354
+ var writeAiTrainResearchSnapshot = async ({
355
+ outputPath,
356
+ result
357
+ }) => {
358
+ const resolvedPath = import_node_path.default.resolve(outputPath);
359
+ await import_promises.default.mkdir(import_node_path.default.dirname(resolvedPath), { recursive: true });
360
+ await import_promises.default.writeFile(
361
+ resolvedPath,
362
+ `${JSON.stringify(result, null, 2)}
363
+ `,
364
+ "utf8"
365
+ );
366
+ return resolvedPath;
367
+ };
353
368
  var normalizeForStableJson = (value) => {
354
369
  if (Array.isArray(value)) {
355
370
  return value.map(normalizeForStableJson);
@@ -464,6 +479,7 @@ var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
464
479
  `packages/strategies/src/${strategyName}/guardrails.ts`,
465
480
  `packages/strategies/src/${strategyName}/pockets.ts`,
466
481
  `packages/strategies/src/${strategyName}/config.ts`,
482
+ "packages/strategies/src/shared/localAiGate.ts",
467
483
  "packages/node/src/ai.ts"
468
484
  ];
469
485
  const optionalSourceEntries = await Promise.all(
@@ -532,5 +548,6 @@ var buildAiTrainLineage = async ({
532
548
  fingerprintResearchValue,
533
549
  summarizeAiTrainCoverage,
534
550
  summarizeAiTrainRejectReasons,
535
- summarizeAiTrainTerminalWindows
551
+ summarizeAiTrainTerminalWindows,
552
+ writeAiTrainResearchSnapshot
536
553
  });
@@ -1637,6 +1637,7 @@ var require_list_it = __commonJS({
1637
1637
  var marketContextPrepare_exports = {};
1638
1638
  __export(marketContextPrepare_exports, {
1639
1639
  prepareMarketContextForRun: () => prepareMarketContextForRun,
1640
+ resolveMarketContextRunRequirements: () => resolveMarketContextRunRequirements,
1640
1641
  shouldPrepareBinanceMarketContextForRun: () => shouldPrepareBinanceMarketContextForRun,
1641
1642
  shouldPrepareCoinMarketCapContextForRun: () => shouldPrepareCoinMarketCapContextForRun,
1642
1643
  shouldPrepareDerivativesContextForRun: () => shouldPrepareDerivativesContextForRun,
@@ -1644,6 +1645,8 @@ __export(marketContextPrepare_exports, {
1644
1645
  });
1645
1646
  module.exports = __toCommonJS(marketContextPrepare_exports);
1646
1647
  var import_chalk4 = __toESM(require("chalk"));
1648
+ var import_timescale5 = require("@tradejs/infra/timescale");
1649
+ var import_registry = require("@tradejs/node/registry");
1647
1650
 
1648
1651
  // src/lib/binanceMarketContextBackfill.ts
1649
1652
  var import_progress = __toESM(require("progress"));
@@ -4904,6 +4907,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
4904
4907
  };
4905
4908
 
4906
4909
  // src/lib/marketContextPrepare.ts
4910
+ var uniqueReasons = (reasons) => [
4911
+ ...new Set(reasons)
4912
+ ];
4907
4913
  var shouldPrepareDerivativesContextForRun = (params) => {
4908
4914
  if (params.universe === "tradfi") return false;
4909
4915
  if (params.mode === "signals") {
@@ -4911,6 +4917,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
4911
4917
  cacheOnly: params.cacheOnly
4912
4918
  });
4913
4919
  }
4920
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
4921
+ return false;
4922
+ }
4914
4923
  return shouldBackfillDerivativesContextForBacktest({
4915
4924
  aiEnabled: Boolean(params.aiEnabled),
4916
4925
  cacheOnly: params.cacheOnly,
@@ -4920,6 +4929,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
4920
4929
  var shouldPrepareBinanceMarketContextForRun = (params) => {
4921
4930
  if (params.universe === "tradfi") return false;
4922
4931
  if (params.mode === "backtest") {
4932
+ if (!params.aiEnabled && !params.mlEnabled) return false;
4923
4933
  return shouldBackfillBinanceMarketContextForBacktest({
4924
4934
  aiEnabled: Boolean(params.aiEnabled),
4925
4935
  cacheOnly: params.cacheOnly,
@@ -4938,6 +4948,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
4938
4948
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
4939
4949
  if (params.universe === "tradfi") return false;
4940
4950
  if (params.mode === "backtest") {
4951
+ if (!params.aiEnabled && !params.mlEnabled) return false;
4941
4952
  return shouldBackfillCoinMarketCapContextForBacktest({
4942
4953
  aiEnabled: Boolean(params.aiEnabled),
4943
4954
  cacheOnly: params.cacheOnly,
@@ -4953,10 +4964,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
4953
4964
  cacheOnly: params.cacheOnly
4954
4965
  });
4955
4966
  };
4956
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
4957
- const strategyRequiresContext = params.strategyNames?.includes(
4958
- "HyperliquidConsensus"
4959
- );
4967
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
4968
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
4960
4969
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
4961
4970
  return false;
4962
4971
  }
@@ -4967,6 +4976,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
4967
4976
  if (strategyRequiresContext) return true;
4968
4977
  return ["1", "true", "yes", "on"].includes(configured);
4969
4978
  };
4979
+ var resolveCoreContextSources = async (params) => {
4980
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
4981
+ if (!strategyNames.length) return [];
4982
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
4983
+ return [
4984
+ ...new Set(
4985
+ strategyNames.flatMap(
4986
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
4987
+ )
4988
+ )
4989
+ ];
4990
+ };
4991
+ var resolveMarketContextRunRequirements = async (params) => {
4992
+ const coreContextSources = await resolveCoreContextSources(params);
4993
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
4994
+ const isCrypto = params.universe !== "tradfi";
4995
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
4996
+ const runtimeReadsContext = params.mode !== "backtest";
4997
+ const reasonsFor = (source) => uniqueReasons([
4998
+ ...coreContextSources.includes(source) ? ["core"] : [],
4999
+ ...params.aiEnabled ? ["ai"] : [],
5000
+ ...params.mlEnabled ? ["ml"] : [],
5001
+ ...runtimeReadsContext ? ["runtime"] : []
5002
+ ]);
5003
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5004
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5005
+ return {
5006
+ binance: {
5007
+ read: readsStandardContext || coreContextSources.includes("binance"),
5008
+ backfill: shouldPrepareBinanceMarketContextForRun(
5009
+ paramsWithCoreDemand("binance")
5010
+ ),
5011
+ requiredBy: reasonsFor("binance")
5012
+ },
5013
+ coinmarketcap: {
5014
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5015
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5016
+ paramsWithCoreDemand("coinmarketcap")
5017
+ ),
5018
+ requiredBy: reasonsFor("coinmarketcap")
5019
+ },
5020
+ derivatives: {
5021
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5022
+ backfill: shouldPrepareDerivativesContextForRun(
5023
+ paramsWithCoreDemand("derivatives")
5024
+ ),
5025
+ requiredBy: reasonsFor("derivatives")
5026
+ },
5027
+ hyperliquidWhales: {
5028
+ read: readsHyperliquid,
5029
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5030
+ params,
5031
+ coreContextSources
5032
+ ),
5033
+ requiredBy: reasonsFor("hyperliquidWhales")
5034
+ }
5035
+ };
5036
+ };
4970
5037
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
4971
5038
  var buildCoinMarketCapBackfillParams = (params) => ({
4972
5039
  userName: params.userName,
@@ -4995,7 +5062,15 @@ var prepareMarketContextForRun = async (params) => {
4995
5062
  console.log(import_chalk4.default.gray(message));
4996
5063
  });
4997
5064
  const timeOperation2 = (label, operation) => timeOperation(label, operation, log);
4998
- if (shouldPrepareDerivativesContextForRun(params)) {
5065
+ const requirements = await resolveMarketContextRunRequirements(params);
5066
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5067
+ if (readableSources.length) {
5068
+ await timeOperation2(
5069
+ "market context schema ensure",
5070
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5071
+ );
5072
+ }
5073
+ if (requirements.derivatives.backfill) {
4999
5074
  await timeOperation2(
5000
5075
  "derivatives context backfill",
5001
5076
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5003,7 +5078,7 @@ var prepareMarketContextForRun = async (params) => {
5003
5078
  )
5004
5079
  );
5005
5080
  }
5006
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5081
+ if (requirements.hyperliquidWhales.backfill) {
5007
5082
  await timeOperation2(
5008
5083
  "hyperliquid whale context backfill",
5009
5084
  () => backfillHyperliquidWhaleContext({
@@ -5015,13 +5090,13 @@ var prepareMarketContextForRun = async (params) => {
5015
5090
  })
5016
5091
  );
5017
5092
  }
5018
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5093
+ if (requirements.binance.backfill) {
5019
5094
  await timeOperation2("binance market context backfill", () => {
5020
5095
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5021
5096
  return backfill(buildBinanceMarketBackfillParams(params));
5022
5097
  });
5023
5098
  }
5024
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5099
+ if (requirements.coinmarketcap.backfill) {
5025
5100
  await timeOperation2(
5026
5101
  "coinmarketcap historical context backfill",
5027
5102
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5029,10 +5104,12 @@ var prepareMarketContextForRun = async (params) => {
5029
5104
  )
5030
5105
  );
5031
5106
  }
5107
+ return requirements;
5032
5108
  };
5033
5109
  // Annotate the CommonJS export names for ESM import in node:
5034
5110
  0 && (module.exports = {
5035
5111
  prepareMarketContextForRun,
5112
+ resolveMarketContextRunRequirements,
5036
5113
  shouldPrepareBinanceMarketContextForRun,
5037
5114
  shouldPrepareCoinMarketCapContextForRun,
5038
5115
  shouldPrepareDerivativesContextForRun,
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
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
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/lib/researchAgent.ts
@@ -39,24 +29,14 @@ __export(researchAgent_exports, {
39
29
  buildResearchAgentPrBody: () => buildResearchAgentPrBody,
40
30
  buildResearchAgentPrTitle: () => buildResearchAgentPrTitle,
41
31
  getResearchAgentAllowedPathPrefixes: () => getResearchAgentAllowedPathPrefixes,
42
- getResearchAgentNotePath: () => getResearchAgentNotePath,
43
32
  normalizeDiffOutput: () => normalizeDiffOutput,
44
33
  parseChangedFilesFromDiff: () => parseChangedFilesFromDiff,
45
- parseGithubRepositoryFromRemote: () => parseGithubRepositoryFromRemote,
46
- toUpperSnakeCase: () => toUpperSnakeCase
34
+ parseGithubRepositoryFromRemote: () => parseGithubRepositoryFromRemote
47
35
  });
48
36
  module.exports = __toCommonJS(researchAgent_exports);
49
- var import_node_path = __toESM(require("path"));
50
37
  var RESEARCH_AGENT_MODEL = "openai/gpt-5.4";
51
38
  var RESEARCH_AGENT_REASONING_EFFORT = "medium";
52
39
  var RESEARCH_AGENT_BRANCH_PREFIX = "codex/research";
53
- var toUpperSnakeCase = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
54
- var RESEARCH_NOTE_FILE_OVERRIDES = {
55
- TrendLine: "AI_TRENDLINE_REPLAY_NOTES.md",
56
- ReverseTrendLine: "AI_REVERSE_TRENDLINE_REPLAY_NOTES.md",
57
- AdaptiveMomentumRibbon: "AI_ADAPTIVE_MOMENTUM_RIBBON_REPLAY_NOTES.md",
58
- VolumeDivergence: "AI_VOLUME_DIVERGENCE_REPLAY_NOTES.md"
59
- };
60
40
  var buildResearchAgentBranchName = (strategy, runId) => {
61
41
  const normalizedStrategy = strategy.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
62
42
  const normalizedRunId = runId.trim().replace(/[^a-zA-Z0-9-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
@@ -64,13 +44,8 @@ var buildResearchAgentBranchName = (strategy, runId) => {
64
44
  };
65
45
  var buildResearchAgentCommitMessage = (strategy, runId) => `Research agent: ${strategy} follow-up for ${runId}`;
66
46
  var buildResearchAgentPrTitle = (strategy, runId) => `Research agent: ${strategy} follow-up for ${runId}`;
67
- var getResearchAgentNotePath = (strategy) => import_node_path.default.join(
68
- "notes",
69
- RESEARCH_NOTE_FILE_OVERRIDES[strategy] || `AI_${toUpperSnakeCase(strategy)}_REPLAY_NOTES.md`
70
- );
71
47
  var getResearchAgentAllowedPathPrefixes = (strategy) => [
72
- `packages/strategies/src/${strategy}/`,
73
- getResearchAgentNotePath(strategy)
48
+ `packages/strategies/src/${strategy}/`
74
49
  ];
75
50
  var parseGithubRepositoryFromRemote = (value) => {
76
51
  const trimmed = value.trim();
@@ -187,9 +162,7 @@ ${diffText}`)) {
187
162
  buildResearchAgentPrBody,
188
163
  buildResearchAgentPrTitle,
189
164
  getResearchAgentAllowedPathPrefixes,
190
- getResearchAgentNotePath,
191
165
  normalizeDiffOutput,
192
166
  parseChangedFilesFromDiff,
193
- parseGithubRepositoryFromRemote,
194
- toUpperSnakeCase
167
+ parseGithubRepositoryFromRemote
195
168
  });
@@ -25,6 +25,7 @@ __export(runtimeDebugEvidence_exports, {
25
25
  buildRuntimeDebugEvaluationId: () => buildRuntimeDebugEvaluationId,
26
26
  buildRuntimeDebugReportAttachment: () => buildRuntimeDebugReportAttachment,
27
27
  buildRuntimeDebugReportPayload: () => buildRuntimeDebugReportPayload,
28
+ buildRuntimeEvidenceReportPayload: () => buildRuntimeEvidenceReportPayload,
28
29
  collectRuntimeDebugEvidence: () => collectRuntimeDebugEvidence
29
30
  });
30
31
  module.exports = __toCommonJS(runtimeDebugEvidence_exports);
@@ -122,6 +123,29 @@ var loadRuntimeTrades = async (userName, {
122
123
  const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
123
124
  return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
124
125
  };
126
+ var loadRuntimeClosedTrades = async (userName, {
127
+ startTime,
128
+ endTime
129
+ }) => {
130
+ const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
131
+ const trades = (await Promise.all(
132
+ dayKeys.map(
133
+ (dayKey) => (0, import_redis.getHashJsonValues)(
134
+ import_redis.redisKeys.runtimeClosedTradeBucket(userName, dayKey)
135
+ )
136
+ )
137
+ )).flat();
138
+ const deduped = /* @__PURE__ */ new Map();
139
+ for (const trade of trades) {
140
+ if (!isRuntimeTradeRecord(trade) || trade.status !== "closed" || !Number.isFinite(trade.exitTimestamp) || trade.exitTimestamp < startTime || trade.exitTimestamp >= endTime) {
141
+ continue;
142
+ }
143
+ deduped.set(trade.orderId, trade);
144
+ }
145
+ return [...deduped.values()].sort(
146
+ (left, right) => left.exitTimestamp - right.exitTimestamp
147
+ );
148
+ };
125
149
 
126
150
  // src/lib/runtimeSignalsLoader.ts
127
151
  var import_redis2 = require("@tradejs/infra/redis");
@@ -319,13 +343,30 @@ var collectRuntimeDebugEvidence = async ({
319
343
  }) => {
320
344
  const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
321
345
  const dayKeySet = new Set(dayKeys);
322
- const [trades, signals, evaluations, evaluationStatsBuckets, configs] = await Promise.all([
346
+ const [
347
+ entryTrades,
348
+ closedTrades,
349
+ signals,
350
+ evaluations,
351
+ evaluationStatsBuckets,
352
+ configs
353
+ ] = await Promise.all([
323
354
  loadRuntimeTrades(userName, { startTime, endTime }),
355
+ loadRuntimeClosedTrades(userName, { startTime, endTime }),
324
356
  loadRuntimeSignals(userName, { startTime, endTime }),
325
357
  loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
326
358
  loadRuntimeSignalEvaluationStatsBuckets(userName),
327
359
  loadRuntimeStrategyConfigs(userName)
328
360
  ]);
361
+ const tradesByOrderId = new Map(
362
+ entryTrades.map((trade) => [trade.orderId, trade])
363
+ );
364
+ for (const trade of closedTrades) {
365
+ tradesByOrderId.set(trade.orderId, trade);
366
+ }
367
+ const trades = [...tradesByOrderId.values()].sort(
368
+ (left, right) => left.entryTimestamp - right.entryTimestamp
369
+ );
329
370
  const filteredStats = evaluationStatsBuckets.filter(
330
371
  (entry) => dayKeySet.has(entry.dayKey) && (!strategies?.length || strategies.includes(entry.strategy))
331
372
  );
@@ -502,6 +543,39 @@ var buildRuntimeDebugReportPayload = async ({
502
543
  strategyConfigs: strategyConfigs ?? []
503
544
  };
504
545
  };
546
+ var buildRuntimeEvidenceReportPayload = ({
547
+ userName,
548
+ startTime,
549
+ endTime,
550
+ signals,
551
+ evaluations,
552
+ trades,
553
+ strategyConfigs,
554
+ evaluationStatsBuckets
555
+ }) => ({
556
+ reportType: "runtime-daily-evidence",
557
+ generatedAt: Date.now(),
558
+ userName,
559
+ window: {
560
+ startTime,
561
+ endTime,
562
+ startMsk: formatRuntimeDebugDateTime(startTime),
563
+ endMsk: formatRuntimeDebugDateTime(endTime),
564
+ dayKeys: (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime)
565
+ },
566
+ counts: {
567
+ trades: trades.length,
568
+ signals: signals.length,
569
+ evaluations: evaluations.length,
570
+ evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
571
+ strategyConfigs: strategyConfigs?.length ?? 0
572
+ },
573
+ trades: trades.map((trade) => ({ trade })),
574
+ signals: signals.map((signal) => ({ signal })),
575
+ evaluations: evaluations.map((evaluation) => ({ evaluation })),
576
+ evaluationStatsBuckets: evaluationStatsBuckets ?? [],
577
+ strategyConfigs: strategyConfigs ?? []
578
+ });
505
579
  var buildRuntimeDebugReportAttachment = async (params) => {
506
580
  const payload = await buildRuntimeDebugReportPayload(params);
507
581
  const dayKey = formatRuntimeDebugIsoDayKey(params.endTime);
@@ -523,5 +597,6 @@ var buildRuntimeDebugReportAttachment = async (params) => {
523
597
  buildRuntimeDebugEvaluationId,
524
598
  buildRuntimeDebugReportAttachment,
525
599
  buildRuntimeDebugReportPayload,
600
+ buildRuntimeEvidenceReportPayload,
526
601
  collectRuntimeDebugEvidence
527
602
  });
@@ -0,0 +1,255 @@
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/runtimeEvidenceArtifacts.ts
31
+ var runtimeEvidenceArtifacts_exports = {};
32
+ __export(runtimeEvidenceArtifacts_exports, {
33
+ RUNTIME_EVIDENCE_COMPLETE_FILE: () => RUNTIME_EVIDENCE_COMPLETE_FILE,
34
+ RUNTIME_EVIDENCE_MANIFEST_FILE: () => RUNTIME_EVIDENCE_MANIFEST_FILE,
35
+ RUNTIME_EVIDENCE_PAYLOAD_FILE: () => RUNTIME_EVIDENCE_PAYLOAD_FILE,
36
+ discoverRuntimeEvidenceBundles: () => discoverRuntimeEvidenceBundles,
37
+ publishRuntimeEvidenceBundle: () => publishRuntimeEvidenceBundle,
38
+ verifyRuntimeEvidenceBundle: () => verifyRuntimeEvidenceBundle
39
+ });
40
+ module.exports = __toCommonJS(runtimeEvidenceArtifacts_exports);
41
+ var import_node_crypto = require("crypto");
42
+ var import_promises = __toESM(require("fs/promises"));
43
+ var import_node_path = __toESM(require("path"));
44
+ var RUNTIME_EVIDENCE_MANIFEST_FILE = "manifest.json";
45
+ var RUNTIME_EVIDENCE_PAYLOAD_FILE = "runtime-evidence.json";
46
+ var RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
47
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
48
+ var sha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
49
+ var isExistingDestinationError = (error) => {
50
+ const code = error.code;
51
+ return code === "EEXIST" || code === "ENOTEMPTY";
52
+ };
53
+ var safeSegment = (value, fallback) => {
54
+ const normalized = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
55
+ return normalized && normalized !== "." && normalized !== ".." ? normalized : fallback;
56
+ };
57
+ var compactTimestamp = (timestamp) => new Date(timestamp).toISOString().replace(/[-:]/g, "").replace(/\.000Z$/, "Z");
58
+ var dateParts = (timestamp) => {
59
+ const date = new Date(timestamp);
60
+ return [
61
+ String(date.getUTCFullYear()),
62
+ String(date.getUTCMonth() + 1).padStart(2, "0"),
63
+ String(date.getUTCDate()).padStart(2, "0")
64
+ ];
65
+ };
66
+ var assertFiniteTimestamp = (value, label) => {
67
+ if (typeof value !== "number" || !Number.isFinite(value)) {
68
+ throw new Error(`Invalid runtime evidence ${label}`);
69
+ }
70
+ return value;
71
+ };
72
+ var parseManifest = (value) => {
73
+ const manifest = asRecord(value);
74
+ const window = asRecord(manifest?.window);
75
+ const payload = asRecord(manifest?.payload);
76
+ const counts = asRecord(manifest?.counts);
77
+ if (manifest?.schemaVersion !== 1 || manifest.reportType !== "runtime-evidence" || typeof manifest.artifactId !== "string" || typeof manifest.deploymentId !== "string" || typeof manifest.userName !== "string" || typeof manifest.createdAt !== "number" || payload?.file !== RUNTIME_EVIDENCE_PAYLOAD_FILE || typeof payload.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(payload.sha256) || typeof payload.bytes !== "number" || !Array.isArray(manifest.lineageKeys)) {
78
+ throw new Error("Invalid runtime evidence manifest");
79
+ }
80
+ return {
81
+ schemaVersion: 1,
82
+ artifactId: manifest.artifactId,
83
+ reportType: "runtime-evidence",
84
+ deploymentId: manifest.deploymentId,
85
+ userName: manifest.userName,
86
+ createdAt: manifest.createdAt,
87
+ window: {
88
+ startTime: assertFiniteTimestamp(window?.startTime, "window.startTime"),
89
+ endTime: assertFiniteTimestamp(window?.endTime, "window.endTime")
90
+ },
91
+ payload: {
92
+ file: RUNTIME_EVIDENCE_PAYLOAD_FILE,
93
+ sha256: payload.sha256,
94
+ bytes: payload.bytes
95
+ },
96
+ counts: Object.fromEntries(
97
+ Object.entries(counts ?? {}).filter(
98
+ (entry) => typeof entry[1] === "number" && Number.isFinite(entry[1])
99
+ )
100
+ ),
101
+ lineageKeys: manifest.lineageKeys.filter(
102
+ (value2) => typeof value2 === "string" && value2.length > 0
103
+ )
104
+ };
105
+ };
106
+ var publishRuntimeEvidenceBundle = async ({
107
+ publishRoot,
108
+ deploymentId,
109
+ userName,
110
+ startTime,
111
+ endTime,
112
+ artifact,
113
+ counts,
114
+ lineageKeys
115
+ }) => {
116
+ const payload = `${JSON.stringify(artifact, null, 2)}
117
+ `;
118
+ const payloadSha256 = sha256(payload);
119
+ const safeDeploymentId = safeSegment(deploymentId, "default");
120
+ const artifactId = [
121
+ compactTimestamp(startTime),
122
+ compactTimestamp(endTime),
123
+ payloadSha256.slice(0, 16)
124
+ ].join("_");
125
+ const createdAt = Date.now();
126
+ const manifest = {
127
+ schemaVersion: 1,
128
+ artifactId,
129
+ reportType: "runtime-evidence",
130
+ deploymentId: safeDeploymentId,
131
+ userName,
132
+ createdAt,
133
+ window: { startTime, endTime },
134
+ payload: {
135
+ file: RUNTIME_EVIDENCE_PAYLOAD_FILE,
136
+ sha256: payloadSha256,
137
+ bytes: Buffer.byteLength(payload)
138
+ },
139
+ counts,
140
+ lineageKeys: [...new Set(lineageKeys)].sort()
141
+ };
142
+ const incomingRoot = import_node_path.default.join(publishRoot, "incoming");
143
+ const readyRoot = import_node_path.default.join(
144
+ publishRoot,
145
+ "ready",
146
+ safeDeploymentId,
147
+ ...dateParts(endTime)
148
+ );
149
+ const bundleDir = import_node_path.default.join(readyRoot, artifactId);
150
+ const temporaryDir = import_node_path.default.join(
151
+ incomingRoot,
152
+ `${artifactId}.tmp-${(0, import_node_crypto.randomUUID)()}`
153
+ );
154
+ await import_promises.default.mkdir(temporaryDir, { recursive: true });
155
+ try {
156
+ await Promise.all([
157
+ import_promises.default.writeFile(
158
+ import_node_path.default.join(temporaryDir, RUNTIME_EVIDENCE_PAYLOAD_FILE),
159
+ payload,
160
+ "utf8"
161
+ ),
162
+ import_promises.default.writeFile(
163
+ import_node_path.default.join(temporaryDir, RUNTIME_EVIDENCE_MANIFEST_FILE),
164
+ `${JSON.stringify(manifest, null, 2)}
165
+ `,
166
+ "utf8"
167
+ )
168
+ ]);
169
+ await import_promises.default.writeFile(
170
+ import_node_path.default.join(temporaryDir, RUNTIME_EVIDENCE_COMPLETE_FILE),
171
+ `${artifactId}
172
+ `,
173
+ "utf8"
174
+ );
175
+ await import_promises.default.mkdir(readyRoot, { recursive: true });
176
+ try {
177
+ await import_promises.default.rename(temporaryDir, bundleDir);
178
+ } catch (error) {
179
+ if (!isExistingDestinationError(error)) {
180
+ throw error;
181
+ }
182
+ await import_promises.default.rm(temporaryDir, { recursive: true, force: true });
183
+ }
184
+ } catch (error) {
185
+ await import_promises.default.rm(temporaryDir, { recursive: true, force: true });
186
+ throw error;
187
+ }
188
+ return verifyRuntimeEvidenceBundle(bundleDir);
189
+ };
190
+ var verifyRuntimeEvidenceBundle = async (bundleDir) => {
191
+ const manifestPath = import_node_path.default.join(bundleDir, RUNTIME_EVIDENCE_MANIFEST_FILE);
192
+ const payloadPath = import_node_path.default.join(bundleDir, RUNTIME_EVIDENCE_PAYLOAD_FILE);
193
+ const completePath = import_node_path.default.join(bundleDir, RUNTIME_EVIDENCE_COMPLETE_FILE);
194
+ const [manifestText, payload, completeText] = await Promise.all([
195
+ import_promises.default.readFile(manifestPath, "utf8"),
196
+ import_promises.default.readFile(payloadPath),
197
+ import_promises.default.readFile(completePath, "utf8")
198
+ ]);
199
+ const manifest = parseManifest(JSON.parse(manifestText));
200
+ if (completeText.trim() !== manifest.artifactId) {
201
+ throw new Error(`Invalid completion marker in ${bundleDir}`);
202
+ }
203
+ if (payload.byteLength !== manifest.payload.bytes) {
204
+ throw new Error(`Runtime evidence size mismatch in ${bundleDir}`);
205
+ }
206
+ if (sha256(payload) !== manifest.payload.sha256) {
207
+ throw new Error(`Runtime evidence checksum mismatch in ${bundleDir}`);
208
+ }
209
+ const artifact = asRecord(JSON.parse(payload.toString("utf8")));
210
+ const artifactWindow = asRecord(artifact?.window);
211
+ if (artifact?.reportType !== "runtime-evidence" || artifactWindow?.startTime !== manifest.window.startTime || artifactWindow.endTime !== manifest.window.endTime) {
212
+ throw new Error(`Runtime evidence payload mismatch in ${bundleDir}`);
213
+ }
214
+ return {
215
+ bundleDir,
216
+ manifestPath,
217
+ payloadPath,
218
+ manifest,
219
+ artifact
220
+ };
221
+ };
222
+ var discoverRuntimeEvidenceBundles = async (rootDir) => {
223
+ const bundles = [];
224
+ const visit = async (directory, depth) => {
225
+ if (depth > 8) return;
226
+ let entries;
227
+ try {
228
+ entries = await import_promises.default.readdir(directory, { withFileTypes: true });
229
+ } catch (error) {
230
+ if (error.code === "ENOENT") return;
231
+ throw error;
232
+ }
233
+ const names = new Set(entries.map((entry) => entry.name));
234
+ if (names.has(RUNTIME_EVIDENCE_MANIFEST_FILE) && names.has(RUNTIME_EVIDENCE_PAYLOAD_FILE) && names.has(RUNTIME_EVIDENCE_COMPLETE_FILE)) {
235
+ bundles.push(directory);
236
+ return;
237
+ }
238
+ await Promise.all(
239
+ entries.filter(
240
+ (entry) => entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.includes(".tmp-")
241
+ ).map((entry) => visit(import_node_path.default.join(directory, entry.name), depth + 1))
242
+ );
243
+ };
244
+ await visit(rootDir, 0);
245
+ return bundles.sort();
246
+ };
247
+ // Annotate the CommonJS export names for ESM import in node:
248
+ 0 && (module.exports = {
249
+ RUNTIME_EVIDENCE_COMPLETE_FILE,
250
+ RUNTIME_EVIDENCE_MANIFEST_FILE,
251
+ RUNTIME_EVIDENCE_PAYLOAD_FILE,
252
+ discoverRuntimeEvidenceBundles,
253
+ publishRuntimeEvidenceBundle,
254
+ verifyRuntimeEvidenceBundle
255
+ });