@alphafox/cli 0.3.21 → 0.3.23
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/auth/refresh.js +3 -4
- package/dist/commands/run.js +2 -2
- package/dist/engine-backtest/fetch-runtime.d.ts +4 -0
- package/dist/engine-backtest/fetch-runtime.js +103 -17
- package/dist/engine-backtest/parse-args.js +10 -2
- package/dist/engine-backtest/run-command.js +4 -1
- package/dist/engine-backtest/sweep-command.js +2 -2
- package/dist/engine-backtest/types.d.ts +5 -1
- package/dist/http/client.js +3 -3
- package/dist/skills-manifest.json +45 -45
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/agents/domain.md +3 -49
- package/docs/agents/issue-tracker.md +5 -41
- package/docs/agents/triage-labels.md +2 -14
- package/package.json +1 -1
- package/skills/account/SKILL.md +1 -1
- package/skills/admin/SKILL.md +1 -1
- package/skills/alphafox/SKILL.md +6 -28
- package/skills/alphafox-shared/SKILL.md +6 -6
- package/skills/auth/SKILL.md +1 -1
- package/skills/cache/SKILL.md +3 -3
- package/skills/engine-backtest/SKILL.md +3 -3
- package/skills/exchange/SKILL.md +1 -1
- package/skills/market/SKILL.md +2 -2
- package/skills/notification/SKILL.md +1 -1
- package/skills/strategy/SKILL.md +6 -4
- package/skills/trading/SKILL.md +2 -2
- package/vendor/backtest-runner/README.md +2 -0
- package/vendor/backtest-runner/index.d.ts +8 -2
- package/vendor/backtest-runner/index.mjs +5 -2
- package/vendor/backtest-runner/lib/series.mjs +5 -171
- package/vendor/backtest-runner/lib/tape-loader-concurrency.mjs +72 -0
- package/vendor/backtest-runner/lib/tape-loader-range-plan.mjs +77 -0
- package/vendor/backtest-runner/lib/tape-loader-range.mjs +222 -0
- package/vendor/backtest-runner/lib/tape-loader.mjs +13 -41
package/dist/auth/refresh.js
CHANGED
|
@@ -257,10 +257,9 @@ async function fetchFollowingSameSiteRedirects(fetchImpl, startUrl, init, maxHop
|
|
|
257
257
|
const nextUrl = new URL(location, url).toString();
|
|
258
258
|
if (!sameAuthSite(originOf(url), originOf(nextUrl)))
|
|
259
259
|
break;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
method !== "HEAD")) {
|
|
260
|
+
// 303 is the only redirect that switches to GET. Apex→www is Cloudflare 301;
|
|
261
|
+
// converting POST to GET drops the OAuth body and the AS returns 405.
|
|
262
|
+
if (response.status === 303) {
|
|
264
263
|
method = "GET";
|
|
265
264
|
body = undefined;
|
|
266
265
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -101,8 +101,8 @@ async function runCli(argv, env = process.env) {
|
|
|
101
101
|
"alphafox schema [operationId]",
|
|
102
102
|
"alphafox catalog",
|
|
103
103
|
"alphafox api METHOD PATH [--body JSON|--config @file]",
|
|
104
|
-
"alphafox engine-backtest run --experiment <uuid> --definition <id> --config @file --exchange <id> --range FROM..TO --initial-equity N",
|
|
105
|
-
"alphafox engine-backtest sweep --experiment <uuid> --definition <id> --config @file --axes @file --exchange <id> --range FROM..TO --initial-equity N --no-persist",
|
|
104
|
+
"alphafox engine-backtest run --experiment <uuid> --definition <id> --config @file --exchange <id> --range FROM..TO --initial-equity N [--verbose]",
|
|
105
|
+
"alphafox engine-backtest sweep --experiment <uuid> --definition <id> --config @file --axes @file --exchange <id> --range FROM..TO --initial-equity N --no-persist [--verbose]",
|
|
106
106
|
"alphafox cache status|clean [--tape|--runtime|--all]",
|
|
107
107
|
"alphafox resolve-symbols <query...> [--exchange binance] [--asset-class equity_perp]",
|
|
108
108
|
"alphafox <domain> <resource> <action> [flags]",
|
|
@@ -8,6 +8,8 @@ export declare const BLOB_RUNTIME_FILES: {
|
|
|
8
8
|
readonly node: "node.mjs";
|
|
9
9
|
readonly nodeWorker: "worker-node.mjs";
|
|
10
10
|
readonly nodeWorkerPath: "worker-node-path.mjs";
|
|
11
|
+
readonly passivbotKernel: "passivbot_kernel.wasm";
|
|
12
|
+
readonly passivbotKernelModule: "passivbot-kernel.mjs";
|
|
11
13
|
};
|
|
12
14
|
export type BlobRuntimeFileKey = keyof typeof BLOB_RUNTIME_FILES;
|
|
13
15
|
export interface EngineBacktestBlobManifest {
|
|
@@ -23,6 +25,8 @@ export interface EngineBacktestBlobManifest {
|
|
|
23
25
|
readonly node: string;
|
|
24
26
|
readonly nodeWorker: string;
|
|
25
27
|
readonly nodeWorkerPath: string;
|
|
28
|
+
readonly passivbotKernel: string;
|
|
29
|
+
readonly passivbotKernelModule: string;
|
|
26
30
|
}
|
|
27
31
|
export interface FetchRuntimeHooks {
|
|
28
32
|
readonly fetch?: typeof fetch;
|
|
@@ -5,12 +5,13 @@ exports.resolveBacktestWasmManifestUrl = resolveBacktestWasmManifestUrl;
|
|
|
5
5
|
exports.resolveRuntimeCacheDir = resolveRuntimeCacheDir;
|
|
6
6
|
exports.parseEngineBacktestBlobManifest = parseEngineBacktestBlobManifest;
|
|
7
7
|
exports.ensureBlobRuntime = ensureBlobRuntime;
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
9
|
const node_fs_1 = require("node:fs");
|
|
9
10
|
const promises_1 = require("node:fs/promises");
|
|
10
11
|
const node_os_1 = require("node:os");
|
|
11
12
|
const node_path_1 = require("node:path");
|
|
12
|
-
const promises_2 = require("node:stream/promises");
|
|
13
13
|
const node_stream_1 = require("node:stream");
|
|
14
|
+
const promises_2 = require("node:stream/promises");
|
|
14
15
|
const errors_1 = require("./errors");
|
|
15
16
|
exports.BACKTEST_RUNTIME_PROTOCOL = 1;
|
|
16
17
|
exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL = "https://zwggllrrna54e2d6.public.blob.vercel-storage.com/engine-backtest/latest.json";
|
|
@@ -22,7 +23,20 @@ exports.BLOB_RUNTIME_FILES = {
|
|
|
22
23
|
node: "node.mjs",
|
|
23
24
|
nodeWorker: "worker-node.mjs",
|
|
24
25
|
nodeWorkerPath: "worker-node-path.mjs",
|
|
26
|
+
passivbotKernel: "passivbot_kernel.wasm",
|
|
27
|
+
passivbotKernelModule: "passivbot-kernel.mjs",
|
|
25
28
|
};
|
|
29
|
+
const HASHED_BLOB_RUNTIME_FILES = [
|
|
30
|
+
exports.BLOB_RUNTIME_FILES.wasm,
|
|
31
|
+
exports.BLOB_RUNTIME_FILES.passivbotKernel,
|
|
32
|
+
exports.BLOB_RUNTIME_FILES.wasmExec,
|
|
33
|
+
exports.BLOB_RUNTIME_FILES.worker,
|
|
34
|
+
exports.BLOB_RUNTIME_FILES.passivbotKernelModule,
|
|
35
|
+
exports.BLOB_RUNTIME_FILES.client,
|
|
36
|
+
exports.BLOB_RUNTIME_FILES.node,
|
|
37
|
+
exports.BLOB_RUNTIME_FILES.nodeWorker,
|
|
38
|
+
exports.BLOB_RUNTIME_FILES.nodeWorkerPath,
|
|
39
|
+
];
|
|
26
40
|
function resolveBacktestWasmManifestUrl(env = process.env) {
|
|
27
41
|
return (env.ALPHAFOX_BACKTEST_WASM_MANIFEST_URL?.trim() ||
|
|
28
42
|
exports.DEFAULT_BACKTEST_WASM_MANIFEST_URL);
|
|
@@ -51,9 +65,19 @@ function parseEngineBacktestBlobManifest(value) {
|
|
|
51
65
|
message: `Backtest runtime protocol is incompatible (got ${String(record.protocol)}, expected ${exports.BACKTEST_RUNTIME_PROTOCOL}).`,
|
|
52
66
|
});
|
|
53
67
|
}
|
|
68
|
+
const hash = readRequiredString(record.hash, "hash");
|
|
69
|
+
if (!/^[0-9a-f]{16}$/.test(hash)) {
|
|
70
|
+
throw new errors_1.EngineBacktestError({
|
|
71
|
+
type: "runtime",
|
|
72
|
+
subtype: "runtime_manifest_invalid",
|
|
73
|
+
message: "Backtest runtime manifest hash must be 16 lowercase hex characters.",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const passivbotKernel = readRequiredHttps(record.passivbotKernel, "passivbotKernel");
|
|
77
|
+
const passivbotKernelModule = readRequiredHttps(record.passivbotKernelModule, "passivbotKernelModule");
|
|
54
78
|
return {
|
|
55
79
|
version: readRequiredString(record.version, "version"),
|
|
56
|
-
hash
|
|
80
|
+
hash,
|
|
57
81
|
protocol: exports.BACKTEST_RUNTIME_PROTOCOL,
|
|
58
82
|
engineSha: readOptionalString(record.engineSha),
|
|
59
83
|
packageVersion: readOptionalString(record.packageVersion),
|
|
@@ -64,6 +88,8 @@ function parseEngineBacktestBlobManifest(value) {
|
|
|
64
88
|
node: readRequiredHttps(record.node, "node"),
|
|
65
89
|
nodeWorker: readRequiredHttps(record.nodeWorker, "nodeWorker"),
|
|
66
90
|
nodeWorkerPath: readRequiredHttps(record.nodeWorkerPath, "nodeWorkerPath"),
|
|
91
|
+
passivbotKernel,
|
|
92
|
+
passivbotKernelModule,
|
|
67
93
|
};
|
|
68
94
|
}
|
|
69
95
|
async function ensureBlobRuntime(env = process.env, hooks) {
|
|
@@ -93,20 +119,66 @@ async function ensureBlobRuntime(env = process.env, hooks) {
|
|
|
93
119
|
}
|
|
94
120
|
const manifest = parseEngineBacktestBlobManifest(await response.json());
|
|
95
121
|
const directory = hooks?.cacheDir ?? resolveRuntimeCacheDir(manifest.hash, env);
|
|
96
|
-
await (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
if (await runtimeMatchesHash(directory, manifest.hash)) {
|
|
123
|
+
return {
|
|
124
|
+
manifest,
|
|
125
|
+
directory,
|
|
126
|
+
nodeEntry: (0, node_path_1.join)(directory, exports.BLOB_RUNTIME_FILES.node),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(directory), { recursive: true });
|
|
130
|
+
let actualHash = "";
|
|
131
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
132
|
+
const staging = `${directory}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`;
|
|
133
|
+
await (0, promises_1.mkdir)(staging, { recursive: true });
|
|
134
|
+
try {
|
|
135
|
+
for (const key of Object.keys(exports.BLOB_RUNTIME_FILES)) {
|
|
136
|
+
await downloadFile(fetchImpl, manifest[key], (0, node_path_1.join)(staging, exports.BLOB_RUNTIME_FILES[key]));
|
|
137
|
+
}
|
|
138
|
+
if (!(await allRuntimeFilesExist(staging))) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
actualHash = await hashBlobRuntime(staging);
|
|
142
|
+
if (actualHash !== manifest.hash) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
await (0, promises_1.mkdir)(directory, { recursive: true });
|
|
146
|
+
for (const fileName of Object.values(exports.BLOB_RUNTIME_FILES)) {
|
|
147
|
+
await (0, promises_1.rename)((0, node_path_1.join)(staging, fileName), (0, node_path_1.join)(directory, fileName));
|
|
148
|
+
}
|
|
149
|
+
if (await runtimeMatchesHash(directory, manifest.hash)) {
|
|
150
|
+
return {
|
|
151
|
+
manifest,
|
|
152
|
+
directory,
|
|
153
|
+
nodeEntry: (0, node_path_1.join)(directory, exports.BLOB_RUNTIME_FILES.node),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await (0, promises_1.rm)(staging, { force: true, recursive: true });
|
|
102
159
|
}
|
|
103
|
-
await downloadFile(fetchImpl, manifest[key], target);
|
|
104
160
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
161
|
+
throw new errors_1.EngineBacktestError({
|
|
162
|
+
type: "runtime",
|
|
163
|
+
subtype: "runtime_integrity_failed",
|
|
164
|
+
message: `Backtest runtime hash mismatch: expected ${manifest.hash}, received ${actualHash}.`,
|
|
165
|
+
hint: "Retry the download; no unverified runtime was cached.",
|
|
166
|
+
details: { expectedHash: manifest.hash, actualHash, directory },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function runtimeMatchesHash(directory, expectedHash) {
|
|
170
|
+
if (!(await allRuntimeFilesExist(directory))) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
return (await hashBlobRuntime(directory)) === expectedHash;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function allRuntimeFilesExist(directory) {
|
|
181
|
+
return (await Promise.all(Object.values(exports.BLOB_RUNTIME_FILES).map((fileName) => fileExists((0, node_path_1.join)(directory, fileName))))).every(Boolean);
|
|
110
182
|
}
|
|
111
183
|
async function downloadFile(fetchImpl, url, target) {
|
|
112
184
|
let response;
|
|
@@ -127,9 +199,23 @@ async function downloadFile(fetchImpl, url, target) {
|
|
|
127
199
|
message: `Backtest runtime file unavailable (${url} HTTP ${response.status}).`,
|
|
128
200
|
});
|
|
129
201
|
}
|
|
130
|
-
const tmp = `${target}.tmp`;
|
|
131
|
-
|
|
132
|
-
|
|
202
|
+
const tmp = `${target}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`;
|
|
203
|
+
try {
|
|
204
|
+
await (0, promises_2.pipeline)(node_stream_1.Readable.fromWeb(response.body), (0, node_fs_1.createWriteStream)(tmp));
|
|
205
|
+
await (0, promises_1.rename)(tmp, target);
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
await (0, promises_1.rm)(tmp, { force: true });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function hashBlobRuntime(directory) {
|
|
212
|
+
const digest = (0, node_crypto_1.createHash)("sha256");
|
|
213
|
+
for (const fileName of HASHED_BLOB_RUNTIME_FILES) {
|
|
214
|
+
for await (const chunk of (0, node_fs_1.createReadStream)((0, node_path_1.join)(directory, fileName))) {
|
|
215
|
+
digest.update(chunk);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return digest.digest("hex").slice(0, 16);
|
|
133
219
|
}
|
|
134
220
|
async function fileExists(path) {
|
|
135
221
|
try {
|
|
@@ -16,11 +16,11 @@ const TIERS = new Set(["free", "pro", "pro_max"]);
|
|
|
16
16
|
const DATA_QUALITY = new Set(["strict", "basic"]);
|
|
17
17
|
const PRICE_PATHS = new Set(["ohlc_path_4", "close_only"]);
|
|
18
18
|
exports.ENGINE_BACKTEST_RUN_USAGE = [
|
|
19
|
-
"alphafox engine-backtest run --experiment <uuid> --definition <id> --config @file.json --exchange <id> --range YYYY-MM-DD..YYYY-MM-DD --initial-equity N [--replay-timeframe 1m]",
|
|
19
|
+
"alphafox engine-backtest run --experiment <uuid> --definition <id> --config @file.json --exchange <id> --range YYYY-MM-DD..YYYY-MM-DD --initial-equity N [--replay-timeframe 1m] [--verbose]",
|
|
20
20
|
"alphafox engine-backtest run --create-experiment --name <name> --definition <id> --config @file.json --exchange <id> --from YYYY-MM-DD --to YYYY-MM-DD --initial-equity N",
|
|
21
21
|
];
|
|
22
22
|
exports.ENGINE_BACKTEST_SWEEP_USAGE = [
|
|
23
|
-
"alphafox engine-backtest sweep --experiment <uuid> --definition <id> --config @file.json --axes @axes.json --exchange <id> --range YYYY-MM-DD..YYYY-MM-DD --initial-equity N [--no-persist] [--mode neighborhood|range] [--search-mode standard|fast] [--concurrency N]",
|
|
23
|
+
"alphafox engine-backtest sweep --experiment <uuid> --definition <id> --config @file.json --axes @axes.json --exchange <id> --range YYYY-MM-DD..YYYY-MM-DD --initial-equity N [--no-persist] [--mode neighborhood|range] [--search-mode standard|fast] [--concurrency N] [--verbose]",
|
|
24
24
|
];
|
|
25
25
|
const SWEEP_MODES = new Set(["neighborhood", "range"]);
|
|
26
26
|
const SEARCH_MODES = new Set(["standard", "fast"]);
|
|
@@ -112,6 +112,7 @@ function parseEngineBacktestRunArgs(args) {
|
|
|
112
112
|
let configSchemaVersion;
|
|
113
113
|
let executionModelOverride;
|
|
114
114
|
let persist = true;
|
|
115
|
+
let verbose = false;
|
|
115
116
|
let help = false;
|
|
116
117
|
let replayTimeframe = replay_timeframe_1.ENGINE_BACKTEST_DEFAULT_REPLAY_TIMEFRAME;
|
|
117
118
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -128,6 +129,10 @@ function parseEngineBacktestRunArgs(args) {
|
|
|
128
129
|
persist = false;
|
|
129
130
|
continue;
|
|
130
131
|
}
|
|
132
|
+
if (a === "--verbose") {
|
|
133
|
+
verbose = true;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
131
136
|
if (!a.startsWith("--")) {
|
|
132
137
|
usage(`Unexpected argument: ${a}`, "unexpected_arg");
|
|
133
138
|
}
|
|
@@ -227,6 +232,7 @@ function parseEngineBacktestRunArgs(args) {
|
|
|
227
232
|
tier,
|
|
228
233
|
dataQualityMode,
|
|
229
234
|
persist,
|
|
235
|
+
verbose,
|
|
230
236
|
replayTimeframe,
|
|
231
237
|
};
|
|
232
238
|
}
|
|
@@ -284,6 +290,7 @@ function parseEngineBacktestRunArgs(args) {
|
|
|
284
290
|
configSchemaVersion,
|
|
285
291
|
executionModelOverride,
|
|
286
292
|
persist,
|
|
293
|
+
verbose,
|
|
287
294
|
replayTimeframe,
|
|
288
295
|
};
|
|
289
296
|
}
|
|
@@ -346,6 +353,7 @@ function parseEngineBacktestSweepArgs(args) {
|
|
|
346
353
|
createExperiment: false,
|
|
347
354
|
dataQualityMode: types_1.DEFAULT_DATA_QUALITY_MODE,
|
|
348
355
|
persist: true,
|
|
356
|
+
verbose: false,
|
|
349
357
|
replayTimeframe: replay_timeframe_1.ENGINE_BACKTEST_DEFAULT_REPLAY_TIMEFRAME,
|
|
350
358
|
axesRaw,
|
|
351
359
|
mode,
|
|
@@ -222,7 +222,9 @@ async function executeEngineBacktestRun(args, flags, env = process.env, deps = {
|
|
|
222
222
|
wasm = wasmLoaded.module;
|
|
223
223
|
runner = runnerLoaded.module;
|
|
224
224
|
}
|
|
225
|
-
const client = wasm.createNodeBacktestClient(
|
|
225
|
+
const client = wasm.createNodeBacktestClient({
|
|
226
|
+
verbose: args.verbose,
|
|
227
|
+
});
|
|
226
228
|
try {
|
|
227
229
|
if (typeof client.init === "function") {
|
|
228
230
|
await client.init();
|
|
@@ -449,6 +451,7 @@ function engineBacktestHelpData() {
|
|
|
449
451
|
"runs.create is write (not high-risk-write); --yes is not required",
|
|
450
452
|
"Do not pass --token; use alphafox auth login",
|
|
451
453
|
"--replay-timeframe defaults to 1m (min 1m). Indicator series still download their native plan timeframes.",
|
|
454
|
+
"Raw Engine worker logs, including per-trade output, are hidden by default. Use --verbose to forward them.",
|
|
452
455
|
"--data-quality defaults to basic: hard tape failures still stop, soft gaps finish the run and appear as coverageNotice (prefix_gap less severe, internal_gap more severe). Use --data-quality strict to fail on any gap.",
|
|
453
456
|
],
|
|
454
457
|
};
|
|
@@ -85,7 +85,7 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
85
85
|
const { wasm, runner } = await loadRuntime(deps, env);
|
|
86
86
|
const clients = [];
|
|
87
87
|
try {
|
|
88
|
-
const client = wasm.createNodeBacktestClient();
|
|
88
|
+
const client = wasm.createNodeBacktestClient({ verbose: args.verbose });
|
|
89
89
|
clients.push(client);
|
|
90
90
|
if (typeof client.init === "function") {
|
|
91
91
|
await client.init();
|
|
@@ -221,7 +221,7 @@ async function executeEngineBacktestSweep(args, flags, env = process.env, deps =
|
|
|
221
221
|
}
|
|
222
222
|
emitProgress("tape", 1);
|
|
223
223
|
while (clients.length < concurrency) {
|
|
224
|
-
const extra = wasm.createNodeBacktestClient();
|
|
224
|
+
const extra = wasm.createNodeBacktestClient({ verbose: args.verbose });
|
|
225
225
|
clients.push(extra);
|
|
226
226
|
if (typeof extra.init === "function") {
|
|
227
227
|
await extra.init();
|
|
@@ -50,6 +50,8 @@ export interface EngineBacktestRunArgs {
|
|
|
50
50
|
readonly configSchemaVersion?: number;
|
|
51
51
|
readonly executionModelOverride?: Partial<ExecutionModel>;
|
|
52
52
|
readonly persist: boolean;
|
|
53
|
+
/** Forward raw Engine WASM worker stdout/stderr. Disabled by default. */
|
|
54
|
+
readonly verbose: boolean;
|
|
53
55
|
/** Replay/download bar. Defaults to 1m; never finer. */
|
|
54
56
|
readonly replayTimeframe: string;
|
|
55
57
|
}
|
|
@@ -256,7 +258,9 @@ export interface BacktestClientLike {
|
|
|
256
258
|
terminate(): void;
|
|
257
259
|
}
|
|
258
260
|
export interface BacktestWasmModule {
|
|
259
|
-
createNodeBacktestClient(options?:
|
|
261
|
+
createNodeBacktestClient(options?: {
|
|
262
|
+
readonly verbose?: boolean;
|
|
263
|
+
}): BacktestClientLike;
|
|
260
264
|
}
|
|
261
265
|
export interface BacktestRunnerModule {
|
|
262
266
|
loadTape(request: {
|
package/dist/http/client.js
CHANGED
|
@@ -146,9 +146,9 @@ async function fetchFollowingAuthRedirects(fetchImpl, startUrl, init, maxHops =
|
|
|
146
146
|
if (!sameAuthSite(originOf(url), originOf(nextUrl))) {
|
|
147
147
|
break;
|
|
148
148
|
}
|
|
149
|
-
// 303 switches to GET
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
// 303 is the only redirect that switches to GET. Apex→www is Cloudflare 301;
|
|
150
|
+
// converting POST to GET drops the OAuth body and the AS returns 405.
|
|
151
|
+
if (response.status === 303) {
|
|
152
152
|
method = "GET";
|
|
153
153
|
body = undefined;
|
|
154
154
|
}
|
|
@@ -1,153 +1,153 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "@alphafox/cli",
|
|
4
|
-
"packageVersion": "0.3.
|
|
4
|
+
"packageVersion": "0.3.23",
|
|
5
5
|
"contractVersion": "2026-08-31",
|
|
6
|
-
"bundleHash": "
|
|
6
|
+
"bundleHash": "cefb5f2b4c977a754941282a46780e64143978f21d4ad1db4da9fed993455094",
|
|
7
7
|
"skills": [
|
|
8
8
|
{
|
|
9
9
|
"name": "alphafox",
|
|
10
|
-
"version": "0.3.
|
|
10
|
+
"version": "0.3.23",
|
|
11
11
|
"files": [
|
|
12
12
|
{
|
|
13
13
|
"path": "SKILL.md",
|
|
14
|
-
"sha256": "
|
|
15
|
-
"size":
|
|
14
|
+
"sha256": "ad970465cc7a8b6db68253cd6c6b375e824c32c197bd10cdd4735052a9dc54e6",
|
|
15
|
+
"size": 6711
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
|
-
"hash": "
|
|
18
|
+
"hash": "57b1b2996a4cb8e22ee36ccacae56ff36bcd64ac7203e528ccbca4b898f39a07"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "alphafox-account",
|
|
22
|
-
"version": "0.3.
|
|
22
|
+
"version": "0.3.23",
|
|
23
23
|
"files": [
|
|
24
24
|
{
|
|
25
25
|
"path": "SKILL.md",
|
|
26
|
-
"sha256": "
|
|
26
|
+
"sha256": "23a35e3a3d6ac9e7da639b9383750f66e9de4dbbe049861da78f7912e9076512",
|
|
27
27
|
"size": 784
|
|
28
28
|
}
|
|
29
29
|
],
|
|
30
|
-
"hash": "
|
|
30
|
+
"hash": "9b604ef9fc3c097333cf55b5e5fadb4c0179ad15c0dc05b3f76805abb4179be3"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"name": "alphafox-admin",
|
|
34
|
-
"version": "0.3.
|
|
34
|
+
"version": "0.3.23",
|
|
35
35
|
"files": [
|
|
36
36
|
{
|
|
37
37
|
"path": "SKILL.md",
|
|
38
|
-
"sha256": "
|
|
38
|
+
"sha256": "d53090a8e0ac05b5d8d753d52086389ed0ee0f9d405c3adfb120d06d3a17841e",
|
|
39
39
|
"size": 2078
|
|
40
40
|
}
|
|
41
41
|
],
|
|
42
|
-
"hash": "
|
|
42
|
+
"hash": "f8c047c2c4f1a49c13d5fe92c1676108fe31bcfdb3eebfb49fbe7719586944bb"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"name": "alphafox-auth",
|
|
46
|
-
"version": "0.3.
|
|
46
|
+
"version": "0.3.23",
|
|
47
47
|
"files": [
|
|
48
48
|
{
|
|
49
49
|
"path": "SKILL.md",
|
|
50
|
-
"sha256": "
|
|
50
|
+
"sha256": "a563b8c330fcee564b9dae3ab177b36e6612a5e6e28c0dcc2770ebe699f45181",
|
|
51
51
|
"size": 2371
|
|
52
52
|
}
|
|
53
53
|
],
|
|
54
|
-
"hash": "
|
|
54
|
+
"hash": "f6e8ac98bca895fb0e5afb0f7f915645267404aa77cc85724475287866619d68"
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"name": "alphafox-cache",
|
|
58
|
-
"version": "0.3.
|
|
58
|
+
"version": "0.3.23",
|
|
59
59
|
"files": [
|
|
60
60
|
{
|
|
61
61
|
"path": "SKILL.md",
|
|
62
|
-
"sha256": "
|
|
63
|
-
"size":
|
|
62
|
+
"sha256": "9858b145e37f91082af9f9ed07d7f89c63f51365de3e1d04925b4bf476fe11c9",
|
|
63
|
+
"size": 1762
|
|
64
64
|
}
|
|
65
65
|
],
|
|
66
|
-
"hash": "
|
|
66
|
+
"hash": "dd38538eb6a66500361406acba11818ef333c50c119735f8dc4e846d20aaee51"
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
"name": "alphafox-engine-backtest",
|
|
70
|
-
"version": "0.3.
|
|
70
|
+
"version": "0.3.23",
|
|
71
71
|
"files": [
|
|
72
72
|
{
|
|
73
73
|
"path": "SKILL.md",
|
|
74
|
-
"sha256": "
|
|
75
|
-
"size":
|
|
74
|
+
"sha256": "feded42f5db1201ea13dca0fdcff0eb8a210024440b9e253f6b2b213065f45bb",
|
|
75
|
+
"size": 9862
|
|
76
76
|
}
|
|
77
77
|
],
|
|
78
|
-
"hash": "
|
|
78
|
+
"hash": "9071605d37c982f7741fe533671fb10d97d17d0265db0712d8b784f4562334ad"
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
"name": "alphafox-exchange",
|
|
82
|
-
"version": "0.3.
|
|
82
|
+
"version": "0.3.23",
|
|
83
83
|
"files": [
|
|
84
84
|
{
|
|
85
85
|
"path": "SKILL.md",
|
|
86
|
-
"sha256": "
|
|
86
|
+
"sha256": "ed25e4487435552e7d2fb647a7c4ab0dcd96c11e65f6fd0076e841095497a412",
|
|
87
87
|
"size": 744
|
|
88
88
|
}
|
|
89
89
|
],
|
|
90
|
-
"hash": "
|
|
90
|
+
"hash": "9dcbabf7b94c11901b35f3cf9f2f980a718ce675534df44e406ef0fb4ace9082"
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
"name": "alphafox-market",
|
|
94
|
-
"version": "0.3.
|
|
94
|
+
"version": "0.3.23",
|
|
95
95
|
"files": [
|
|
96
96
|
{
|
|
97
97
|
"path": "SKILL.md",
|
|
98
|
-
"sha256": "
|
|
99
|
-
"size":
|
|
98
|
+
"sha256": "3e5835e3d4d658b43265ecb2d062bf2440e8f76f112104b0e66d4ee4a4362738",
|
|
99
|
+
"size": 3208
|
|
100
100
|
}
|
|
101
101
|
],
|
|
102
|
-
"hash": "
|
|
102
|
+
"hash": "ff13c2cf880c6a7e0a8c20da2fffcdfc32f05737e1d860c3d76c3b1883a70d9d"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"name": "alphafox-notification",
|
|
106
|
-
"version": "0.3.
|
|
106
|
+
"version": "0.3.23",
|
|
107
107
|
"files": [
|
|
108
108
|
{
|
|
109
109
|
"path": "SKILL.md",
|
|
110
|
-
"sha256": "
|
|
110
|
+
"sha256": "684c29d65ef408836fe6841d272ac9c3cbf725ee9763deb886fad2e1076c07a2",
|
|
111
111
|
"size": 699
|
|
112
112
|
}
|
|
113
113
|
],
|
|
114
|
-
"hash": "
|
|
114
|
+
"hash": "318099f64c6952496f21a7046723e4c5c9edfedb2f2687930cb5b80678ae22db"
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
"name": "alphafox-shared",
|
|
118
|
-
"version": "0.3.
|
|
118
|
+
"version": "0.3.23",
|
|
119
119
|
"files": [
|
|
120
120
|
{
|
|
121
121
|
"path": "SKILL.md",
|
|
122
|
-
"sha256": "
|
|
123
|
-
"size":
|
|
122
|
+
"sha256": "247c5f488e14c2ad1996d9a7167c69c593da31cb340f09bb1e52a572031db550",
|
|
123
|
+
"size": 7819
|
|
124
124
|
}
|
|
125
125
|
],
|
|
126
|
-
"hash": "
|
|
126
|
+
"hash": "5834e09bc8fdd24240c35233f8b5035b362a9d289a7f0f7698aac7480d1ef429"
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
"name": "alphafox-strategy",
|
|
130
|
-
"version": "0.3.
|
|
130
|
+
"version": "0.3.23",
|
|
131
131
|
"files": [
|
|
132
132
|
{
|
|
133
133
|
"path": "SKILL.md",
|
|
134
|
-
"sha256": "
|
|
135
|
-
"size":
|
|
134
|
+
"sha256": "cf1e0c364de027cdf63113848ae9eb2194e1efc8025c3224da3fd86cf6990adc",
|
|
135
|
+
"size": 8039
|
|
136
136
|
}
|
|
137
137
|
],
|
|
138
|
-
"hash": "
|
|
138
|
+
"hash": "a17d382e02007479b66f2b879d38c7eaaa802738dc679e2e96bf42ed7d9eeb4b"
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
"name": "alphafox-trading",
|
|
142
|
-
"version": "0.3.
|
|
142
|
+
"version": "0.3.23",
|
|
143
143
|
"files": [
|
|
144
144
|
{
|
|
145
145
|
"path": "SKILL.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"size":
|
|
146
|
+
"sha256": "70b4ad0b01e29102024141337f28bda4fb6cdf38292cd5001411908830a8a747",
|
|
147
|
+
"size": 4920
|
|
148
148
|
}
|
|
149
149
|
],
|
|
150
|
-
"hash": "
|
|
150
|
+
"hash": "b12154ddcc48a9b0248548f935bcae940b9d514b5c233f7c5e5590ef449ac329"
|
|
151
151
|
}
|
|
152
152
|
]
|
|
153
153
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const CLI_NAME = "alphafox";
|
|
2
2
|
export declare const CLI_PACKAGE = "@alphafox/cli";
|
|
3
|
-
export declare const CLI_VERSION = "0.3.
|
|
3
|
+
export declare const CLI_VERSION = "0.3.23";
|
|
4
4
|
export { CATALOG_VERSION as CLI_CONTRACT_VERSION } from "./catalog/operations";
|
package/dist/version.js
CHANGED
|
@@ -3,6 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CLI_CONTRACT_VERSION = exports.CLI_VERSION = exports.CLI_PACKAGE = exports.CLI_NAME = void 0;
|
|
4
4
|
exports.CLI_NAME = "alphafox";
|
|
5
5
|
exports.CLI_PACKAGE = "@alphafox/cli";
|
|
6
|
-
exports.CLI_VERSION = "0.3.
|
|
6
|
+
exports.CLI_VERSION = "0.3.23";
|
|
7
7
|
var operations_1 = require("./catalog/operations");
|
|
8
8
|
Object.defineProperty(exports, "CLI_CONTRACT_VERSION", { enumerable: true, get: function () { return operations_1.CATALOG_VERSION; } });
|
package/docs/agents/domain.md
CHANGED
|
@@ -1,51 +1,5 @@
|
|
|
1
|
-
# Domain
|
|
1
|
+
# Domain references
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
For a task that changes domain terminology, behavior across contexts, or architecture, locate the relevant entry in root `CONTEXT-MAP.md` (if present), otherwise `CONTEXT.md`, and read only the relevant glossary sections and ADRs. Ordinary documentation, formatting and isolated mechanical edits do not require a domain-document tour.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **`CONTEXT.md`** at the repo root, or
|
|
8
|
-
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
|
9
|
-
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
|
10
|
-
|
|
11
|
-
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
|
12
|
-
|
|
13
|
-
## File structure
|
|
14
|
-
|
|
15
|
-
Single-context repo (most repos):
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
/
|
|
19
|
-
├── CONTEXT.md
|
|
20
|
-
├── docs/adr/
|
|
21
|
-
│ ├── 0001-event-sourced-orders.md
|
|
22
|
-
│ └── 0002-postgres-for-write-model.md
|
|
23
|
-
└── src/
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
/
|
|
30
|
-
├── CONTEXT-MAP.md
|
|
31
|
-
├── docs/adr/ ← system-wide decisions
|
|
32
|
-
└── src/
|
|
33
|
-
├── ordering/
|
|
34
|
-
│ ├── CONTEXT.md
|
|
35
|
-
│ └── docs/adr/ ← context-specific decisions
|
|
36
|
-
└── billing/
|
|
37
|
-
├── CONTEXT.md
|
|
38
|
-
└── docs/adr/
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Use the glossary's vocabulary
|
|
42
|
-
|
|
43
|
-
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
|
44
|
-
|
|
45
|
-
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
|
46
|
-
|
|
47
|
-
## Flag ADR conflicts
|
|
48
|
-
|
|
49
|
-
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
|
50
|
-
|
|
51
|
-
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
|
5
|
+
Use established terms and surface an actual conflict with an existing ADR before changing the decision. Missing files are not a setup blocker. Add glossary entries or ADRs only when the task resolves a durable ambiguity or decision, not for every edit. Reuse unchanged material within the task.
|