@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,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MARKET_SYMBOLS_PATH = void 0;
4
+ exports.marketSymbolsPath = marketSymbolsPath;
5
+ exports.extractCatalogSymbols = extractCatalogSymbols;
6
+ const errors_1 = require("./errors");
7
+ exports.MARKET_SYMBOLS_PATH = "/api/v1/market/symbols";
8
+ function marketSymbolsPath(exchangeId) {
9
+ const query = new URLSearchParams({ exchange: exchangeId }).toString();
10
+ return `${exports.MARKET_SYMBOLS_PATH}?${query}`;
11
+ }
12
+ function extractCatalogSymbols(json) {
13
+ const root = asRecord(json);
14
+ const payload = asRecord(root?.data) ?? root;
15
+ const symbols = payload?.symbols;
16
+ if (!Array.isArray(symbols)) {
17
+ throw new errors_1.ResolveSymbolsError({
18
+ type: "runtime",
19
+ subtype: "catalog_invalid",
20
+ message: "market.symbols.list response did not include a symbols array",
21
+ details: json,
22
+ });
23
+ }
24
+ const out = [];
25
+ const seen = new Set();
26
+ for (const item of symbols) {
27
+ if (typeof item !== "string")
28
+ continue;
29
+ const trimmed = item.trim();
30
+ if (!trimmed || seen.has(trimmed))
31
+ continue;
32
+ seen.add(trimmed);
33
+ out.push(trimmed);
34
+ }
35
+ if (out.length === 0) {
36
+ throw new errors_1.ResolveSymbolsError({
37
+ type: "runtime",
38
+ subtype: "catalog_empty",
39
+ message: "market.symbols.list returned no contract symbols",
40
+ details: json,
41
+ });
42
+ }
43
+ return out;
44
+ }
45
+ function asRecord(value) {
46
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
47
+ return undefined;
48
+ }
49
+ return value;
50
+ }
@@ -0,0 +1,18 @@
1
+ export declare class ResolveSymbolsError extends Error {
2
+ readonly type: string;
3
+ readonly subtype?: string;
4
+ readonly status?: number;
5
+ readonly code?: string | number;
6
+ readonly hint?: string;
7
+ readonly details?: unknown;
8
+ constructor(input: {
9
+ readonly message: string;
10
+ readonly type?: string;
11
+ readonly subtype?: string;
12
+ readonly status?: number;
13
+ readonly code?: string | number;
14
+ readonly hint?: string;
15
+ readonly details?: unknown;
16
+ });
17
+ }
18
+ export declare function isResolveSymbolsError(value: unknown): value is ResolveSymbolsError;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResolveSymbolsError = void 0;
4
+ exports.isResolveSymbolsError = isResolveSymbolsError;
5
+ class ResolveSymbolsError extends Error {
6
+ type;
7
+ subtype;
8
+ status;
9
+ code;
10
+ hint;
11
+ details;
12
+ constructor(input) {
13
+ super(input.message);
14
+ this.name = "ResolveSymbolsError";
15
+ this.type = input.type ?? "runtime";
16
+ this.subtype = input.subtype;
17
+ this.status = input.status;
18
+ this.code = input.code;
19
+ this.hint = input.hint;
20
+ this.details = input.details;
21
+ }
22
+ }
23
+ exports.ResolveSymbolsError = ResolveSymbolsError;
24
+ function isResolveSymbolsError(value) {
25
+ return value instanceof ResolveSymbolsError;
26
+ }
@@ -0,0 +1,8 @@
1
+ export declare const RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = "binance_perp_usdt";
2
+ export interface ResolveSymbolsExchange {
3
+ readonly id: string;
4
+ readonly label: string;
5
+ readonly aliases: readonly string[];
6
+ }
7
+ export declare const RESOLVE_SYMBOLS_EXCHANGES: readonly ResolveSymbolsExchange[];
8
+ export declare function resolveSymbolsExchangeId(raw: string): ResolveSymbolsExchange;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESOLVE_SYMBOLS_EXCHANGES = exports.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = void 0;
4
+ exports.resolveSymbolsExchangeId = resolveSymbolsExchangeId;
5
+ exports.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE = "binance_perp_usdt";
6
+ exports.RESOLVE_SYMBOLS_EXCHANGES = [
7
+ {
8
+ id: "binance_perp_usdt",
9
+ label: "Binance",
10
+ aliases: ["binance", "binanceusdm", "binance_perp_usdt"],
11
+ },
12
+ {
13
+ id: "okx_perp_usdt",
14
+ label: "OKX",
15
+ aliases: ["okx", "okx_perp_usdt"],
16
+ },
17
+ {
18
+ id: "bybit_perp_usdt",
19
+ label: "Bybit",
20
+ aliases: ["bybit", "bybit_perp_usdt"],
21
+ },
22
+ {
23
+ id: "bitget_perp_usdt",
24
+ label: "Bitget",
25
+ aliases: ["bitget", "bitget_perp_usdt"],
26
+ },
27
+ {
28
+ id: "hyperliquid_perp_usdc",
29
+ label: "HyperLiquid",
30
+ aliases: ["hyperliquid", "hyperliquid_perp_usdc"],
31
+ },
32
+ {
33
+ id: "aster_perp_usdt",
34
+ label: "Aster",
35
+ aliases: ["aster", "aster_perp_usdt"],
36
+ },
37
+ ];
38
+ const EXCHANGE_BY_ALIAS = new Map();
39
+ for (const exchange of exports.RESOLVE_SYMBOLS_EXCHANGES) {
40
+ EXCHANGE_BY_ALIAS.set(exchange.id, exchange);
41
+ for (const alias of exchange.aliases) {
42
+ EXCHANGE_BY_ALIAS.set(alias.toLowerCase(), exchange);
43
+ }
44
+ }
45
+ function resolveSymbolsExchangeId(raw) {
46
+ const key = raw.trim().toLowerCase();
47
+ const exchange = EXCHANGE_BY_ALIAS.get(key);
48
+ if (!exchange) {
49
+ const allowed = exports.RESOLVE_SYMBOLS_EXCHANGES.map((item) => item.aliases[0]).join("|");
50
+ throw new Error(`--exchange must be ${allowed} (got ${raw.trim() || "<empty>"})`);
51
+ }
52
+ return exchange;
53
+ }
@@ -0,0 +1,6 @@
1
+ import type { CatalogSymbol, ResolveSymbolsQueryResult } from "./types";
2
+ export declare function normalizeSymbolSearchKey(value: string): string;
3
+ export declare function parseCatalogSymbol(symbol: string): CatalogSymbol | null;
4
+ export declare function indexCatalogSymbols(symbols: readonly string[]): readonly CatalogSymbol[];
5
+ export declare function resolveQueryAgainstCatalog(query: string, catalog: readonly CatalogSymbol[], limit?: number): ResolveSymbolsQueryResult;
6
+ export declare function levenshtein(left: string, right: string): number;
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSymbolSearchKey = normalizeSymbolSearchKey;
4
+ exports.parseCatalogSymbol = parseCatalogSymbol;
5
+ exports.indexCatalogSymbols = indexCatalogSymbols;
6
+ exports.resolveQueryAgainstCatalog = resolveQueryAgainstCatalog;
7
+ exports.levenshtein = levenshtein;
8
+ const LINEAR_PERP_PATTERN = /^([\p{L}\p{N}]+(?:-[\p{L}\p{N}]+)*)\/(USDT|USDC|USD):\2$/u;
9
+ const QUOTE_ASSETS = ["USDT", "USDC", "BUSD", "USD"];
10
+ const EXACT_REASONS = new Set([
11
+ "exact_canonical",
12
+ "exact_compact",
13
+ "exact_pair",
14
+ "exact_base",
15
+ ]);
16
+ function normalizeSymbolSearchKey(value) {
17
+ return value
18
+ .trim()
19
+ .toUpperCase()
20
+ .replace(/[^\p{L}\p{N}]/gu, "");
21
+ }
22
+ function parseCatalogSymbol(symbol) {
23
+ const trimmed = symbol.trim();
24
+ if (!trimmed)
25
+ return null;
26
+ const upper = trimmed.toUpperCase();
27
+ const linear = LINEAR_PERP_PATTERN.exec(upper);
28
+ if (linear) {
29
+ const base = linear[1];
30
+ const quote = linear[2];
31
+ const canonical = `${base}/${quote}:${quote}`;
32
+ const compact = `${base}${quote}`;
33
+ return {
34
+ symbol: canonical,
35
+ base,
36
+ quote,
37
+ pair: `${base}/${quote}`,
38
+ compact,
39
+ searchKey: normalizeSymbolSearchKey(canonical),
40
+ baseKey: normalizeSymbolSearchKey(base),
41
+ compactKey: normalizeSymbolSearchKey(compact),
42
+ };
43
+ }
44
+ const slash = upper.indexOf("/");
45
+ const colon = upper.lastIndexOf(":");
46
+ if (slash > 0) {
47
+ const base = upper.slice(0, slash);
48
+ const quote = colon > slash ? upper.slice(slash + 1, colon) : upper.slice(slash + 1);
49
+ const compact = `${base}${quote}`;
50
+ return {
51
+ symbol: upper,
52
+ base,
53
+ quote,
54
+ pair: `${base}/${quote}`,
55
+ compact,
56
+ searchKey: normalizeSymbolSearchKey(upper),
57
+ baseKey: normalizeSymbolSearchKey(base),
58
+ compactKey: normalizeSymbolSearchKey(compact),
59
+ };
60
+ }
61
+ return {
62
+ symbol: upper,
63
+ base: upper,
64
+ quote: "",
65
+ pair: upper,
66
+ compact: upper,
67
+ searchKey: normalizeSymbolSearchKey(upper),
68
+ baseKey: normalizeSymbolSearchKey(upper),
69
+ compactKey: normalizeSymbolSearchKey(upper),
70
+ };
71
+ }
72
+ function indexCatalogSymbols(symbols) {
73
+ const seen = new Set();
74
+ const indexed = [];
75
+ for (const raw of symbols) {
76
+ const parsed = parseCatalogSymbol(raw);
77
+ if (!parsed)
78
+ continue;
79
+ if (seen.has(parsed.symbol))
80
+ continue;
81
+ seen.add(parsed.symbol);
82
+ indexed.push(parsed);
83
+ }
84
+ return indexed;
85
+ }
86
+ function resolveQueryAgainstCatalog(query, catalog, limit = 8) {
87
+ const trimmed = query.trim();
88
+ const matches = rankMatches(trimmed, catalog);
89
+ const exact = matches.filter((item) => EXACT_REASONS.has(item.reason));
90
+ const close = matches.filter((item) => !EXACT_REASONS.has(item.reason));
91
+ const capped = (items) => items.slice(0, Math.max(1, limit));
92
+ if (exact.length === 1) {
93
+ return {
94
+ query: trimmed,
95
+ status: "exact",
96
+ resolved: exact[0].symbol,
97
+ needsConfirmation: false,
98
+ matches: exact,
99
+ matchCount: exact.length,
100
+ };
101
+ }
102
+ if (exact.length > 1) {
103
+ return {
104
+ query: trimmed,
105
+ status: "ambiguous",
106
+ resolved: null,
107
+ needsConfirmation: true,
108
+ matches: capped(exact),
109
+ matchCount: exact.length,
110
+ };
111
+ }
112
+ if (close.length === 1) {
113
+ return {
114
+ query: trimmed,
115
+ status: "close",
116
+ resolved: close[0].symbol,
117
+ needsConfirmation: true,
118
+ matches: close,
119
+ matchCount: 1,
120
+ };
121
+ }
122
+ if (close.length > 1) {
123
+ return {
124
+ query: trimmed,
125
+ status: "ambiguous",
126
+ resolved: null,
127
+ needsConfirmation: true,
128
+ matches: capped(close),
129
+ matchCount: close.length,
130
+ };
131
+ }
132
+ return {
133
+ query: trimmed,
134
+ status: "none",
135
+ resolved: null,
136
+ needsConfirmation: false,
137
+ matches: [],
138
+ matchCount: 0,
139
+ };
140
+ }
141
+ function rankMatches(query, catalog) {
142
+ const canonicalQuery = query.trim().toUpperCase();
143
+ const queryKey = normalizeSymbolSearchKey(query);
144
+ if (!canonicalQuery || !queryKey)
145
+ return [];
146
+ if (isQuoteAssetKey(queryKey))
147
+ return [];
148
+ const queryBaseKey = queryBaseSearchKey(canonicalQuery, queryKey);
149
+ const scored = [];
150
+ for (const item of catalog) {
151
+ const ranked = rankCatalogSymbol(item, canonicalQuery, queryKey, queryBaseKey);
152
+ if (ranked)
153
+ scored.push(ranked);
154
+ }
155
+ scored.sort((left, right) => {
156
+ if (right.score !== left.score)
157
+ return right.score - left.score;
158
+ return left.symbol.localeCompare(right.symbol);
159
+ });
160
+ return scored;
161
+ }
162
+ function rankCatalogSymbol(item, canonicalQuery, queryKey, queryBaseKey) {
163
+ if (item.symbol === canonicalQuery) {
164
+ return match(item.symbol, "exact_canonical", 100);
165
+ }
166
+ if (item.pair === canonicalQuery) {
167
+ return match(item.symbol, "exact_pair", 96);
168
+ }
169
+ if (item.compactKey === queryKey || item.searchKey === queryKey) {
170
+ return match(item.symbol, "exact_compact", 98);
171
+ }
172
+ if (item.baseKey === queryKey || item.baseKey === queryBaseKey) {
173
+ return match(item.symbol, "exact_base", 95);
174
+ }
175
+ let best = null;
176
+ const consider = (reason, score) => {
177
+ if (!best || score > best.score) {
178
+ best = match(item.symbol, reason, score);
179
+ }
180
+ };
181
+ if (queryBaseKey.length >= 2 && item.baseKey.startsWith(queryBaseKey)) {
182
+ consider("prefix", clampScore(80 - (item.baseKey.length - queryBaseKey.length)));
183
+ }
184
+ if (queryBaseKey.length >= 3 && queryBaseKey.startsWith(item.baseKey)) {
185
+ consider("prefix", clampScore(74 - (queryBaseKey.length - item.baseKey.length)));
186
+ }
187
+ if (queryBaseKey.length >= 3 && item.baseKey.includes(queryBaseKey)) {
188
+ consider("contains", 55);
189
+ }
190
+ const distance = levenshtein(queryBaseKey, item.baseKey);
191
+ if (isCloseDistance(queryBaseKey, item.baseKey, distance)) {
192
+ consider("close", clampScore(48 - distance * 8));
193
+ }
194
+ return best;
195
+ }
196
+ function queryBaseSearchKey(canonicalQuery, queryKey) {
197
+ const linear = LINEAR_PERP_PATTERN.exec(canonicalQuery);
198
+ if (linear)
199
+ return normalizeSymbolSearchKey(linear[1]);
200
+ const slash = canonicalQuery.indexOf("/");
201
+ if (slash > 0) {
202
+ return normalizeSymbolSearchKey(canonicalQuery.slice(0, slash));
203
+ }
204
+ for (const quote of QUOTE_ASSETS) {
205
+ if (queryKey.length > quote.length && queryKey.endsWith(quote)) {
206
+ return queryKey.slice(0, -quote.length);
207
+ }
208
+ }
209
+ return queryKey;
210
+ }
211
+ function isQuoteAssetKey(queryKey) {
212
+ return QUOTE_ASSETS.includes(queryKey);
213
+ }
214
+ function isCloseDistance(left, right, distance) {
215
+ const shortest = Math.min(left.length, right.length);
216
+ if (shortest < 2 || distance <= 0)
217
+ return false;
218
+ if (shortest <= 4)
219
+ return distance === 1;
220
+ return distance <= 2;
221
+ }
222
+ function match(symbol, reason, score) {
223
+ return { symbol, reason, score };
224
+ }
225
+ function clampScore(score) {
226
+ return Math.max(1, Math.min(94, score));
227
+ }
228
+ function levenshtein(left, right) {
229
+ if (left === right)
230
+ return 0;
231
+ if (left.length === 0)
232
+ return right.length;
233
+ if (right.length === 0)
234
+ return left.length;
235
+ const prev = new Array(right.length + 1);
236
+ const next = new Array(right.length + 1);
237
+ for (let j = 0; j <= right.length; j += 1)
238
+ prev[j] = j;
239
+ for (let i = 1; i <= left.length; i += 1) {
240
+ next[0] = i;
241
+ const leftCh = left.charCodeAt(i - 1);
242
+ for (let j = 1; j <= right.length; j += 1) {
243
+ const cost = leftCh === right.charCodeAt(j - 1) ? 0 : 1;
244
+ next[j] = Math.min((prev[j] ?? 0) + 1, (next[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
245
+ }
246
+ for (let j = 0; j <= right.length; j += 1)
247
+ prev[j] = next[j] ?? 0;
248
+ }
249
+ return prev[right.length] ?? Math.max(left.length, right.length);
250
+ }
@@ -0,0 +1,5 @@
1
+ import type { ResolveSymbolsRunArgs } from "./types";
2
+ export declare const RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
3
+ export declare const RESOLVE_SYMBOLS_MAX_LIMIT = 25;
4
+ export declare const RESOLVE_SYMBOLS_USAGE: string[];
5
+ export declare function parseResolveSymbolsArgs(args: readonly string[]): ResolveSymbolsRunArgs;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESOLVE_SYMBOLS_USAGE = exports.RESOLVE_SYMBOLS_MAX_LIMIT = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = void 0;
4
+ exports.parseResolveSymbolsArgs = parseResolveSymbolsArgs;
5
+ const errors_1 = require("./errors");
6
+ const exchanges_1 = require("./exchanges");
7
+ exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT = 8;
8
+ exports.RESOLVE_SYMBOLS_MAX_LIMIT = 25;
9
+ exports.RESOLVE_SYMBOLS_USAGE = [
10
+ "alphafox resolve-symbols <query...> [--exchange binance] [--limit 8]",
11
+ "alphafox resolve-symbols --query BTC --query ETH --exchange okx",
12
+ ];
13
+ function usage(message, subtype = "invalid_args") {
14
+ throw new errors_1.ResolveSymbolsError({
15
+ type: "usage",
16
+ subtype,
17
+ message,
18
+ hint: exports.RESOLVE_SYMBOLS_USAGE[0],
19
+ status: 400,
20
+ });
21
+ }
22
+ function takeValue(args, i, flag) {
23
+ const next = args[i + 1];
24
+ if (next === undefined || next.startsWith("--")) {
25
+ usage(`Missing value for ${flag}`, "missing_flag_value");
26
+ }
27
+ return { value: next, next: i + 1 };
28
+ }
29
+ function parseResolveSymbolsArgs(args) {
30
+ const queries = [];
31
+ let exchange = exchanges_1.RESOLVE_SYMBOLS_DEFAULT_EXCHANGE;
32
+ let limit = exports.RESOLVE_SYMBOLS_DEFAULT_LIMIT;
33
+ let help = false;
34
+ for (let i = 0; i < args.length; i += 1) {
35
+ const a = args[i];
36
+ if (a === "--help" || a === "-h") {
37
+ help = true;
38
+ continue;
39
+ }
40
+ if (!a.startsWith("--")) {
41
+ queries.push(a);
42
+ continue;
43
+ }
44
+ const eq = a.indexOf("=");
45
+ const flag = eq >= 0 ? a.slice(0, eq) : a;
46
+ const inline = eq >= 0 ? a.slice(eq + 1) : undefined;
47
+ const read = () => {
48
+ if (inline !== undefined)
49
+ return inline;
50
+ const taken = takeValue(args, i, flag);
51
+ i = taken.next;
52
+ return taken.value;
53
+ };
54
+ switch (flag) {
55
+ case "--query":
56
+ case "--symbol":
57
+ queries.push(read());
58
+ break;
59
+ case "--exchange":
60
+ exchange = read();
61
+ break;
62
+ case "--limit": {
63
+ const n = Number(read());
64
+ if (!Number.isInteger(n) || n <= 0 || n > exports.RESOLVE_SYMBOLS_MAX_LIMIT) {
65
+ usage(`--limit must be an integer 1..${exports.RESOLVE_SYMBOLS_MAX_LIMIT}`, "invalid_limit");
66
+ }
67
+ limit = n;
68
+ break;
69
+ }
70
+ default:
71
+ usage(`Unknown flag: ${flag}`, "unknown_flag");
72
+ }
73
+ }
74
+ if (help) {
75
+ return { help: true, queries: [], exchange, limit };
76
+ }
77
+ const normalizedQueries = queries.map((item) => item.trim()).filter(Boolean);
78
+ if (normalizedQueries.length === 0) {
79
+ usage("Provide at least one symbol query", "missing_query");
80
+ }
81
+ let resolvedExchange;
82
+ try {
83
+ resolvedExchange = (0, exchanges_1.resolveSymbolsExchangeId)(exchange).id;
84
+ }
85
+ catch (err) {
86
+ usage(err instanceof Error ? err.message : String(err), "invalid_exchange");
87
+ }
88
+ return {
89
+ help: false,
90
+ queries: uniqueQueries(normalizedQueries),
91
+ exchange: resolvedExchange,
92
+ limit,
93
+ };
94
+ }
95
+ function uniqueQueries(queries) {
96
+ const seen = new Set();
97
+ const out = [];
98
+ for (const query of queries) {
99
+ const key = query.toUpperCase();
100
+ if (seen.has(key))
101
+ continue;
102
+ seen.add(key);
103
+ out.push(query);
104
+ }
105
+ return out;
106
+ }
@@ -0,0 +1,21 @@
1
+ import type { ApiRequestOptions, ApiResponse } from "../http/client";
2
+ import type { ResolveSymbolsRunArgs, ResolveSymbolsSuccess } from "./types";
3
+ export interface ResolveSymbolsCliFlags {
4
+ readonly profile?: string;
5
+ readonly format: "json" | "jsonl" | "text";
6
+ readonly yes: boolean;
7
+ readonly dryRun: boolean;
8
+ readonly noInput: boolean;
9
+ readonly unsafeCustomEndpoint?: string;
10
+ readonly jq?: string;
11
+ }
12
+ export interface ResolveSymbolsRunDeps {
13
+ readonly apiRequest?: (options: ApiRequestOptions, env?: NodeJS.ProcessEnv) => Promise<ApiResponse>;
14
+ }
15
+ export declare function resolveSymbolsHelpData(): {
16
+ readonly name: string;
17
+ readonly usage: string[];
18
+ readonly notes: string[];
19
+ };
20
+ export declare function executeResolveSymbols(args: ResolveSymbolsRunArgs, flags: ResolveSymbolsCliFlags, env?: NodeJS.ProcessEnv, deps?: ResolveSymbolsRunDeps): Promise<ResolveSymbolsSuccess>;
21
+ export declare function cmdResolveSymbols(args: string[], flags: ResolveSymbolsCliFlags, env?: NodeJS.ProcessEnv, deps?: ResolveSymbolsRunDeps): Promise<number>;