perp-cli 0.4.2 → 0.4.9

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 (89) hide show
  1. package/README.md +46 -5
  2. package/dist/arb/reconcile.d.ts +28 -0
  3. package/dist/arb/reconcile.js +162 -0
  4. package/dist/arb/sizing.d.ts +12 -1
  5. package/dist/arb/sizing.js +38 -5
  6. package/dist/arb/state.d.ts +6 -0
  7. package/dist/cli-spec.d.ts +0 -4
  8. package/dist/cli-spec.js +8 -1
  9. package/dist/commands/account.d.ts +1 -1
  10. package/dist/commands/account.js +300 -63
  11. package/dist/commands/agent.js +16 -9
  12. package/dist/commands/arb/index.js +243 -49
  13. package/dist/commands/arb-auto.js +696 -9
  14. package/dist/commands/arb.d.ts +2 -1
  15. package/dist/commands/arb.js +535 -131
  16. package/dist/commands/bot.js +207 -0
  17. package/dist/commands/dex.js +7 -1
  18. package/dist/commands/funding.d.ts +2 -1
  19. package/dist/commands/funding.js +211 -4
  20. package/dist/commands/funds.d.ts +4 -0
  21. package/dist/commands/funds.js +741 -0
  22. package/dist/commands/history.d.ts +2 -1
  23. package/dist/commands/history.js +508 -5
  24. package/dist/commands/manage.js +6 -4
  25. package/dist/commands/market.js +35 -2
  26. package/dist/commands/multileg.d.ts +16 -0
  27. package/dist/commands/multileg.js +174 -0
  28. package/dist/commands/pnl.d.ts +12 -0
  29. package/dist/commands/pnl.js +243 -0
  30. package/dist/commands/risk.d.ts +2 -0
  31. package/dist/commands/risk.js +56 -0
  32. package/dist/commands/settings.js +147 -0
  33. package/dist/commands/stream.js +132 -55
  34. package/dist/commands/trade.d.ts +1 -1
  35. package/dist/commands/trade.js +270 -267
  36. package/dist/commands/wallet.d.ts +4 -0
  37. package/dist/commands/wallet.js +11 -1
  38. package/dist/config.d.ts +6 -1
  39. package/dist/config.js +21 -1
  40. package/dist/dashboard/ws-feeds.js +1 -1
  41. package/dist/equity-tracker.d.ts +57 -0
  42. package/dist/equity-tracker.js +209 -0
  43. package/dist/exchanges/hyperliquid-spot.d.ts +69 -0
  44. package/dist/exchanges/hyperliquid-spot.js +309 -0
  45. package/dist/exchanges/hyperliquid.d.ts +26 -5
  46. package/dist/exchanges/hyperliquid.js +171 -73
  47. package/dist/exchanges/interface.d.ts +1 -1
  48. package/dist/exchanges/lighter-spot.d.ts +55 -0
  49. package/dist/exchanges/lighter-spot.js +415 -0
  50. package/dist/exchanges/lighter.d.ts +10 -2
  51. package/dist/exchanges/lighter.js +210 -56
  52. package/dist/exchanges/pacifica.d.ts +11 -4
  53. package/dist/exchanges/pacifica.js +64 -37
  54. package/dist/exchanges/spot-interface.d.ts +45 -0
  55. package/dist/exchanges/spot-interface.js +22 -0
  56. package/dist/execution-log.d.ts +2 -2
  57. package/dist/funding/rates.d.ts +36 -0
  58. package/dist/funding/rates.js +146 -0
  59. package/dist/index.js +126 -161
  60. package/dist/liquidity.d.ts +12 -0
  61. package/dist/liquidity.js +43 -0
  62. package/dist/mcp-server.js +43 -96
  63. package/dist/signer/evm-local.d.ts +12 -0
  64. package/dist/signer/evm-local.js +19 -0
  65. package/dist/signer/index.d.ts +3 -0
  66. package/dist/signer/index.js +2 -0
  67. package/dist/signer/interface.d.ts +31 -0
  68. package/dist/signer/interface.js +7 -0
  69. package/dist/signer/solana-local.d.ts +13 -0
  70. package/dist/signer/solana-local.js +19 -0
  71. package/dist/smart-order.js +14 -3
  72. package/dist/strategies/split-order.d.ts +49 -0
  73. package/dist/strategies/split-order.js +138 -0
  74. package/dist/utils.js +7 -1
  75. package/dist/ws/market-feed.d.ts +74 -0
  76. package/dist/ws/market-feed.js +227 -0
  77. package/package.json +1 -1
  78. package/skills/perp-cli/SKILL.md +80 -4
  79. package/skills/perp-cli/references/agent-operations.md +19 -9
  80. package/skills/perp-cli/references/commands.md +54 -32
  81. package/skills/perp-cli/references/strategies.md +27 -17
  82. package/skills/perp-cli/scripts/arb-monitor.sh +74 -0
  83. package/skills/perp-cli/scripts/funding-analysis.sh +85 -0
  84. package/skills/perp-cli/scripts/preflight.sh +98 -0
  85. package/skills/perp-cli/scripts/spot-perp-scan.sh +83 -0
  86. package/skills/perp-cli/scripts/validate-arb.sh +112 -0
  87. package/skills/perp-cli/templates/arb-scan.sh +0 -15
  88. package/skills/perp-cli/templates/open-position.sh +0 -38
  89. package/skills/perp-cli/templates/wallet-setup.sh +0 -17
package/README.md CHANGED
@@ -13,9 +13,10 @@ npx -y perp-cli --json portfolio
13
13
  ## Features
14
14
 
15
15
  - **3 Exchanges** — trade, bridge, arbitrage across Pacifica, Hyperliquid, Lighter
16
- - **Funding Rate Arb** — scan spreads + one-command dual-leg execution (`arb exec`)
16
+ - **Funding Rate Arb** — perp-perp + spot-perp scan & one-command dual-leg execution
17
17
  - **Portfolio** — single call returns balances, positions, risk level across all exchanges
18
- - **CCTP V2 Bridge** — $0 fee USDC bridging (Solana, Arbitrum, Base)
18
+ - **Funds** — deposit, withdraw, CCTP bridge, internal transfer in one group
19
+ - **Bots** — TWAP, grid, DCA, trailing-stop with background job management
19
20
  - **Agent-First Design** — `--json`, `--fields`, `--ndjson`, `--dry-run`, runtime schema introspection
20
21
  - **Safety** — pre-trade validation, response sanitization, client-id deduplication
21
22
 
@@ -33,6 +34,32 @@ perp wallet show
33
34
 
34
35
  Same EVM key works for both Hyperliquid and Lighter.
35
36
 
37
+ > **Lighter API Key Index:** Indexes 0–3 are reserved by Lighter's frontend (web/mobile). perp-cli defaults to index `4`. Override with `LIGHTER_API_KEY_INDEX` env var or `--key-index` flag on `manage setup-api-key`. Valid range: 4–254.
38
+
39
+ ## Command Groups
40
+
41
+ | Group | Description |
42
+ |-------|-------------|
43
+ | `market` | Prices, orderbook, funding, klines, HIP-3 dexes |
44
+ | `account` | Balance, positions, orders, margin |
45
+ | `trade` | Market/limit/stop orders, close, scale, split execution |
46
+ | `arb` | Funding rate arb — scan, exec, close, monitor (perp-perp & spot-perp) |
47
+ | `bot` | TWAP, grid, DCA, trailing-stop bots |
48
+ | `funds` | Deposit, withdraw, transfer, CCTP bridge |
49
+ | `bridge` | Cross-chain USDC bridge (deBridge DLN) |
50
+ | `risk` | Risk limits, liquidation distance, guardrails |
51
+ | `wallet` | Multi-wallet management & on-chain balances |
52
+ | `history` | Execution log, PnL, performance breakdown |
53
+ | `manage` | Margin mode, subaccount, API keys, builder |
54
+ | `portfolio` | Cross-exchange unified overview |
55
+ | `dashboard` | Live web dashboard |
56
+ | `settings` | CLI settings (referrals, defaults) |
57
+ | `backtest` | Strategy backtesting |
58
+ | `plan` | Multi-step composite execution plans |
59
+ | `rebalance` | Cross-exchange balance management |
60
+ | `jobs` | Background job management (tmux) |
61
+ | `agent` | Schema introspection, capabilities, health check |
62
+
36
63
  ## Core Commands
37
64
 
38
65
  ```bash
@@ -46,18 +73,32 @@ perp --json -e <EX> market funding <SYM>
46
73
 
47
74
  # Trading
48
75
  perp --json -e <EX> trade market <SYM> buy <SIZE>
76
+ perp --json -e <EX> trade market <SYM> buy <SIZE> --smart # IOC limit at best bid/ask (less slippage)
77
+ perp --json -e <EX> trade split <SYM> buy 5000 # orderbook-aware split (large orders)
49
78
  perp --json -e <EX> trade close <SYM>
50
79
  perp --json -e <EX> trade leverage <SYM> <N>
51
80
 
52
81
  # Funding rate arbitrage
53
- perp --json arb scan --min 5 # find opportunities
82
+ perp --json arb scan --min 5 # perp-perp opportunities
83
+ perp --json arb scan --mode spot-perp # spot+perp opportunities
54
84
  perp --json arb exec <SYM> <longEx> <shortEx> <$> --leverage 2 --isolated # execute both legs
85
+ perp --json arb spot-exec <SYM> <spotEx> <perpEx> <$> # spot+perp entry
86
+
87
+ # Funds (deposit, withdraw, transfer)
88
+ perp --json funds deposit hyperliquid 100
89
+ perp --json funds withdraw pacifica 50
90
+ perp --json funds transfer 100 <ADDRESS> # HL internal transfer
91
+ perp --json funds info # all routes & limits
55
92
 
56
93
  # Risk
57
94
  perp --json risk limits --max-leverage 5
58
95
  perp --json risk liquidation-distance
59
96
 
60
- # Bridge
97
+ # Bots
98
+ perp --json bot twap <SYM> buy <SIZE> 30m
99
+ perp --json bot grid <SYM> --range 5 --grids 10 --size 100
100
+
101
+ # Bridge (cross-chain USDC)
61
102
  perp --json bridge quote --from solana --to arbitrum --amount 100
62
103
  perp --json bridge send --from solana --to arbitrum --amount 100
63
104
  ```
@@ -90,7 +131,7 @@ perp --json portfolio
90
131
  # Runtime schema introspection (don't guess commands — query this)
91
132
  perp --json agent schema
92
133
 
93
- # Filter output to save tokens
134
+ # Filter output to specific fields (saves tokens)
94
135
  perp --json --fields totalEquity,risk portfolio
95
136
 
96
137
  # Stream large lists as NDJSON (one JSON per line)
@@ -0,0 +1,28 @@
1
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ export interface ReconciliationIssue {
3
+ type: "orphan" | "phantom" | "size_mismatch";
4
+ symbol: string;
5
+ exchange: string;
6
+ side: "long" | "short";
7
+ localSize: number | null;
8
+ exchangeSize: number | null;
9
+ severity: "warning" | "critical";
10
+ suggestion: string;
11
+ }
12
+ export interface ReconciliationResult {
13
+ issues: ReconciliationIssue[];
14
+ healthy: number;
15
+ fixesApplied?: string[];
16
+ }
17
+ /**
18
+ * Reconcile local arb state with actual exchange positions.
19
+ *
20
+ * Detects three types of issues:
21
+ * - **Orphan**: Position exists on exchange but not in local arb state (manual entry?)
22
+ * - **Phantom**: Position exists in local state but not on exchange (liquidated? manually closed?)
23
+ * - **Size mismatch**: Both exist but sizes differ beyond tolerance
24
+ */
25
+ export declare function reconcileArbPositions(adapters: Map<string, ExchangeAdapter>, opts?: {
26
+ fix?: boolean;
27
+ tolerancePct?: number;
28
+ }): Promise<ReconciliationResult>;
@@ -0,0 +1,162 @@
1
+ import { getPositions, removePosition, updatePosition } from "./state.js";
2
+ /** Normalize symbol for comparison (strip -PERP suffix, uppercase) */
3
+ function normalizeSymbol(symbol) {
4
+ return symbol.toUpperCase().replace(/-PERP$/, "");
5
+ }
6
+ /** Find a matching exchange position by symbol */
7
+ function findExchangePosition(positions, symbol, side) {
8
+ const norm = normalizeSymbol(symbol);
9
+ return positions.find(p => normalizeSymbol(p.symbol) === norm && p.side === side);
10
+ }
11
+ /**
12
+ * Reconcile local arb state with actual exchange positions.
13
+ *
14
+ * Detects three types of issues:
15
+ * - **Orphan**: Position exists on exchange but not in local arb state (manual entry?)
16
+ * - **Phantom**: Position exists in local state but not on exchange (liquidated? manually closed?)
17
+ * - **Size mismatch**: Both exist but sizes differ beyond tolerance
18
+ */
19
+ export async function reconcileArbPositions(adapters, opts) {
20
+ const tolerancePct = opts?.tolerancePct ?? 1;
21
+ const shouldFix = opts?.fix ?? false;
22
+ const issues = [];
23
+ const fixesApplied = [];
24
+ let healthy = 0;
25
+ const localPositions = getPositions();
26
+ // Fetch all exchange positions
27
+ const exchangePositions = new Map();
28
+ for (const [name, adapter] of adapters) {
29
+ try {
30
+ const pos = await adapter.getPositions();
31
+ exchangePositions.set(name, pos);
32
+ }
33
+ catch {
34
+ exchangePositions.set(name, []);
35
+ }
36
+ }
37
+ // Track which exchange positions are accounted for
38
+ const accountedFor = new Set(); // "exchange:symbol:side"
39
+ // Check each local arb position against exchange
40
+ for (const arb of localPositions) {
41
+ // Check long leg
42
+ const longPositions = exchangePositions.get(arb.longExchange) ?? [];
43
+ const longMatch = findExchangePosition(longPositions, arb.symbol, "long");
44
+ if (!longMatch) {
45
+ issues.push({
46
+ type: "phantom",
47
+ symbol: arb.symbol,
48
+ exchange: arb.longExchange,
49
+ side: "long",
50
+ localSize: arb.longSize,
51
+ exchangeSize: null,
52
+ severity: "critical",
53
+ suggestion: `Local state has long ${arb.symbol} on ${arb.longExchange} but exchange has no position. May have been liquidated or manually closed.`,
54
+ });
55
+ if (shouldFix) {
56
+ removePosition(arb.symbol);
57
+ fixesApplied.push(`Removed phantom position ${arb.symbol} (long leg missing on ${arb.longExchange})`);
58
+ continue; // Skip short leg check since we removed the position
59
+ }
60
+ }
61
+ else {
62
+ accountedFor.add(`${arb.longExchange}:${normalizeSymbol(arb.symbol)}:long`);
63
+ const exchangeSize = Math.abs(Number(longMatch.size));
64
+ const localSize = Math.abs(arb.longSize);
65
+ const diffPct = localSize > 0 ? (Math.abs(exchangeSize - localSize) / localSize) * 100 : 0;
66
+ if (diffPct > tolerancePct) {
67
+ issues.push({
68
+ type: "size_mismatch",
69
+ symbol: arb.symbol,
70
+ exchange: arb.longExchange,
71
+ side: "long",
72
+ localSize,
73
+ exchangeSize,
74
+ severity: "warning",
75
+ suggestion: `Long size mismatch: local ${localSize} vs exchange ${exchangeSize} (${diffPct.toFixed(1)}% diff). Partial fill or manual adjustment?`,
76
+ });
77
+ if (shouldFix) {
78
+ updatePosition(arb.symbol, { longSize: exchangeSize });
79
+ fixesApplied.push(`Updated ${arb.symbol} long size: ${localSize} → ${exchangeSize}`);
80
+ }
81
+ }
82
+ else {
83
+ healthy++;
84
+ }
85
+ }
86
+ // Check short leg
87
+ const shortPositions = exchangePositions.get(arb.shortExchange) ?? [];
88
+ const shortMatch = findExchangePosition(shortPositions, arb.symbol, "short");
89
+ if (!shortMatch) {
90
+ // Don't double-report if we already removed via fix
91
+ if (shouldFix && !getPositions().find(p => p.symbol === arb.symbol))
92
+ continue;
93
+ issues.push({
94
+ type: "phantom",
95
+ symbol: arb.symbol,
96
+ exchange: arb.shortExchange,
97
+ side: "short",
98
+ localSize: arb.shortSize,
99
+ exchangeSize: null,
100
+ severity: "critical",
101
+ suggestion: `Local state has short ${arb.symbol} on ${arb.shortExchange} but exchange has no position. May have been liquidated or manually closed.`,
102
+ });
103
+ if (shouldFix) {
104
+ removePosition(arb.symbol);
105
+ fixesApplied.push(`Removed phantom position ${arb.symbol} (short leg missing on ${arb.shortExchange})`);
106
+ }
107
+ }
108
+ else {
109
+ accountedFor.add(`${arb.shortExchange}:${normalizeSymbol(arb.symbol)}:short`);
110
+ const exchangeSize = Math.abs(Number(shortMatch.size));
111
+ const localSize = Math.abs(arb.shortSize);
112
+ const diffPct = localSize > 0 ? (Math.abs(exchangeSize - localSize) / localSize) * 100 : 0;
113
+ if (diffPct > tolerancePct) {
114
+ issues.push({
115
+ type: "size_mismatch",
116
+ symbol: arb.symbol,
117
+ exchange: arb.shortExchange,
118
+ side: "short",
119
+ localSize,
120
+ exchangeSize,
121
+ severity: "warning",
122
+ suggestion: `Short size mismatch: local ${localSize} vs exchange ${exchangeSize} (${diffPct.toFixed(1)}% diff). Partial fill or manual adjustment?`,
123
+ });
124
+ if (shouldFix) {
125
+ updatePosition(arb.symbol, { shortSize: exchangeSize });
126
+ fixesApplied.push(`Updated ${arb.symbol} short size: ${localSize} → ${exchangeSize}`);
127
+ }
128
+ }
129
+ else {
130
+ healthy++;
131
+ }
132
+ }
133
+ }
134
+ // Check for orphan positions (on exchange but not in local state)
135
+ for (const [exchangeName, positions] of exchangePositions) {
136
+ for (const pos of positions) {
137
+ const key = `${exchangeName}:${normalizeSymbol(pos.symbol)}:${pos.side}`;
138
+ if (!accountedFor.has(key)) {
139
+ // Check if this exchange is used in any arb position
140
+ const isArbExchange = localPositions.some(p => p.longExchange === exchangeName || p.shortExchange === exchangeName);
141
+ // Only report orphans on exchanges that are part of arb state
142
+ if (isArbExchange || localPositions.length > 0) {
143
+ issues.push({
144
+ type: "orphan",
145
+ symbol: pos.symbol,
146
+ exchange: exchangeName,
147
+ side: pos.side,
148
+ localSize: null,
149
+ exchangeSize: Math.abs(Number(pos.size)),
150
+ severity: "warning",
151
+ suggestion: `Position on ${exchangeName} not tracked in arb state. Manual entry or untracked strategy?`,
152
+ });
153
+ }
154
+ }
155
+ }
156
+ }
157
+ return {
158
+ issues,
159
+ healthy,
160
+ ...(fixesApplied.length > 0 ? { fixesApplied } : {}),
161
+ };
162
+ }
@@ -5,7 +5,18 @@ import type { ExchangeAdapter } from "../exchanges/interface.js";
5
5
  *
6
6
  * @returns size string that works for both exchanges, or null if not viable
7
7
  */
8
- export declare function computeMatchedSize(sizeUsd: number, price: number, longExchange: string, shortExchange: string): {
8
+ export declare function computeMatchedSize(sizeUsd: number, price: number, longExchange: string, shortExchange: string, opts?: {
9
+ longSizeDecimals?: number;
10
+ shortSizeDecimals?: number;
11
+ }): {
12
+ size: string;
13
+ notional: number;
14
+ } | null;
15
+ /**
16
+ * Compute matched size for spot-perp arb.
17
+ * Uses spot exchange's size decimals for the spot leg.
18
+ */
19
+ export declare function computeSpotPerpMatchedSize(sizeUsd: number, price: number, spotExchange: string, perpExchange: string, spotSizeDecimals?: number): {
9
20
  size: string;
10
21
  notional: number;
11
22
  } | null;
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * @returns size string that works for both exchanges, or null if not viable
6
6
  */
7
- export function computeMatchedSize(sizeUsd, price, longExchange, shortExchange) {
7
+ export function computeMatchedSize(sizeUsd, price, longExchange, shortExchange, opts) {
8
8
  if (price <= 0)
9
9
  return null;
10
10
  const rawSize = sizeUsd / price;
11
- // Size decimals per exchange (conservative defaults)
12
- const szDecimals = Math.min(getSizeDecimals(longExchange), getSizeDecimals(shortExchange));
11
+ // Use explicitly provided decimals if available, else conservative defaults
12
+ const szDecimals = Math.min(opts?.longSizeDecimals ?? getSizeDecimals(longExchange), opts?.shortSizeDecimals ?? getSizeDecimals(shortExchange));
13
13
  // Round DOWN to the least precise exchange's decimals
14
14
  const factor = Math.pow(10, szDecimals);
15
15
  const roundedSize = Math.floor(rawSize * factor) / factor;
@@ -30,12 +30,15 @@ export function computeMatchedSize(sizeUsd, price, longExchange, shortExchange)
30
30
  }
31
31
  return { size: roundedSize.toFixed(szDecimals), notional };
32
32
  }
33
- /** Size decimal precision by exchange */
33
+ /** Size decimal precision by exchange (conservative fallbacks — prefer explicit decimals via opts) */
34
34
  function getSizeDecimals(exchange) {
35
35
  switch (exchange.toLowerCase()) {
36
- case "hyperliquid": return 1; // Most HL perps use 1 decimal
36
+ case "hyperliquid": return 2; // HL perps range 0-5; 2 is safe middle ground
37
37
  case "lighter": return 2;
38
38
  case "pacifica": return 4;
39
+ // Spot exchanges
40
+ case "spot:hyperliquid": return 2;
41
+ case "spot:lighter": return 2;
39
42
  default: return 2;
40
43
  }
41
44
  }
@@ -45,9 +48,39 @@ function getMinNotional(exchange) {
45
48
  case "hyperliquid": return 10;
46
49
  case "lighter": return 10;
47
50
  case "pacifica": return 1;
51
+ case "spot:hyperliquid": return 10;
52
+ case "spot:lighter": return 10;
48
53
  default: return 10;
49
54
  }
50
55
  }
56
+ /**
57
+ * Compute matched size for spot-perp arb.
58
+ * Uses spot exchange's size decimals for the spot leg.
59
+ */
60
+ export function computeSpotPerpMatchedSize(sizeUsd, price, spotExchange, perpExchange, spotSizeDecimals) {
61
+ if (price <= 0)
62
+ return null;
63
+ // If explicit spot decimals provided, use them; otherwise derive from exchange
64
+ const spotDec = spotSizeDecimals ?? getSizeDecimals(`spot:${spotExchange}`);
65
+ const perpDec = getSizeDecimals(perpExchange);
66
+ const szDecimals = Math.min(spotDec, perpDec);
67
+ const rawSize = sizeUsd / price;
68
+ const factor = Math.pow(10, szDecimals);
69
+ const roundedSize = Math.floor(rawSize * factor) / factor;
70
+ if (roundedSize <= 0)
71
+ return null;
72
+ const notional = roundedSize * price;
73
+ const minNotional = Math.max(getMinNotional(`spot:${spotExchange}`), getMinNotional(perpExchange));
74
+ if (notional < minNotional) {
75
+ const roundedUp = Math.ceil(rawSize * factor) / factor;
76
+ const notionalUp = roundedUp * price;
77
+ if (notionalUp <= sizeUsd * 1.2) {
78
+ return { size: roundedUp.toFixed(szDecimals), notional: notionalUp };
79
+ }
80
+ return null;
81
+ }
82
+ return { size: roundedSize.toFixed(szDecimals), notional };
83
+ }
51
84
  /**
52
85
  * After both legs are submitted, verify actual fills match.
53
86
  * If there's a size mismatch, place a correction order on the larger side.
@@ -13,6 +13,12 @@ export interface ArbPositionState {
13
13
  entryShortPrice: number;
14
14
  accumulatedFunding: number;
15
15
  lastCheckTime: string;
16
+ /** "perp-perp" (default) or "spot-perp" */
17
+ mode?: "perp-perp" | "spot-perp";
18
+ /** Spot leg exchange name (for spot-perp mode) */
19
+ spotExchange?: string;
20
+ /** Spot symbol, e.g. "ETH/USDC" (for spot-perp mode) */
21
+ spotSymbol?: string;
16
22
  }
17
23
  export interface ArbDaemonState {
18
24
  version: 1;
@@ -1,7 +1,3 @@
1
- /**
2
- * Generates a structured CLI spec from Commander's program tree.
3
- * Used by `perp api-spec` so agents can discover all commands programmatically.
4
- */
5
1
  import type { Command } from "commander";
6
2
  interface ArgSpec {
7
3
  name: string;
package/dist/cli-spec.js CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Generates a structured CLI spec from Commander's program tree.
3
+ * Used by `perp api-spec` so agents can discover all commands programmatically.
4
+ */
5
+ import { createRequire } from "node:module";
6
+ const require = createRequire(import.meta.url);
7
+ const { version: pkgVersion } = require("../package.json");
1
8
  function extractCommand(cmd) {
2
9
  const args = cmd._args ?? [];
3
10
  const options = cmd.options.map((opt) => ({
@@ -27,7 +34,7 @@ export function getCliSpec(program) {
27
34
  }));
28
35
  return {
29
36
  name: "perp",
30
- version: "0.1.0",
37
+ version: pkgVersion,
31
38
  description: "Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter)",
32
39
  globalOptions,
33
40
  commands,
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
2
  import type { ExchangeAdapter } from "../exchanges/interface.js";
3
- export declare function registerAccountCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean): void;
3
+ export declare function registerAccountCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;