@tradejs/cli 3.1.29-beta.276 → 3.1.29-beta.278

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.
@@ -0,0 +1,103 @@
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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/lib/mcpReplayProcess.ts
32
+ var mcpReplayProcess_exports = {};
33
+ __export(mcpReplayProcess_exports, {
34
+ buildMcpReplayProcess: () => buildMcpReplayProcess
35
+ });
36
+ module.exports = __toCommonJS(mcpReplayProcess_exports);
37
+ var import_node_path = __toESM(require("path"));
38
+ var import_node_crypto = require("crypto");
39
+ var import_promises = __toESM(require("fs/promises"));
40
+
41
+ // src/lib/runtimeEvidenceArtifacts.ts
42
+ var runtimeEvidenceArtifacts_exports = {};
43
+ __reExport(runtimeEvidenceArtifacts_exports, require("@tradejs/node/diagnostics"));
44
+
45
+ // src/lib/mcpReplayProcess.ts
46
+ var client = `
47
+ const fs = require('node:fs');
48
+ const path = require('node:path');
49
+ const [root, id, request] = process.argv.slice(1);
50
+ const file = path.join(root, id);
51
+ fs.mkdirSync(root, {recursive:true});
52
+ try { fs.writeFileSync(file + '.request', request, {flag:'wx'}); }
53
+ catch (error) { if (error.code !== 'EEXIST') throw error; if (fs.readFileSync(file + '.request', 'utf8') !== request) throw new Error('Replay request identity conflict'); }
54
+ process.on('SIGTERM', () => { fs.writeFileSync(file + '.cancel', 'cancel'); process.exit(143); });
55
+ const timer = setInterval(() => {
56
+ try {
57
+ const result = JSON.parse(fs.readFileSync(file + '.result', 'utf8'));
58
+ console.log(result.message);
59
+ clearInterval(timer);
60
+ process.exit(result.exitCode === 0 ? 0 : 1);
61
+ } catch (error) { if (error.code !== 'ENOENT') throw error; }
62
+ }, 1000);
63
+ `;
64
+ var buildMcpReplayProcess = async ({
65
+ evidencePath,
66
+ outDir,
67
+ runId,
68
+ projectRoot,
69
+ env
70
+ }) => {
71
+ if (!/^[a-f0-9]{64}$/.test(runId))
72
+ throw new Error("Invalid replay request id");
73
+ const bundle = await (0, runtimeEvidenceArtifacts_exports.verifyRuntimeEvidenceBundle)(import_node_path.default.dirname(evidencePath));
74
+ if (import_node_path.default.resolve(evidencePath) !== import_node_path.default.resolve(bundle.payloadPath))
75
+ throw new Error("Replay requires the verified bundle payload");
76
+ const relative = import_node_path.default.relative(import_node_path.default.join(projectRoot, "data"), evidencePath);
77
+ if (relative.startsWith("..") || import_node_path.default.isAbsolute(relative))
78
+ throw new Error("Evidence must be inside Project data");
79
+ const root = env.MCP_REPLAY_REQUEST_ROOT || import_node_path.default.join(projectRoot, "data/mcp-replay-requests");
80
+ const heartbeat = JSON.parse(
81
+ await import_promises.default.readFile(import_node_path.default.join(root, "heartbeat.json"), "utf8")
82
+ );
83
+ if (!Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
84
+ throw new Error("Host replay runner heartbeat is stale");
85
+ const bytes = await import_promises.default.readFile(evidencePath);
86
+ const request = JSON.stringify({
87
+ schemaVersion: 1,
88
+ id: runId,
89
+ evidence: relative,
90
+ artifactId: bundle.manifest.artifactId,
91
+ deploymentId: bundle.manifest.deploymentId,
92
+ payloadSha256: (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex")
93
+ });
94
+ void outDir;
95
+ return {
96
+ executable: process.execPath,
97
+ args: ["-e", client, root, runId, request]
98
+ };
99
+ };
100
+ // Annotate the CommonJS export names for ESM import in node:
101
+ 0 && (module.exports = {
102
+ buildMcpReplayProcess
103
+ });
@@ -249,21 +249,38 @@ var buildStrategyMonitoringProfile = ({
249
249
 
250
250
  // src/lib/strategyRelease/evidenceRetention.ts
251
251
  var DAY_MS2 = 864e5;
252
+ var isResearchRetired = (research) => research?.status === "retired" && research.retirement?.policy === "discard-unrecoverable-without-rerun" && Boolean(research.retirement.reason.trim()) && Boolean(research.retirement.missingEvidence.length) && research.retirement.missingEvidence.every((item) => Boolean(item.trim()));
252
253
  var planStrategyEvidenceRetention = ({
253
254
  now,
254
255
  entries,
256
+ research,
255
257
  retentionDays = {
256
258
  operational_redis: 3,
257
259
  verbose_payload: 14,
260
+ research_payload: 0,
261
+ research_cache: 0,
258
262
  verified_runtime_bundle: 90,
259
263
  compact_ledger: null
260
264
  }
261
265
  }) => {
262
266
  const keep = [];
263
267
  const remove = [];
268
+ const retired = isResearchRetired(research);
269
+ const roles = new Set(
270
+ research?.permanentArtifacts.map((artifact) => artifact.role)
271
+ );
272
+ const terminalResearch = Boolean(
273
+ (research?.status === "completed" || retired) && Number.isFinite(research?.completedAt) && research.completedAt <= now && (retired ? ["note"] : ["note", "candidates", "metrics"]).every(
274
+ (role) => roles.has(role)
275
+ )
276
+ );
264
277
  for (const entry of entries) {
265
278
  const days = retentionDays[entry.kind];
266
- if (days == null || !entry.verified || !entry.aggregated || now - entry.createdAt <= days * DAY_MS2) {
279
+ const isResearch = entry.kind === "research_payload" || entry.kind === "research_cache";
280
+ const completedResearch = Boolean(
281
+ terminalResearch && research?.researchId === entry.researchId && /^[a-f0-9]{64}$/.test(entry.sha256 ?? "")
282
+ );
283
+ if (isResearch && !completedResearch || days == null || !entry.verified || !entry.aggregated && !(isResearch && retired) || !isResearch && now - entry.createdAt <= days * DAY_MS2) {
267
284
  keep.push(entry);
268
285
  } else {
269
286
  remove.push(entry);
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,15 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
18
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
30
 
20
31
  // src/scripts/mcpWorker.ts
@@ -24,16 +35,83 @@ __export(mcpWorker_exports, {
24
35
  });
25
36
  module.exports = __toCommonJS(mcpWorker_exports);
26
37
  var import_node_child_process = require("child_process");
27
- var import_node_crypto = require("crypto");
28
- var import_promises = require("fs/promises");
38
+ var import_node_crypto2 = require("crypto");
39
+ var import_promises2 = require("fs/promises");
29
40
  var import_redis = require("@tradejs/infra/redis");
30
41
  var import_mcp = require("@tradejs/node/mcp");
42
+
43
+ // src/lib/mcpReplayProcess.ts
44
+ var import_node_path = __toESM(require("path"));
45
+ var import_node_crypto = require("crypto");
46
+ var import_promises = __toESM(require("fs/promises"));
47
+
48
+ // src/lib/runtimeEvidenceArtifacts.ts
49
+ var runtimeEvidenceArtifacts_exports = {};
50
+ __reExport(runtimeEvidenceArtifacts_exports, require("@tradejs/node/diagnostics"));
51
+
52
+ // src/lib/mcpReplayProcess.ts
53
+ var client = `
54
+ const fs = require('node:fs');
55
+ const path = require('node:path');
56
+ const [root, id, request] = process.argv.slice(1);
57
+ const file = path.join(root, id);
58
+ fs.mkdirSync(root, {recursive:true});
59
+ try { fs.writeFileSync(file + '.request', request, {flag:'wx'}); }
60
+ catch (error) { if (error.code !== 'EEXIST') throw error; if (fs.readFileSync(file + '.request', 'utf8') !== request) throw new Error('Replay request identity conflict'); }
61
+ process.on('SIGTERM', () => { fs.writeFileSync(file + '.cancel', 'cancel'); process.exit(143); });
62
+ const timer = setInterval(() => {
63
+ try {
64
+ const result = JSON.parse(fs.readFileSync(file + '.result', 'utf8'));
65
+ console.log(result.message);
66
+ clearInterval(timer);
67
+ process.exit(result.exitCode === 0 ? 0 : 1);
68
+ } catch (error) { if (error.code !== 'ENOENT') throw error; }
69
+ }, 1000);
70
+ `;
71
+ var buildMcpReplayProcess = async ({
72
+ evidencePath,
73
+ outDir,
74
+ runId,
75
+ projectRoot,
76
+ env
77
+ }) => {
78
+ if (!/^[a-f0-9]{64}$/.test(runId))
79
+ throw new Error("Invalid replay request id");
80
+ const bundle = await (0, runtimeEvidenceArtifacts_exports.verifyRuntimeEvidenceBundle)(import_node_path.default.dirname(evidencePath));
81
+ if (import_node_path.default.resolve(evidencePath) !== import_node_path.default.resolve(bundle.payloadPath))
82
+ throw new Error("Replay requires the verified bundle payload");
83
+ const relative = import_node_path.default.relative(import_node_path.default.join(projectRoot, "data"), evidencePath);
84
+ if (relative.startsWith("..") || import_node_path.default.isAbsolute(relative))
85
+ throw new Error("Evidence must be inside Project data");
86
+ const root = env.MCP_REPLAY_REQUEST_ROOT || import_node_path.default.join(projectRoot, "data/mcp-replay-requests");
87
+ const heartbeat = JSON.parse(
88
+ await import_promises.default.readFile(import_node_path.default.join(root, "heartbeat.json"), "utf8")
89
+ );
90
+ if (!Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
91
+ throw new Error("Host replay runner heartbeat is stale");
92
+ const bytes = await import_promises.default.readFile(evidencePath);
93
+ const request = JSON.stringify({
94
+ schemaVersion: 1,
95
+ id: runId,
96
+ evidence: relative,
97
+ artifactId: bundle.manifest.artifactId,
98
+ deploymentId: bundle.manifest.deploymentId,
99
+ payloadSha256: (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex")
100
+ });
101
+ void outDir;
102
+ return {
103
+ executable: process.execPath,
104
+ args: ["-e", client, root, runId, request]
105
+ };
106
+ };
107
+
108
+ // src/scripts/mcpWorker.ts
31
109
  var pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
110
  var leaseKey = "worker:lease";
33
111
  var LEASE_SECONDS = 15;
34
112
  var JOB_TIMEOUT_MS = 60 * 60 * 1e3;
35
113
  var main = async (options = {}) => {
36
- const owner = (0, import_node_crypto.randomUUID)();
114
+ const owner = (0, import_node_crypto2.randomUUID)();
37
115
  if (!await import_redis.mcpStorage.put(leaseKey, owner, LEASE_SECONDS, true))
38
116
  throw new Error("Another MCP worker owns this storage");
39
117
  let stopping = false;
@@ -41,6 +119,18 @@ var main = async (options = {}) => {
41
119
  let renewing = false;
42
120
  let leaseLost = false;
43
121
  let manifestHash = "";
122
+ let replayReady = false;
123
+ const updateReplayReady = async () => {
124
+ try {
125
+ const root = process.env.MCP_REPLAY_REQUEST_ROOT || `${process.env.PROJECT_CWD || process.cwd()}/data/mcp-replay-requests`;
126
+ const heartbeat2 = JSON.parse(
127
+ await (0, import_promises2.readFile)(`${root}/heartbeat.json`, "utf8")
128
+ );
129
+ replayReady = Number.isFinite(heartbeat2.observedAt) && Date.now() - heartbeat2.observedAt >= 0 && Date.now() - heartbeat2.observedAt < 12e4;
130
+ } catch {
131
+ replayReady = false;
132
+ }
133
+ };
44
134
  const signalChild = (signal) => {
45
135
  if (!child?.pid) return;
46
136
  try {
@@ -54,7 +144,7 @@ var main = async (options = {}) => {
54
144
  const heartbeatValue = () => ({
55
145
  observedAt: Date.now(),
56
146
  packageManifestSha256: manifestHash,
57
- replayAvailable: Boolean(process.env.MCP_REPLAY_REDIS_HOST)
147
+ replayAvailable: replayReady
58
148
  });
59
149
  const stop = () => {
60
150
  stopping = true;
@@ -70,6 +160,7 @@ var main = async (options = {}) => {
70
160
  try {
71
161
  if (!await import_redis.mcpStorage.compare(leaseKey, owner, owner, LEASE_SECONDS))
72
162
  throw new Error("Worker lease lost");
163
+ await updateReplayReady();
73
164
  await import_redis.mcpStorage.put("worker:heartbeat", heartbeatValue(), LEASE_SECONDS);
74
165
  } catch {
75
166
  leaseLost = true;
@@ -80,6 +171,7 @@ var main = async (options = {}) => {
80
171
  }, 2e3);
81
172
  try {
82
173
  manifestHash = await (0, import_mcp.mcpPackageManifestHash)();
174
+ await updateReplayReady();
83
175
  await import_redis.mcpStorage.put("worker:heartbeat", heartbeatValue(), LEASE_SECONDS);
84
176
  let cursor = "0";
85
177
  do {
@@ -144,7 +236,7 @@ var main = async (options = {}) => {
144
236
  );
145
237
  continue;
146
238
  }
147
- await (0, import_promises.mkdir)((0, import_mcp.mcpJobDirectory)(started), { recursive: true });
239
+ await (0, import_promises2.mkdir)((0, import_mcp.mcpJobDirectory)(started), { recursive: true });
148
240
  if (started.kind === "backtest")
149
241
  await (0, import_redis.setDataStrict)(
150
242
  import_redis.redisKeys.backtestConfig(
@@ -167,7 +259,9 @@ var main = async (options = {}) => {
167
259
  };
168
260
  if (started.kind === "runtime-feedback-replay") {
169
261
  for (const name of Object.keys(jobEnv)) {
170
- if (/secret|token|api.?key|private.?key|cookie|credential/i.test(name))
262
+ if (/secret|password|token|api.?key|private.?key|cookie|credential/i.test(
263
+ name
264
+ ))
171
265
  delete jobEnv[name];
172
266
  }
173
267
  Object.assign(jobEnv, {
@@ -180,7 +274,35 @@ var main = async (options = {}) => {
180
274
  TRADEJS_TIMESCALE_READ_ONLY: "true"
181
275
  });
182
276
  }
183
- child = (0, import_node_child_process.spawn)(process.execPath, [process.argv[1], ...started.args], {
277
+ let command = {
278
+ executable: process.execPath,
279
+ args: [process.argv[1], ...started.args]
280
+ };
281
+ try {
282
+ if (started.kind === "runtime-feedback-replay")
283
+ command = await buildMcpReplayProcess({
284
+ evidencePath: String(started.request.verifiedEvidencePath),
285
+ outDir: (0, import_mcp.mcpJobDirectory)(started),
286
+ runId: started.id,
287
+ projectRoot: process.env.PROJECT_CWD || process.cwd(),
288
+ env: process.env
289
+ });
290
+ } catch (error) {
291
+ await import_redis.mcpStorage.compare(
292
+ jobKey,
293
+ started,
294
+ {
295
+ ...started,
296
+ status: "failed",
297
+ finishedAt: Date.now(),
298
+ updatedAt: Date.now(),
299
+ error: error instanceof Error ? error.message : "Replay preparation failed"
300
+ },
301
+ import_mcp.MCP_JOB_TTL
302
+ );
303
+ continue;
304
+ }
305
+ child = (0, import_node_child_process.spawn)(command.executable, command.args, {
184
306
  cwd: process.env.PROJECT_CWD || process.cwd(),
185
307
  env: jobEnv,
186
308
  detached: process.platform !== "win32",