@tradejs/cli 3.0.1 → 3.1.1

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.
@@ -4350,7 +4350,7 @@ var buildRuntimeModeStrategyConfig = ({
4350
4350
  ...strategyConfig,
4351
4351
  ENV: env,
4352
4352
  MAKE_ORDERS: makeOrders,
4353
- INTERVAL: interval,
4353
+ INTERVAL: strategyConfig.INTERVAL ?? interval,
4354
4354
  ...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
4355
4355
  ...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
4356
4356
  });
@@ -4913,28 +4913,37 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
4913
4913
 
4914
4914
  // src/lib/strategyGateFingerprint.ts
4915
4915
  var import_node_crypto = require("crypto");
4916
+ var import_node_module = require("module");
4916
4917
  var import_promises = __toESM(require("fs/promises"));
4917
4918
  var import_node_path = __toESM(require("path"));
4918
- var gateFingerprintRelativePaths = (strategyName) => [
4919
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
4920
- `packages/strategies/src/${strategyName}/guardrails.ts`,
4921
- `packages/strategies/src/${strategyName}/pockets.ts`,
4922
- `packages/strategies/src/${strategyName}/config.ts`,
4923
- "packages/strategies/src/shared/localAiGate.ts",
4924
- "packages/node/src/ai.ts",
4925
- "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
4919
+ var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
4920
+ var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
4921
+ var gateFingerprintSpecifiers = (strategyName) => [
4922
+ getStrategyPackageName(strategyName),
4923
+ "@tradejs/strategy-kit/ai-gate",
4924
+ "@tradejs/node/ai",
4925
+ "@tradejs/core/strategies"
4926
4926
  ];
4927
4927
  var resolveStrategyGateFingerprint = async ({
4928
4928
  projectRoot,
4929
4929
  strategyName,
4930
4930
  gitSha
4931
4931
  }) => {
4932
- const candidates = await Promise.all(
4933
- gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
4934
- const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
4935
- return content == null ? null : { relativePath, content };
4932
+ const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
4933
+ const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
4934
+ const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
4935
+ const candidates = requireFromProject ? await Promise.all(
4936
+ gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
4937
+ let resolvedPath;
4938
+ try {
4939
+ resolvedPath = requireFromProject.resolve(specifier);
4940
+ } catch {
4941
+ return null;
4942
+ }
4943
+ const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
4944
+ return content == null ? null : { specifier, content };
4936
4945
  })
4937
- );
4946
+ ) : [];
4938
4947
  const sourceEntries = candidates.filter(
4939
4948
  (entry) => entry != null
4940
4949
  );
@@ -4942,7 +4951,7 @@ var resolveStrategyGateFingerprint = async ({
4942
4951
  hash.update(strategyName);
4943
4952
  if (sourceEntries.length) {
4944
4953
  for (const entry of sourceEntries) {
4945
- hash.update(entry.relativePath);
4954
+ hash.update(entry.specifier);
4946
4955
  hash.update(entry.content);
4947
4956
  }
4948
4957
  } else {
@@ -4950,7 +4959,7 @@ var resolveStrategyGateFingerprint = async ({
4950
4959
  }
4951
4960
  return {
4952
4961
  gateFingerprint: hash.digest("hex").slice(0, 16),
4953
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
4962
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
4954
4963
  };
4955
4964
  };
4956
4965
 
@@ -5129,14 +5138,29 @@ var resolveRuntimeGateFingerprint = async ({
5129
5138
  gateFingerprintCache.set(cacheKey, result);
5130
5139
  return result;
5131
5140
  };
5132
- var buildRuntimeLineage = async ({
5141
+ async function buildRuntimeLineage({
5133
5142
  projectRoot,
5134
5143
  strategyName,
5135
5144
  compositionId,
5145
+ releaseVersion,
5146
+ strategyPackageVersion,
5147
+ runtimePackageVersion,
5136
5148
  config,
5137
5149
  runContext,
5138
5150
  env = process.env
5139
- }) => {
5151
+ }) {
5152
+ if (releaseVersion != null) {
5153
+ if (!Number.isSafeInteger(releaseVersion) || releaseVersion <= 0) {
5154
+ throw new Error(`Invalid runtime releaseVersion: ${releaseVersion}`);
5155
+ }
5156
+ return {
5157
+ schemaVersion: 2,
5158
+ releaseVersion,
5159
+ strategyPackageVersion,
5160
+ runtimePackageVersion,
5161
+ maxLossValue: resolveRuntimeMaxLossValue(config)
5162
+ };
5163
+ }
5140
5164
  const git = resolveGitLineage(projectRoot);
5141
5165
  const hyperliquidPerpUniverseFingerprint = typeof import_strategies3.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies3.getHyperliquidPerpUniverseSnapshot)().fingerprint : await resolveSnapshotFingerprint(
5142
5166
  projectRoot,
@@ -5171,8 +5195,8 @@ var buildRuntimeLineage = async ({
5171
5195
  contextFingerprint: fingerprintRuntimeValue(context),
5172
5196
  maxLossValue: resolveRuntimeMaxLossValue(config)
5173
5197
  };
5174
- };
5175
- var runtimeLineageKey = (lineage) => [
5198
+ }
5199
+ var runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? ["v2", lineage.releaseVersion].join(":") : [
5176
5200
  lineage.schemaVersion,
5177
5201
  lineage.compositionId ?? "unbound-composition",
5178
5202
  lineage.gitSha ?? "unknown",
@@ -5424,6 +5448,7 @@ var runHistoricalSignalsReplay = async ({
5424
5448
  userName: replayUserName,
5425
5449
  connectorName,
5426
5450
  runtimeConfigId: "config",
5451
+ runtimeLineage,
5427
5452
  config: buildReplayStrategyConfig({
5428
5453
  strategyConfig,
5429
5454
  interval
@@ -7204,6 +7229,7 @@ var buildRuntimeEvidenceLineageScopes = ({
7204
7229
  strategy,
7205
7230
  symbol,
7206
7231
  runtimeConfigId,
7232
+ runtimeReleaseVersion,
7207
7233
  timestamp,
7208
7234
  lineage
7209
7235
  }) => {
@@ -7216,6 +7242,7 @@ var buildRuntimeEvidenceLineageScopes = ({
7216
7242
  strategy,
7217
7243
  symbol,
7218
7244
  ...runtimeConfigId ? { runtimeConfigId } : {},
7245
+ ...runtimeReleaseVersion ? { runtimeReleaseVersion } : {},
7219
7246
  lineage,
7220
7247
  firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
7221
7248
  lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
@@ -7226,6 +7253,7 @@ var buildRuntimeEvidenceLineageScopes = ({
7226
7253
  strategy: signal.strategy,
7227
7254
  symbol: signal.symbol,
7228
7255
  runtimeConfigId: signal.runtimeConfigId,
7256
+ runtimeReleaseVersion: signal.runtimeReleaseVersion,
7229
7257
  timestamp: signal.timestamp,
7230
7258
  lineage: signal.runtimeLineage
7231
7259
  });
@@ -7235,6 +7263,7 @@ var buildRuntimeEvidenceLineageScopes = ({
7235
7263
  strategy: evaluation.strategy,
7236
7264
  symbol: evaluation.symbol,
7237
7265
  runtimeConfigId: evaluation.runtimeConfigId,
7266
+ runtimeReleaseVersion: evaluation.runtimeReleaseVersion,
7238
7267
  timestamp: evaluation.timestamp,
7239
7268
  lineage: evaluation.runtimeLineage
7240
7269
  });
@@ -8545,13 +8574,26 @@ var renderLineageRows = (runtimeComparison) => {
8545
8574
  contexts: /* @__PURE__ */ new Set()
8546
8575
  };
8547
8576
  existing.scopes += 1;
8548
- existing.gitShas.add(record.lineage.gitSha ?? "unknown");
8549
- existing.dirty.add(
8550
- record.lineage.gitDirty == null ? "unknown" : String(record.lineage.gitDirty)
8551
- );
8552
- existing.gates.add(record.lineage.gateFingerprint);
8553
- existing.configs.add(record.lineage.configFingerprint);
8554
- existing.contexts.add(record.lineage.contextFingerprint);
8577
+ if (record.lineage.schemaVersion === 2) {
8578
+ existing.gitShas.add("release-versioned");
8579
+ existing.dirty.add("n/a");
8580
+ existing.gates.add("release-versioned");
8581
+ existing.configs.add(`v${record.lineage.releaseVersion}`);
8582
+ existing.contexts.add(
8583
+ [
8584
+ record.lineage.strategyPackageVersion ?? "strategy:unknown",
8585
+ record.lineage.runtimePackageVersion ?? "runtime:unknown"
8586
+ ].join("/")
8587
+ );
8588
+ } else {
8589
+ existing.gitShas.add(record.lineage.gitSha ?? "unknown");
8590
+ existing.dirty.add(
8591
+ record.lineage.gitDirty == null ? "unknown" : String(record.lineage.gitDirty)
8592
+ );
8593
+ existing.gates.add(record.lineage.gateFingerprint);
8594
+ existing.configs.add(record.lineage.configFingerprint);
8595
+ existing.contexts.add(record.lineage.contextFingerprint);
8596
+ }
8555
8597
  grouped.set(record.strategy, existing);
8556
8598
  }
8557
8599
  const formatSet = (values) => {
@@ -57,12 +57,12 @@ var import_chalk2 = __toESM(require("chalk"));
57
57
  var import_node_child_process = require("child_process");
58
58
  var import_promises = __toESM(require("fs/promises"));
59
59
  var import_node_os = __toESM(require("os"));
60
- var import_node_path = __toESM(require("path"));
60
+ var import_node_path2 = __toESM(require("path"));
61
61
  var import_constants = require("@tradejs/core/constants");
62
62
  var import_ai = require("@tradejs/infra/ai");
63
63
  var import_logger = require("@tradejs/infra/logger");
64
64
  var import_redis2 = require("@tradejs/infra/redis");
65
- var import_strategies = require("@tradejs/strategies");
65
+ var import_registry = require("@tradejs/node/registry");
66
66
 
67
67
  // src/lib/runtimeRedis.ts
68
68
  var import_redis = require("@tradejs/infra/redis");
@@ -84,6 +84,26 @@ var BACKTEST_CLI_RUNTIME_CONFIG_KEYS = /* @__PURE__ */ new Set([
84
84
  "BACKTEST_ENTRY_DELAY_BARS"
85
85
  ]);
86
86
 
87
+ // src/lib/researchRoots.ts
88
+ var import_node_path = __toESM(require("path"));
89
+ var SOURCE_REPOSITORY_ROOT_ENV = "TRADEJS_SOURCE_REPOSITORY_ROOT";
90
+ var firstNonEmpty = (...values) => values.map((value) => String(value || "").trim()).find(Boolean);
91
+ var resolveResearchRoots = (options = {}) => {
92
+ const cwd = import_node_path.default.resolve(options.cwd || process.cwd());
93
+ const env = options.env || process.env;
94
+ const projectRoot2 = import_node_path.default.resolve(
95
+ firstNonEmpty(options.projectRoot, env.PROJECT_CWD, cwd) || cwd
96
+ );
97
+ const sourceRepositoryRoot2 = import_node_path.default.resolve(
98
+ firstNonEmpty(
99
+ options.sourceRepositoryRoot,
100
+ env[SOURCE_REPOSITORY_ROOT_ENV],
101
+ cwd
102
+ ) || cwd
103
+ );
104
+ return { projectRoot: projectRoot2, sourceRepositoryRoot: sourceRepositoryRoot2 };
105
+ };
106
+
87
107
  // src/lib/telegramReports.ts
88
108
  var import_cli = require("@tradejs/node/cli");
89
109
  var TELEGRAM_REPORT_CHUNK_LIMIT = 3900;
@@ -175,11 +195,11 @@ import_args.default.option(["O", "outDir"], "AI export output directory", "data/
175
195
  import_args.default.option(["A", "skipAgent"], "Skip direct research agent invocation", false);
176
196
  import_args.default.option(["j", "json"], "Print structured JSON summary", false);
177
197
  var flags = import_args.default.parse(process.argv);
178
- var projectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
198
+ var { projectRoot, sourceRepositoryRoot } = resolveResearchRoots();
179
199
  var dotenvPath = String(
180
- process.env.DOTENV_CONFIG_PATH || import_node_path.default.join(projectRoot, ".env")
181
- ).trim() || import_node_path.default.join(projectRoot, ".env");
182
- var getResearchAutoLockPath = (userName) => import_node_path.default.join(
200
+ process.env.DOTENV_CONFIG_PATH || import_node_path2.default.join(projectRoot, ".env")
201
+ ).trim() || import_node_path2.default.join(projectRoot, ".env");
202
+ var getResearchAutoLockPath = (userName) => import_node_path2.default.join(
183
203
  projectRoot,
184
204
  "data",
185
205
  "cache",
@@ -223,7 +243,7 @@ var acquireResearchAutoLock = async ({
223
243
  startedAt,
224
244
  argv: [...process.argv]
225
245
  };
226
- await import_promises.default.mkdir(import_node_path.default.dirname(lockPath), { recursive: true });
246
+ await import_promises.default.mkdir(import_node_path2.default.dirname(lockPath), { recursive: true });
227
247
  const writeLock = async () => {
228
248
  await import_promises.default.writeFile(lockPath, JSON.stringify(nextLock, null, 2), {
229
249
  flag: "wx"
@@ -360,7 +380,7 @@ var listMergedFiles = async (outDir, strategyName) => {
360
380
  try {
361
381
  const entries = await import_promises.default.readdir(outDir);
362
382
  const prefix = `ai-dataset-${(0, import_ai.toFileToken)(strategyName)}-merged-`;
363
- return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).sort().map((name) => import_node_path.default.join(outDir, name));
383
+ return entries.filter((name) => name.startsWith(prefix) && name.endsWith(".jsonl")).sort().map((name) => import_node_path2.default.join(outDir, name));
364
384
  } catch {
365
385
  return [];
366
386
  }
@@ -480,6 +500,7 @@ var runCliCommand = async (params) => {
480
500
  const env = {
481
501
  ...process.env,
482
502
  PROJECT_CWD: projectRoot,
503
+ [SOURCE_REPOSITORY_ROOT_ENV]: sourceRepositoryRoot,
483
504
  DOTENV_CONFIG_PATH: dotenvPath,
484
505
  ...params.nodeHeapMb ? {
485
506
  NODE_OPTIONS: buildNodeOptions({
@@ -489,8 +510,8 @@ var runCliCommand = async (params) => {
489
510
  };
490
511
  return new Promise((resolve, reject) => {
491
512
  const child = (0, import_node_child_process.spawn)(
492
- "bash",
493
- ["./bin/run-cli-runtime.sh", params.command, ...params.args],
513
+ process.execPath,
514
+ [import_node_path2.default.resolve(process.argv[1]), params.command, ...params.args],
494
515
  {
495
516
  cwd: projectRoot,
496
517
  env,
@@ -672,8 +693,8 @@ var main = async () => {
672
693
  const jsonOutput = Boolean(flags.json);
673
694
  const skipAgent = Boolean(flags.skipAgent);
674
695
  const outDirArg = String(flags.outDir || "data/ai/export").trim() || "data/ai/export";
675
- const outDir = import_node_path.default.resolve(projectRoot, outDirArg);
676
- const cleanDirArg = import_node_path.default.relative(import_node_path.default.join(projectRoot, "data"), outDir).replace(/\\/g, "/");
696
+ const outDir = import_node_path2.default.resolve(projectRoot, outDirArg);
697
+ const cleanDirArg = import_node_path2.default.relative(import_node_path2.default.join(projectRoot, "data"), outDir).replace(/\\/g, "/");
677
698
  const target = await resolveTarget();
678
699
  const runId = `${Date.now()}-${(0, import_ai.toFileToken)(target.strategy)}`;
679
700
  const run = {
@@ -762,7 +783,7 @@ var main = async () => {
762
783
  target.strategy
763
784
  );
764
785
  const backtestConfigKey = import_redis2.redisKeys.backtestConfig(userName, target.config);
765
- const defaults = (0, import_strategies.getBuiltInStrategyDefaultConfig)(target.strategy) || {};
786
+ const defaults = await (0, import_registry.getStrategyDefaults)(target.strategy, projectRoot) || {};
766
787
  const userStrategyConfig = await (0, import_redis2.getData)(
767
788
  strategyConfigKey,
768
789
  {}
@@ -0,0 +1,290 @@
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/scripts/runtimeConfig.ts
31
+ var runtimeConfig_exports = {};
32
+ __export(runtimeConfig_exports, {
33
+ main: () => main,
34
+ runtimeConfig: () => runtimeConfig
35
+ });
36
+ module.exports = __toCommonJS(runtimeConfig_exports);
37
+ var import_config = require("dotenv/config");
38
+ var import_promises = require("fs/promises");
39
+ var import_node_path = __toESM(require("path"));
40
+ var import_runtimeDeployments = require("@tradejs/infra/runtimeDeployments");
41
+ var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
42
+ var import_runtimeStrategyReleases = require("@tradejs/infra/runtimeStrategyReleases");
43
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
44
+ var import_strategies = require("@tradejs/node/strategies");
45
+ var argv = process.argv.slice(2);
46
+ var action = argv[0] ?? "inspect";
47
+ var option = (name) => {
48
+ const index = argv.indexOf(`--${name}`);
49
+ return index >= 0 ? argv[index + 1] : void 0;
50
+ };
51
+ var hasFlag = (name) => argv.includes(`--${name}`);
52
+ var required = (name) => {
53
+ const value = String(option(name) ?? "").trim();
54
+ if (!value) throw new Error(`--${name} is required`);
55
+ return value;
56
+ };
57
+ var projectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
58
+ var userName = String(option("user") ?? process.env.USER_NAME ?? "root");
59
+ var OPERATIONAL_CONFIG_KEYS = [
60
+ "ACCOUNT_ID",
61
+ "AI_REPLAY_ANALYSES",
62
+ "DEPLOYMENT_ID",
63
+ "ENABLE",
64
+ "ENV",
65
+ "MAKE_ORDERS",
66
+ "RECORD_RUNTIME_TRADES",
67
+ "configId"
68
+ ];
69
+ var toReleaseConfig = async (strategyName, sourceConfig) => {
70
+ const defaults = await (0, import_strategies.getStrategyDefaults)(strategyName, projectRoot) ?? {};
71
+ const config = { ...defaults, ...sourceConfig };
72
+ for (const key of OPERATIONAL_CONFIG_KEYS) delete config[key];
73
+ return config;
74
+ };
75
+ var writeMigrationBackup = async (payload) => {
76
+ const directory = import_node_path.default.join(projectRoot, "data", "runtime-config-backups");
77
+ await (0, import_promises.mkdir)(directory, { recursive: true });
78
+ const backupPath = import_node_path.default.join(
79
+ directory,
80
+ `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`
81
+ );
82
+ await (0, import_promises.writeFile)(backupPath, `${JSON.stringify(payload, null, 2)}
83
+ `, {
84
+ flag: "wx"
85
+ });
86
+ return backupPath;
87
+ };
88
+ var publish = async ({
89
+ strategyName,
90
+ sourceConfig
91
+ }) => {
92
+ const config = await toReleaseConfig(strategyName, sourceConfig);
93
+ const metadata = await (0, import_runtimeStrategies.getRuntimeStrategyPackageMetadata)({
94
+ strategyName,
95
+ projectRoot
96
+ });
97
+ return (0, import_runtimeStrategyReleases.publishRuntimeStrategyRelease)({
98
+ userName,
99
+ strategyName,
100
+ config,
101
+ ...metadata,
102
+ createdBy: `cli:${userName}`
103
+ });
104
+ };
105
+ var migrate = async () => {
106
+ const strategyName = required("strategy");
107
+ const configId = option("config") ?? "config";
108
+ const deploymentId = required("deployment");
109
+ const [sourceConfig, deployment] = await Promise.all([
110
+ (0, import_runtimeStrategyConfigs.getRuntimeStrategyConfig)(userName, strategyName, configId),
111
+ (0, import_runtimeDeployments.getRuntimeDeployment)(userName, deploymentId)
112
+ ]);
113
+ if (!sourceConfig) {
114
+ throw new Error(`Legacy config not found: ${strategyName}:${configId}`);
115
+ }
116
+ if (!deployment) throw new Error(`Deployment not found: ${deploymentId}`);
117
+ const reference = deployment.strategies.find(
118
+ (strategy) => strategy.strategyName === strategyName
119
+ );
120
+ if (!reference) {
121
+ throw new Error(`${strategyName} is not bound to ${deploymentId}`);
122
+ }
123
+ const preview = {
124
+ source: { configId, config: sourceConfig },
125
+ releaseConfig: await toReleaseConfig(strategyName, sourceConfig),
126
+ deployment: {
127
+ id: deployment.id,
128
+ accountId: deployment.accountId,
129
+ strategy: {
130
+ strategyName,
131
+ releaseVersion: "<allocated on write>",
132
+ controlState: "entries_paused"
133
+ }
134
+ }
135
+ };
136
+ if (!hasFlag("write")) {
137
+ console.log(JSON.stringify({ dryRun: true, ...preview }, null, 2));
138
+ return;
139
+ }
140
+ const backupPath = await writeMigrationBackup({
141
+ userName,
142
+ capturedAt: Date.now(),
143
+ deployment,
144
+ legacyConfig: sourceConfig
145
+ });
146
+ const release = await publish({ strategyName, sourceConfig });
147
+ const updatedDeployment = {
148
+ ...deployment,
149
+ strategies: deployment.strategies.map(
150
+ (strategy) => strategy.strategyName === strategyName ? {
151
+ strategyName,
152
+ releaseVersion: release.releaseVersion,
153
+ controlState: "entries_paused"
154
+ } : strategy
155
+ )
156
+ };
157
+ await (0, import_runtimeDeployments.saveRuntimeDeployment)(userName, updatedDeployment);
158
+ console.log(
159
+ JSON.stringify(
160
+ { migrated: true, backupPath, release, deployment: updatedDeployment },
161
+ null,
162
+ 2
163
+ )
164
+ );
165
+ };
166
+ var publishDraft = async () => {
167
+ const strategyName = required("strategy");
168
+ const draft = await (0, import_runtimeStrategyReleases.getRuntimeStrategyDraft)(userName, strategyName);
169
+ if (!draft) throw new Error(`Draft not found: ${strategyName}`);
170
+ const release = await publish({ strategyName, sourceConfig: draft.config });
171
+ console.log(JSON.stringify({ release }, null, 2));
172
+ };
173
+ var updateControlState = async (nextState) => {
174
+ const strategyName = required("strategy");
175
+ const deploymentId = required("deployment");
176
+ const deployment = await (0, import_runtimeDeployments.getRuntimeDeployment)(userName, deploymentId);
177
+ if (!deployment) throw new Error(`Deployment not found: ${deploymentId}`);
178
+ const reference = deployment.strategies.find(
179
+ (strategy) => strategy.strategyName === strategyName
180
+ );
181
+ if (!reference?.releaseVersion) {
182
+ throw new Error(`${strategyName} is not a versioned deployment reference`);
183
+ }
184
+ const previousState = reference.controlState ?? "active";
185
+ const updated = await (0, import_runtimeDeployments.saveRuntimeDeployment)(userName, {
186
+ ...deployment,
187
+ strategies: deployment.strategies.map(
188
+ (strategy) => strategy.strategyName === strategyName ? { ...strategy, controlState: nextState } : strategy
189
+ )
190
+ });
191
+ const event = previousState === nextState ? null : await (0, import_runtimeStrategyReleases.recordRuntimeStrategyControlEvent)({
192
+ userName,
193
+ deploymentId,
194
+ strategyName,
195
+ releaseVersion: reference.releaseVersion,
196
+ previousState,
197
+ nextState,
198
+ createdBy: `cli:${userName}`
199
+ });
200
+ console.log(JSON.stringify({ deployment: updated, event }, null, 2));
201
+ };
202
+ var rollback = async () => {
203
+ const strategyName = required("strategy");
204
+ const deploymentId = required("deployment");
205
+ const releaseVersion = Number(required("release"));
206
+ if (!Number.isSafeInteger(releaseVersion) || releaseVersion <= 0) {
207
+ throw new Error("--release must be a positive integer");
208
+ }
209
+ const deployment = await (0, import_runtimeDeployments.getRuntimeDeployment)(userName, deploymentId);
210
+ if (!deployment) throw new Error(`Deployment not found: ${deploymentId}`);
211
+ const release = (await (0, import_runtimeStrategyReleases.listRuntimeStrategyReleases)(userName, strategyName)).find((candidate) => candidate.releaseVersion === releaseVersion);
212
+ if (!release)
213
+ throw new Error(`Release not found: ${strategyName} v${releaseVersion}`);
214
+ const backupPath = await writeMigrationBackup({
215
+ userName,
216
+ capturedAt: Date.now(),
217
+ deployment,
218
+ reason: "rollback"
219
+ });
220
+ const updated = await (0, import_runtimeDeployments.saveRuntimeDeployment)(userName, {
221
+ ...deployment,
222
+ strategies: deployment.strategies.map(
223
+ (strategy) => strategy.strategyName === strategyName ? {
224
+ strategyName,
225
+ releaseVersion,
226
+ controlState: "entries_paused"
227
+ } : strategy
228
+ )
229
+ });
230
+ console.log(JSON.stringify({ backupPath, deployment: updated }, null, 2));
231
+ };
232
+ var verify = async () => {
233
+ const deployments = await (0, import_runtimeDeployments.listRuntimeDeployments)(userName);
234
+ const selected = option("deployment") ? deployments.filter((deployment) => deployment.id === option("deployment")) : deployments;
235
+ const results = [];
236
+ for (const deployment of selected) {
237
+ try {
238
+ const strategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
239
+ userName,
240
+ projectRoot,
241
+ deployment,
242
+ connectorName: deployment.connectorName
243
+ });
244
+ results.push({
245
+ deploymentId: deployment.id,
246
+ ok: true,
247
+ strategies: strategies.map((strategy) => ({
248
+ strategyName: strategy.strategyName,
249
+ releaseVersion: strategy.releaseVersion,
250
+ controlState: strategy.controlState,
251
+ interval: strategy.interval,
252
+ universe: strategy.universe,
253
+ accountId: strategy.accountId
254
+ }))
255
+ });
256
+ } catch (error) {
257
+ results.push({
258
+ deploymentId: deployment.id,
259
+ ok: false,
260
+ error: error instanceof Error ? error.message : String(error)
261
+ });
262
+ }
263
+ }
264
+ console.log(JSON.stringify({ userName, results }, null, 2));
265
+ if (results.some((result) => !result.ok)) process.exitCode = 1;
266
+ };
267
+ var inspect = async () => {
268
+ const strategyName = option("strategy");
269
+ const deployments = await (0, import_runtimeDeployments.listRuntimeDeployments)(userName);
270
+ const releases = strategyName ? await (0, import_runtimeStrategyReleases.listRuntimeStrategyReleases)(userName, strategyName) : void 0;
271
+ console.log(JSON.stringify({ userName, deployments, releases }, null, 2));
272
+ };
273
+ var runtimeConfig = async () => {
274
+ if (action === "migrate") return migrate();
275
+ if (action === "publish-draft") return publishDraft();
276
+ if (action === "verify") return verify();
277
+ if (action === "pause") return updateControlState("entries_paused");
278
+ if (action === "resume") return updateControlState("active");
279
+ if (action === "rollback") return rollback();
280
+ if (action === "inspect") return inspect();
281
+ throw new Error(
282
+ "Usage: tradejs runtime-config inspect|verify|migrate|publish-draft|pause|resume|rollback"
283
+ );
284
+ };
285
+ var main = runtimeConfig;
286
+ // Annotate the CommonJS export names for ESM import in node:
287
+ 0 && (module.exports = {
288
+ main,
289
+ runtimeConfig
290
+ });
@@ -568,11 +568,12 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
568
568
 
569
569
  // src/lib/strategyGateFingerprint.ts
570
570
  var import_node_crypto2 = require("crypto");
571
+ var import_node_module = require("module");
571
572
  var import_promises2 = __toESM(require("fs/promises"));
572
573
  var import_node_path2 = __toESM(require("path"));
573
574
 
574
575
  // src/lib/runtimeLineage.ts
575
- var runtimeLineageKey = (lineage) => [
576
+ var runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? ["v2", lineage.releaseVersion].join(":") : [
576
577
  lineage.schemaVersion,
577
578
  lineage.compositionId ?? "unbound-composition",
578
579
  lineage.gitSha ?? "unknown",
@@ -4808,7 +4808,7 @@ var buildRuntimeModeStrategyConfig = ({
4808
4808
  ...strategyConfig,
4809
4809
  ENV: env,
4810
4810
  MAKE_ORDERS: makeOrders,
4811
- INTERVAL: interval2,
4811
+ INTERVAL: strategyConfig.INTERVAL ?? interval2,
4812
4812
  ...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
4813
4813
  ...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
4814
4814
  });