@alphafox/cli 0.3.2 → 0.3.4

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.
Files changed (69) hide show
  1. package/README.md +4 -3
  2. package/dist/auth/loopback-callback.js +6 -6
  3. package/dist/catalog/allowlist.d.ts +1 -1
  4. package/dist/catalog/allowlist.js +1 -1
  5. package/dist/catalog/generated/registry.json +338 -49
  6. package/dist/catalog/generated/schemas.json +1880 -779
  7. package/dist/catalog/omit.d.ts +6 -0
  8. package/dist/catalog/omit.js +11 -0
  9. package/dist/catalog/operations.d.ts +1 -1
  10. package/dist/catalog/operations.js +5 -2
  11. package/dist/commands/run.js +10 -3
  12. package/dist/engine-backtest/load-config.d.ts +4 -0
  13. package/dist/engine-backtest/load-config.js +44 -0
  14. package/dist/engine-backtest/parse-args.d.ts +3 -1
  15. package/dist/engine-backtest/parse-args.js +87 -3
  16. package/dist/engine-backtest/parse-axes.d.ts +3 -0
  17. package/dist/engine-backtest/parse-axes.js +167 -0
  18. package/dist/engine-backtest/persist.d.ts +60 -1
  19. package/dist/engine-backtest/persist.js +196 -1
  20. package/dist/engine-backtest/return-curve.d.ts +27 -0
  21. package/dist/engine-backtest/return-curve.js +80 -0
  22. package/dist/engine-backtest/run-command.d.ts +1 -4
  23. package/dist/engine-backtest/run-command.js +61 -45
  24. package/dist/engine-backtest/sweep-command.d.ts +13 -0
  25. package/dist/engine-backtest/sweep-command.js +749 -0
  26. package/dist/engine-backtest/sweep-kernel/caps.d.ts +26 -0
  27. package/dist/engine-backtest/sweep-kernel/caps.js +48 -0
  28. package/dist/engine-backtest/sweep-kernel/index.d.ts +13 -0
  29. package/dist/engine-backtest/sweep-kernel/index.js +34 -0
  30. package/dist/engine-backtest/sweep-kernel/plan.d.ts +22 -0
  31. package/dist/engine-backtest/sweep-kernel/plan.js +320 -0
  32. package/dist/engine-backtest/sweep-kernel/results.d.ts +72 -0
  33. package/dist/engine-backtest/sweep-kernel/results.js +150 -0
  34. package/dist/engine-backtest/sweep-kernel/session.d.ts +55 -0
  35. package/dist/engine-backtest/sweep-kernel/session.js +56 -0
  36. package/dist/engine-backtest/sweep-kernel/types.d.ts +36 -0
  37. package/dist/engine-backtest/sweep-kernel/types.js +2 -0
  38. package/dist/engine-backtest/types.d.ts +130 -0
  39. package/dist/install/exec.js +5 -1
  40. package/dist/install/wizard.js +26 -29
  41. package/dist/resolve-symbols/catalog.d.ts +3 -0
  42. package/dist/resolve-symbols/catalog.js +50 -0
  43. package/dist/resolve-symbols/errors.d.ts +18 -0
  44. package/dist/resolve-symbols/errors.js +26 -0
  45. package/dist/resolve-symbols/exchanges.d.ts +8 -0
  46. package/dist/resolve-symbols/exchanges.js +53 -0
  47. package/dist/resolve-symbols/match.d.ts +6 -0
  48. package/dist/resolve-symbols/match.js +250 -0
  49. package/dist/resolve-symbols/parse-args.d.ts +5 -0
  50. package/dist/resolve-symbols/parse-args.js +106 -0
  51. package/dist/resolve-symbols/run-command.d.ts +21 -0
  52. package/dist/resolve-symbols/run-command.js +149 -0
  53. package/dist/resolve-symbols/types.d.ts +37 -0
  54. package/dist/resolve-symbols/types.js +2 -0
  55. package/dist/version.d.ts +1 -1
  56. package/dist/version.js +1 -1
  57. package/docs/alphafox-cli-installation-guide.md +2 -2
  58. package/package.json +1 -1
  59. package/skills/account/SKILL.md +1 -1
  60. package/skills/admin/SKILL.md +1 -1
  61. package/skills/alphafox/SKILL.md +38 -0
  62. package/skills/alphafox-shared/SKILL.md +5 -1
  63. package/skills/auth/SKILL.md +1 -1
  64. package/skills/engine-backtest/SKILL.md +48 -6
  65. package/skills/exchange/SKILL.md +1 -1
  66. package/skills/market/SKILL.md +27 -4
  67. package/skills/notification/SKILL.md +1 -1
  68. package/skills/strategy/SKILL.md +7 -20
  69. package/skills/trading/SKILL.md +3 -1
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractSweepMetrics = extractSweepMetrics;
4
+ exports.selectBestNonLiquidatedPoint = selectBestNonLiquidatedPoint;
5
+ exports.resolveIslandRiskLevel = resolveIslandRiskLevel;
6
+ exports.analyzeSweepIsland = analyzeSweepIsland;
7
+ function extractSweepMetrics(source) {
8
+ return {
9
+ returnPct: source.returnPct,
10
+ maxDrawdownPct: source.maxDrawdownPct,
11
+ sharpeRatio: source.sharpeRatio,
12
+ winRatePct: source.winRatePct,
13
+ ...(source.maxLeverage === undefined
14
+ ? {}
15
+ : { maxLeverage: source.maxLeverage }),
16
+ liquidationCount: typeof source.liquidationCount === "number" &&
17
+ Number.isFinite(source.liquidationCount)
18
+ ? source.liquidationCount
19
+ : source.liquidated
20
+ ? 1
21
+ : 0,
22
+ netPnl: source.netPnl,
23
+ finalEquity: source.finalEquity,
24
+ tradeCount: source.tradeCount,
25
+ };
26
+ }
27
+ function selectBestNonLiquidatedPoint(points) {
28
+ const candidates = points.filter(isOkNonLiquidatedPoint);
29
+ if (candidates.length === 0) {
30
+ return null;
31
+ }
32
+ const best = candidates.reduce((leader, point) => point.metrics.returnPct > leader.metrics.returnPct ? point : leader);
33
+ return {
34
+ coordinate: best.coordinate,
35
+ returnPct: best.metrics.returnPct,
36
+ };
37
+ }
38
+ function resolveIslandRiskLevel(score) {
39
+ if (score >= 80) {
40
+ return "high";
41
+ }
42
+ if (score >= 50) {
43
+ return "medium";
44
+ }
45
+ if (score >= 30) {
46
+ return "low_medium";
47
+ }
48
+ return "low";
49
+ }
50
+ /**
51
+ * Score each swept point by how sharply it deviates from its neighbors, then
52
+ * flag the point closest to `centerValue` when it sits on a fragile island.
53
+ */
54
+ function analyzeSweepIsland(points, centerValue) {
55
+ const rows = [...points].sort((left, right) => left.value - right.value);
56
+ if (rows.length === 0) {
57
+ return {
58
+ points: [],
59
+ warning: null,
60
+ overallScore: 0,
61
+ overallRiskLevel: "low",
62
+ };
63
+ }
64
+ const returnStd = populationStd(rows.map((row) => row.returnPct));
65
+ const drawdownStd = populationStd(rows.map((row) => row.maxDrawdownPct));
66
+ const centerIndex = rows.reduce((bestIndex, row, index) => Math.abs(row.value - centerValue) <
67
+ Math.abs(rows[bestIndex].value - centerValue)
68
+ ? index
69
+ : bestIndex, 0);
70
+ const scored = rows.map((row, index) => {
71
+ const neighbors = [index - 1, index + 1]
72
+ .filter((i) => i >= 0 && i < rows.length)
73
+ .map((i) => rows[i]);
74
+ const score = scoreNeighborhood(row, neighbors, returnStd, drawdownStd);
75
+ return {
76
+ ...row,
77
+ isCenter: index === centerIndex,
78
+ islandScore: Number(score.toFixed(2)),
79
+ riskLevel: resolveIslandRiskLevel(score),
80
+ };
81
+ });
82
+ const center = scored[centerIndex];
83
+ const centerNeighbors = [centerIndex - 1, centerIndex + 1]
84
+ .filter((i) => i >= 0 && i < scored.length)
85
+ .map((i) => scored[i]);
86
+ const hasLiquidationJump = center.liquidationCount === 0 &&
87
+ centerNeighbors.some((row) => row.liquidationCount > 0);
88
+ const warning = hasLiquidationJump || center.islandScore >= 50
89
+ ? {
90
+ centerValue: center.value,
91
+ islandScore: center.islandScore,
92
+ riskLevel: center.riskLevel,
93
+ counterexamples: centerNeighbors
94
+ .filter((row) => row.liquidationCount > center.liquidationCount)
95
+ .map((row) => ({
96
+ counterValue: row.value,
97
+ centerReturnPct: center.returnPct,
98
+ counterReturnPct: row.returnPct,
99
+ centerLiquidationCount: center.liquidationCount,
100
+ counterLiquidationCount: row.liquidationCount,
101
+ })),
102
+ }
103
+ : null;
104
+ const overallScore = scored.reduce((highest, row) => Math.max(highest, row.islandScore), 0);
105
+ return {
106
+ points: scored,
107
+ warning,
108
+ overallScore,
109
+ overallRiskLevel: resolveIslandRiskLevel(overallScore),
110
+ };
111
+ }
112
+ function isOkNonLiquidatedPoint(point) {
113
+ return (point.status === "ok" &&
114
+ point.metrics !== undefined &&
115
+ Number.isFinite(point.metrics.returnPct) &&
116
+ Number.isFinite(point.metrics.liquidationCount) &&
117
+ point.metrics.liquidationCount === 0);
118
+ }
119
+ function scoreNeighborhood(row, neighbors, returnStd, drawdownStd) {
120
+ if (neighbors.length === 0) {
121
+ return 0;
122
+ }
123
+ const neighborReturn = average(neighbors.map((n) => n.returnPct));
124
+ const neighborDrawdown = average(neighbors.map((n) => n.maxDrawdownPct));
125
+ const neighborLiquidations = average(neighbors.map((n) => n.liquidationCount));
126
+ const returnAdvantage = (Math.min(Math.max(row.returnPct - neighborReturn, 0) / Math.max(returnStd, 0.5), 2) /
127
+ 2) *
128
+ 100;
129
+ const drawdownAdvantage = (Math.min(Math.max(neighborDrawdown - row.maxDrawdownPct, 0) /
130
+ Math.max(drawdownStd, 0.1), 2) /
131
+ 2) *
132
+ 100;
133
+ const liquidationJump = (Math.min(Math.max(neighborLiquidations - row.liquidationCount, 0), 3) /
134
+ 3) *
135
+ 100;
136
+ return (0.35 * returnAdvantage + 0.3 * drawdownAdvantage + 0.25 * liquidationJump);
137
+ }
138
+ function average(values) {
139
+ if (values.length === 0) {
140
+ return 0;
141
+ }
142
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
143
+ }
144
+ function populationStd(values) {
145
+ if (values.length < 2) {
146
+ return 0;
147
+ }
148
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
149
+ return Math.sqrt(values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length);
150
+ }
@@ -0,0 +1,55 @@
1
+ export type SweepExecutionState = {
2
+ readonly status: "idle";
3
+ } | {
4
+ readonly status: "running";
5
+ readonly total: number;
6
+ } | {
7
+ readonly status: "succeeded";
8
+ readonly resultId: string;
9
+ } | {
10
+ readonly status: "failed";
11
+ readonly message: string;
12
+ } | {
13
+ readonly status: "cancelled";
14
+ };
15
+ export type SweepCloudSaveState = {
16
+ readonly status: "idle";
17
+ } | {
18
+ readonly status: "saving";
19
+ } | {
20
+ readonly status: "saved";
21
+ readonly sweepId: string;
22
+ } | {
23
+ readonly status: "failed";
24
+ readonly message: string;
25
+ };
26
+ export interface SweepSessionState {
27
+ readonly execution: SweepExecutionState;
28
+ readonly cloudSave: SweepCloudSaveState;
29
+ }
30
+ export type SweepSessionEvent = {
31
+ readonly type: "execution-started";
32
+ readonly total: number;
33
+ } | {
34
+ readonly type: "execution-succeeded";
35
+ readonly resultId: string;
36
+ } | {
37
+ readonly type: "execution-failed";
38
+ readonly message: string;
39
+ } | {
40
+ readonly type: "execution-cancelled";
41
+ } | {
42
+ readonly type: "execution-reset";
43
+ } | {
44
+ readonly type: "cloud-save-started";
45
+ } | {
46
+ readonly type: "cloud-save-succeeded";
47
+ readonly sweepId: string;
48
+ } | {
49
+ readonly type: "cloud-save-failed";
50
+ readonly message: string;
51
+ } | {
52
+ readonly type: "cloud-save-retry";
53
+ };
54
+ export declare function createSweepSessionState(): SweepSessionState;
55
+ export declare function reduceSweepSession(state: SweepSessionState, event: SweepSessionEvent): SweepSessionState;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSweepSessionState = createSweepSessionState;
4
+ exports.reduceSweepSession = reduceSweepSession;
5
+ const IDLE_CLOUD_SAVE = { status: "idle" };
6
+ function createSweepSessionState() {
7
+ return {
8
+ execution: { status: "idle" },
9
+ cloudSave: IDLE_CLOUD_SAVE,
10
+ };
11
+ }
12
+ function reduceSweepSession(state, event) {
13
+ switch (event.type) {
14
+ case "execution-started":
15
+ return {
16
+ execution: { status: "running", total: event.total },
17
+ cloudSave: IDLE_CLOUD_SAVE,
18
+ };
19
+ case "execution-succeeded":
20
+ return {
21
+ execution: { status: "succeeded", resultId: event.resultId },
22
+ cloudSave: IDLE_CLOUD_SAVE,
23
+ };
24
+ case "execution-failed":
25
+ return {
26
+ execution: { status: "failed", message: event.message },
27
+ cloudSave: IDLE_CLOUD_SAVE,
28
+ };
29
+ case "execution-cancelled":
30
+ return {
31
+ execution: { status: "cancelled" },
32
+ cloudSave: IDLE_CLOUD_SAVE,
33
+ };
34
+ case "execution-reset":
35
+ return createSweepSessionState();
36
+ case "cloud-save-started":
37
+ case "cloud-save-retry":
38
+ return applyCloudSave(state, { status: "saving" });
39
+ case "cloud-save-succeeded":
40
+ return applyCloudSave(state, {
41
+ status: "saved",
42
+ sweepId: event.sweepId,
43
+ });
44
+ case "cloud-save-failed":
45
+ return applyCloudSave(state, {
46
+ status: "failed",
47
+ message: event.message,
48
+ });
49
+ }
50
+ }
51
+ function applyCloudSave(state, cloudSave) {
52
+ if (state.execution.status !== "succeeded") {
53
+ return state;
54
+ }
55
+ return { ...state, cloudSave };
56
+ }
@@ -0,0 +1,36 @@
1
+ export type SweepSubscriptionTier = "free" | "pro" | "pro_max";
2
+ export type SweepSearchMode = "standard" | "fast";
3
+ export interface SweepCoordinate {
4
+ readonly values: readonly number[];
5
+ }
6
+ export interface SweepAxisWindow {
7
+ readonly min: number;
8
+ readonly max: number;
9
+ readonly step: number;
10
+ }
11
+ /** Explicit values, or a min/max/step window around the current value. */
12
+ export interface SweepAxisInput {
13
+ readonly path: readonly string[];
14
+ readonly current: number;
15
+ readonly isInteger: boolean;
16
+ readonly min?: number;
17
+ readonly max?: number;
18
+ readonly minExclusive?: boolean;
19
+ readonly maxExclusive?: boolean;
20
+ readonly values?: readonly number[];
21
+ readonly window?: SweepAxisWindow;
22
+ }
23
+ export interface SweepPlanAxis {
24
+ readonly path: readonly string[];
25
+ readonly current: number;
26
+ readonly values: readonly number[];
27
+ }
28
+ export interface SweepPlan {
29
+ readonly axes: readonly SweepPlanAxis[];
30
+ readonly coordinates: readonly SweepCoordinate[];
31
+ readonly requestedCombinationCount: number;
32
+ readonly sampled: boolean;
33
+ }
34
+ export type SweepConfigRecord = {
35
+ readonly [key: string]: unknown;
36
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -34,6 +34,14 @@ export interface EngineBacktestRunArgs {
34
34
  /** Replay/download bar. Defaults to 1m; never finer. */
35
35
  readonly replayTimeframe: string;
36
36
  }
37
+ export type SweepMode = "neighborhood" | "range";
38
+ export type SweepSearchMode = "standard" | "fast";
39
+ export interface EngineBacktestSweepArgs extends EngineBacktestRunArgs {
40
+ readonly axesRaw?: string;
41
+ readonly mode: SweepMode;
42
+ readonly searchMode: SweepSearchMode;
43
+ readonly concurrency: number;
44
+ }
37
45
  export interface EngineBacktestSeriesRequirement {
38
46
  readonly symbol: string;
39
47
  readonly timeframe: string;
@@ -105,6 +113,37 @@ export interface EngineBacktestResult {
105
113
  }>;
106
114
  readonly warnings?: string[];
107
115
  }
116
+ export interface EngineBacktestBatchVariant {
117
+ readonly runId: string;
118
+ readonly config: unknown;
119
+ }
120
+ export interface EngineBacktestBatchRequest {
121
+ readonly version: 1;
122
+ readonly batchId: string;
123
+ readonly baseScenario: Omit<EngineBacktestScenario, "tape">;
124
+ readonly variants: readonly EngineBacktestBatchVariant[];
125
+ readonly tape: EngineBacktestTapeInput;
126
+ }
127
+ export interface EngineBacktestBatchPointResult {
128
+ readonly runId: string;
129
+ readonly status: "completed" | "failed";
130
+ readonly metrics: EngineBacktestMetrics;
131
+ readonly errors?: Array<{
132
+ code: string;
133
+ message: string;
134
+ path?: string;
135
+ }>;
136
+ }
137
+ export interface EngineBacktestBatchResult {
138
+ readonly batchId: string;
139
+ readonly status: "completed" | "failed";
140
+ readonly results: readonly EngineBacktestBatchPointResult[];
141
+ readonly errors?: Array<{
142
+ code: string;
143
+ message: string;
144
+ path?: string;
145
+ }>;
146
+ }
108
147
  export interface EngineBacktestSupportReason {
109
148
  readonly code: string;
110
149
  readonly message: string;
@@ -180,6 +219,7 @@ export interface BacktestClientLike {
180
219
  readonly config: unknown;
181
220
  }): Promise<EngineBacktestPlan | EngineBacktestFailure>;
182
221
  runBacktest(scenario: EngineBacktestScenario, buffers: Record<string, ArrayBuffer>, onProgress?: (fraction: number) => void): Promise<EngineBacktestResult>;
222
+ runBacktestBatch(batch: EngineBacktestBatchRequest, buffers: Record<string, ArrayBuffer>, onProgress?: (fraction: number) => void): Promise<EngineBacktestBatchResult>;
183
223
  terminate(): void;
184
224
  }
185
225
  export interface BacktestWasmModule {
@@ -237,6 +277,8 @@ export interface CreateRunRequestBody {
237
277
  readonly metrics: EngineBacktestMetrics;
238
278
  readonly engineVersion: string;
239
279
  readonly configSchemaVersion: number;
280
+ /** Compact [[unix_ms, cumulative_return]]; omitted when the local run has no series. */
281
+ readonly returnCurve?: ReadonlyArray<readonly [number, number]>;
240
282
  }
241
283
  export interface EngineBacktestRunSuccess {
242
284
  readonly metrics: EngineBacktestMetrics;
@@ -247,3 +289,91 @@ export interface EngineBacktestRunSuccess {
247
289
  readonly persisted: boolean;
248
290
  readonly coverageWarnings: readonly string[];
249
291
  }
292
+ export interface EngineBacktestSweepPointRow {
293
+ readonly coordinate: {
294
+ readonly values: readonly number[];
295
+ };
296
+ readonly status: "ok" | "failed";
297
+ readonly metrics?: {
298
+ readonly returnPct: number;
299
+ readonly maxDrawdownPct: number;
300
+ readonly sharpeRatio: number;
301
+ readonly winRatePct: number;
302
+ readonly maxLeverage?: number;
303
+ readonly liquidationCount: number;
304
+ readonly netPnl: number;
305
+ readonly finalEquity: number;
306
+ readonly tradeCount: number;
307
+ };
308
+ readonly error?: string;
309
+ }
310
+ export interface CreateSweepPointSummary {
311
+ readonly coordinate: {
312
+ readonly values: readonly number[];
313
+ };
314
+ readonly status: "ok" | "failed";
315
+ readonly error?: string;
316
+ readonly metrics?: EngineBacktestSweepPointRow["metrics"];
317
+ }
318
+ export interface CreateSweepRequestBody {
319
+ readonly clientSweepId: string;
320
+ readonly baseInputSnapshot: PersistedSnapshot;
321
+ readonly axes: ReadonlyArray<{
322
+ readonly path: readonly string[];
323
+ readonly current: number;
324
+ readonly values: readonly number[];
325
+ }>;
326
+ readonly searchMetadata: {
327
+ readonly mode: SweepMode;
328
+ readonly searchMode: SweepSearchMode;
329
+ readonly concurrency: number;
330
+ readonly requestedCombinationCount: number;
331
+ readonly sampled: boolean;
332
+ };
333
+ readonly aggregateSummary: {
334
+ readonly successfulCount: number;
335
+ readonly failedCount: number;
336
+ readonly liquidatedCount: number;
337
+ readonly elapsedMs: number;
338
+ readonly best: {
339
+ readonly coordinate: {
340
+ readonly values: readonly number[];
341
+ };
342
+ readonly returnPct: number;
343
+ } | null;
344
+ };
345
+ readonly pointSummaries: readonly CreateSweepPointSummary[];
346
+ readonly engineVersion: string;
347
+ readonly configSchemaVersion: number;
348
+ }
349
+ export interface EngineBacktestSweepSuccess {
350
+ readonly persisted: boolean;
351
+ readonly sweepId?: string;
352
+ readonly clientSweepId?: string;
353
+ readonly mode: SweepMode;
354
+ readonly searchMode: SweepSearchMode;
355
+ readonly requestedCombinationCount: number;
356
+ readonly sampled: boolean;
357
+ readonly combinationCount: number;
358
+ readonly successfulCount: number;
359
+ readonly failedCount: number;
360
+ readonly liquidatedCount: number;
361
+ readonly elapsedMs: number;
362
+ readonly best: {
363
+ readonly coordinate: {
364
+ readonly values: readonly number[];
365
+ };
366
+ readonly returnPct: number;
367
+ readonly config: unknown;
368
+ } | null;
369
+ readonly points: readonly EngineBacktestSweepPointRow[];
370
+ readonly engineVersion: string;
371
+ readonly experimentId?: string;
372
+ readonly experimentUrl?: string;
373
+ readonly coverageWarnings: readonly string[];
374
+ readonly axes: ReadonlyArray<{
375
+ readonly path: readonly string[];
376
+ readonly current: number;
377
+ readonly values: readonly number[];
378
+ }>;
379
+ }
@@ -50,7 +50,11 @@ async function confirmTty(message) {
50
50
  try {
51
51
  const answer = await rl.question(`${message} [Y/n] `);
52
52
  const token = answer.trim().toLowerCase();
53
- return token === "" || token === "y" || token === "yes";
53
+ return (token === "" ||
54
+ token === "y" ||
55
+ token === "yes" ||
56
+ token === "是" ||
57
+ token === "好");
54
58
  }
55
59
  finally {
56
60
  rl.close();
@@ -49,24 +49,21 @@ function parseNpmListVersion(output) {
49
49
  return match?.[1] ?? null;
50
50
  }
51
51
  function skillsListHasAlphafox(output) {
52
- const prefix = types_1.SKILLS_NAME_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
- return new RegExp(`(^|\\s)${prefix}[\\w-]+`, "m").test(output);
52
+ return /(^|\s)alphafox(?:-[\w-]+)?(?=\s|$)/m.test(output);
54
53
  }
55
54
  function nextSteps(input) {
56
- const steps = [
57
- "Restart the AI tool so newly installed Skills are loaded.",
58
- ];
55
+ const steps = ["请重启 AI 工具,以便加载刚安装的 Skills。"];
59
56
  if (input.auth.action === "skipped" || input.auth.action === "planned") {
60
57
  steps.push("alphafox auth login --browser --format json --no-input", "alphafox auth login --no-wait --format json --no-input");
61
58
  }
62
- steps.push("alphafox doctor --format json --no-input", `Agent install guide: ${types_1.AGENT_INSTALL_GUIDE_BLOB_URL}`);
59
+ steps.push("alphafox doctor --format json --no-input", `Agent 安装指南:${types_1.AGENT_INSTALL_GUIDE_BLOB_URL}`);
63
60
  if (input.dryRun) {
64
- steps.unshift("This was --dry-run; re-run without it to apply changes.");
61
+ steps.unshift("这是 --dry-run,去掉该参数再运行才会真正安装。");
65
62
  }
66
63
  return steps;
67
64
  }
68
65
  async function runInstallWizard(flags, env = process.env, runner = (0, exec_1.createDefaultInstallRunner)(env, defaultSearchDirs())) {
69
- runner.log("Setting up Alphafox CLI...");
66
+ runner.log("正在安装 Alphafox CLI");
70
67
  const installedVer = await readGloballyInstalledVersion(runner);
71
68
  const latestVer = await readLatestVersion(runner);
72
69
  const needsUpgrade = Boolean(installedVer) &&
@@ -140,7 +137,7 @@ async function npmPrefixGlobal(runner) {
140
137
  async function stepInstallCli(flags, runner, versions) {
141
138
  const { installedVer, latestVer, needsUpgrade } = versions;
142
139
  if (installedVer && !needsUpgrade) {
143
- runner.log(`Already installed (${installedVer}). Skipped`);
140
+ runner.log(`已安装(${installedVer}),已跳过`);
144
141
  return {
145
142
  action: flags.dryRun ? "planned" : "skipped",
146
143
  version: installedVer,
@@ -149,8 +146,8 @@ async function stepInstallCli(flags, runner, versions) {
149
146
  }
150
147
  if (flags.dryRun) {
151
148
  runner.log(needsUpgrade
152
- ? `Would upgrade ${version_1.CLI_PACKAGE} (${installedVer} → ${latestVer ?? "latest"})`
153
- : `Would install ${version_1.CLI_PACKAGE} globally`);
149
+ ? `将升级 ${version_1.CLI_PACKAGE}(${installedVer} → ${latestVer ?? "latest"})`
150
+ : `将全局安装 ${version_1.CLI_PACKAGE}`);
154
151
  return {
155
152
  action: "planned",
156
153
  previousVersion: installedVer ?? undefined,
@@ -158,8 +155,8 @@ async function stepInstallCli(flags, runner, versions) {
158
155
  };
159
156
  }
160
157
  runner.log(needsUpgrade
161
- ? `Upgrading ${version_1.CLI_PACKAGE} (${installedVer} → ${latestVer})...`
162
- : `Installing ${version_1.CLI_PACKAGE} globally...`);
158
+ ? `正在升级 ${version_1.CLI_PACKAGE}(${installedVer} → ${latestVer})…`
159
+ : `正在全局安装 ${version_1.CLI_PACKAGE}…`);
163
160
  try {
164
161
  await runner.exec("npm", ["install", "-g", version_1.CLI_PACKAGE], {
165
162
  timeoutMs: NPM_TIMEOUT_MS,
@@ -169,13 +166,13 @@ async function stepInstallCli(flags, runner, versions) {
169
166
  throw new types_1.InstallError({
170
167
  type: "install",
171
168
  subtype: "npm_global_failed",
172
- message: `Failed to install ${version_1.CLI_PACKAGE} globally.`,
169
+ message: `全局安装 ${version_1.CLI_PACKAGE} 失败。`,
173
170
  hint: `npm install -g ${version_1.CLI_PACKAGE}`,
174
171
  details: err instanceof Error ? err.message : String(err),
175
172
  });
176
173
  }
177
174
  const after = (await readGloballyInstalledVersion(runner)) ?? latestVer ?? version_1.CLI_VERSION;
178
- runner.log(needsUpgrade ? `Upgraded to ${after}` : "Installed globally");
175
+ runner.log(needsUpgrade ? `已升级到 ${after}` : "已全局安装");
179
176
  return {
180
177
  action: needsUpgrade ? "upgraded" : "installed",
181
178
  version: after,
@@ -214,7 +211,7 @@ async function stepInstallSkills(flags, runner) {
214
211
  const sources = await resolveSkillsSources(runner);
215
212
  const already = flags.dryRun ? false : await skillsAlreadyInstalled(runner);
216
213
  if (already) {
217
- runner.log("Skills already installed. Skipped");
214
+ runner.log("Skills 已安装,已跳过");
218
215
  return {
219
216
  action: "skipped",
220
217
  source: sources[0],
@@ -223,19 +220,19 @@ async function stepInstallSkills(flags, runner) {
223
220
  };
224
221
  }
225
222
  if (flags.dryRun) {
226
- runner.log(`Would install Skills from ${sources[0] ?? types_1.SKILLS_GITHUB_SOURCE}`);
223
+ runner.log(`将从 ${sources[0] ?? types_1.SKILLS_GITHUB_SOURCE} 安装 Skills`);
227
224
  return {
228
225
  action: "planned",
229
226
  source: sources[0] ?? types_1.SKILLS_GITHUB_SOURCE,
230
227
  scope: "global",
231
228
  };
232
229
  }
233
- runner.log("Installing AI Skills...");
230
+ runner.log("正在安装 AI Skills");
234
231
  let lastError;
235
232
  for (const source of sources) {
236
233
  try {
237
234
  await runner.exec("npx", ["-y", "skills", "add", source, "-y", "-g"], { timeoutMs: SKILLS_TIMEOUT_MS });
238
- runner.log(`Skills installed (${source})`);
235
+ runner.log(`Skills 已安装(${source})`);
239
236
  return { action: "installed", source, scope: "global" };
240
237
  }
241
238
  catch (err) {
@@ -245,7 +242,7 @@ async function stepInstallSkills(flags, runner) {
245
242
  throw new types_1.InstallError({
246
243
  type: "install",
247
244
  subtype: "skills_add_failed",
248
- message: "Failed to install Agent Skills.",
245
+ message: "安装 Agent Skills 失败。",
249
246
  hint: `npx skills add ${types_1.SKILLS_GITHUB_SOURCE} -y -g`,
250
247
  details: lastError instanceof Error ? lastError.message : String(lastError),
251
248
  });
@@ -274,16 +271,16 @@ async function stepAuth(flags, runner) {
274
271
  if (flags.dryRun) {
275
272
  return { action: "planned", reason: "non-interactive" };
276
273
  }
277
- runner.log("To finish setup, run:\n alphafox auth login --browser\n alphafox auth login --no-wait --format json --no-input");
274
+ runner.log("要完成安装,请运行:\n alphafox auth login --browser\n alphafox auth login --no-wait --format json --no-input");
278
275
  return { action: "skipped", reason: "non-interactive" };
279
276
  }
280
277
  if (flags.dryRun) {
281
- runner.log("Would prompt for alphafox auth login --browser");
278
+ runner.log("将提示运行 alphafox auth login --browser");
282
279
  return { action: "planned", reason: "tty" };
283
280
  }
284
- const yes = await runner.confirm("Log in now so Agents can call the public Application API?");
281
+ const yes = await runner.confirm("现在登录,以便 Agent 可以调用公开 Application API");
285
282
  if (!yes) {
286
- runner.log("Skipped login. Run alphafox auth login later.");
283
+ runner.log("已跳过登录。之后可运行 alphafox auth login");
287
284
  return { action: "skipped", reason: "declined" };
288
285
  }
289
286
  const bin = await resolveAlphafoxBin(runner);
@@ -291,19 +288,19 @@ async function stepAuth(flags, runner) {
291
288
  throw new types_1.InstallError({
292
289
  type: "install",
293
290
  subtype: "cli_bin_missing",
294
- message: "alphafox binary not found after install.",
295
- hint: "Ensure the npm global bin directory is on PATH, then run alphafox auth login --browser.",
291
+ message: "安装完成后找不到 alphafox 可执行文件。",
292
+ hint: "请确认 npm 全局 bin 目录在 PATH 中,然后运行 alphafox auth login --browser",
296
293
  });
297
294
  }
298
295
  try {
299
296
  await runner.execInherit(bin, ["auth", "login", "--browser"], {
300
297
  timeoutMs: 10 * 60_000,
301
298
  });
302
- runner.log("Login complete");
299
+ runner.log("登录完成");
303
300
  return { action: "completed" };
304
301
  }
305
302
  catch (err) {
306
- runner.log("Login failed. Run alphafox auth login --browser to retry.");
303
+ runner.log("登录失败。请运行 alphafox auth login --browser 重试。");
307
304
  return {
308
305
  action: "failed",
309
306
  reason: err instanceof Error ? err.message : String(err),
@@ -319,7 +316,7 @@ function installHelpData() {
319
316
  "alphafox install --no-auth",
320
317
  "alphafox install --dry-run",
321
318
  ],
322
- description: "Install @alphafox/cli globally and copy co-versioned Agent Skills into detected agents (Cursor, Claude Code, Codex, …) via npx skills add.",
319
+ description: "全局安装 @alphafox/cli,并通过 npx skills add 把同版本 Agent Skills 写入本机已检测到的 Agent(CursorClaude CodeCodex 等)的用户级目录。",
323
320
  agentGuide: types_1.AGENT_INSTALL_GUIDE_BLOB_URL,
324
321
  };
325
322
  }
@@ -0,0 +1,3 @@
1
+ export declare const MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
2
+ export declare function marketSymbolsPath(exchangeId: string): string;
3
+ export declare function extractCatalogSymbols(json: unknown): string[];