minara 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Minara
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -18,15 +18,10 @@
18
18
 
19
19
  ## Features
20
20
 
21
- - **Multi-method Login** — Email verification code, Google OAuth, or Apple ID
22
- - **Deposit & Withdraw** — View deposit addresses across chains, withdraw to external wallets
23
- - **Asset Management** — View wallet balances across all supported chains
24
- - **AI Chat** — Stream conversations with Minara AI, with thinking and deep-research modes
25
- - **Spot Trading** — Cross-chain token swaps with dry-run simulation
26
- - **Perpetual Futures** — Deposit, withdraw, place/cancel orders, manage leverage (Hyperliquid)
27
- - **Limit Orders** — Create, list, and cancel price-triggered orders
28
- - **Copy Trading** — Follow wallet addresses with configurable bots
29
- - **Market Discovery** — Trending tokens, Fear & Greed Index, Bitcoin metrics
21
+ - **AI Chat** — Crypto-native AI for on-chain analysis, market research, and DeFi due diligence. Interactive REPL & single-shot queries with `fast` / `quality` / `thinking` modes
22
+ - **Wallet & Balance** — Unified balance view, spot holdings with PnL, perps account overview, deposits and withdrawals
23
+ - **Chain-Abstracted Trading** — Cross-chain swaps with automatic chain detection, perpetual futures, and limit orders. Accepts `$TICKER`, token name, or contract address
24
+ - **Market Discovery** — Trending tokens, Fear & Greed Index, on-chain metrics, and token / stock search
30
25
 
31
26
  ## Installation
32
27
 
@@ -54,11 +49,14 @@ minara account
54
49
  # View deposit addresses
55
50
  minara deposit
56
51
 
57
- # Chat with Minara AI
52
+ # Chat with Minara AI (interactive REPL)
53
+ minara chat
54
+
55
+ # Or send a single question
58
56
  minara chat "What's the best DeFi yield right now?"
59
57
 
60
- # Swap tokens
61
- minara swap
58
+ # Swap tokens (chain auto-detected from token)
59
+ minara swap -t '$BONK' -s buy -a 100
62
60
 
63
61
  # View trending tokens
64
62
  minara discover trending
@@ -68,10 +66,10 @@ minara discover trending
68
66
 
69
67
  ### Auth & Account
70
68
 
71
- | Command | Description |
72
- |---------|-------------|
73
- | `minara login` | Login via email, Google, or Apple ID |
74
- | `minara logout` | Logout and clear local credentials |
69
+ | Command | Description |
70
+ | ---------------- | ------------------------------------------- |
71
+ | `minara login` | Login via email, Google, or Apple ID |
72
+ | `minara logout` | Logout and clear local credentials |
75
73
  | `minara account` | View your account info and wallet addresses |
76
74
 
77
75
  ```bash
@@ -83,43 +81,57 @@ minara login --apple # Apple ID (opens browser)
83
81
 
84
82
  ### Wallet & Funds
85
83
 
86
- | Command | Description |
87
- |---------|-------------|
88
- | `minara assets` | View wallet balances across all chains |
89
- | `minara deposit` | Show deposit addresses and supported networks |
90
- | `minara withdraw` | Withdraw tokens to an external wallet |
84
+ | Command | Description |
85
+ | --------------------- | ----------------------------------------------- |
86
+ | `minara balance` | Combined USDC/USDT balance across spot and perps |
87
+ | `minara assets` | Full overview: spot holdings + perps account |
88
+ | `minara assets spot` | Spot wallet: portfolio value, cost, PnL, holdings |
89
+ | `minara assets perps` | Perps account: equity, margin, positions |
90
+ | `minara deposit` | Deposit to spot (view addresses) or perps (direct / from spot) |
91
+ | `minara withdraw` | Withdraw tokens to an external wallet |
91
92
 
92
93
  ```bash
93
- minara deposit
94
- minara withdraw -c solana -t <token> -a 10 --to <address>
95
- minara withdraw # Interactive mode
94
+ minara balance # Quick total: Spot + Perps available balance
95
+ minara assets # Full overview (spot + perps)
96
+ minara assets spot # Spot wallet with PnL breakdown
97
+ minara assets perps # Perps equity, margin, positions
98
+ minara deposit # Interactive: Spot (addresses) or Perps (address / transfer)
99
+ minara deposit spot # Show spot wallet deposit addresses (EVM + Solana)
100
+ minara deposit perps # Perps: show Arbitrum deposit address, or transfer from Spot → Perps
101
+ minara withdraw -c solana -t '$SOL' -a 10 --to <address>
102
+ minara withdraw # Interactive mode (accepts ticker or address)
96
103
  ```
97
104
 
98
105
  ### Spot Trading
99
106
 
100
- | Command | Description |
101
- |---------|-------------|
102
- | `minara swap` | Swap tokens (cross-chain) |
107
+ | Command | Description |
108
+ | ----------------- | ---------------------------------- |
109
+ | `minara swap` | Swap tokens (chain auto-detected) |
103
110
  | `minara transfer` | Transfer tokens to another address |
104
111
 
105
112
  ```bash
106
- minara swap # Interactive
107
- minara swap -c solana -s buy -t <token> -a 100
113
+ minara swap # Interactive: side → token → amount
114
+ minara swap -s buy -t '$BONK' -a 100 # Buy by ticker (chain auto-detected)
115
+ minara swap -s sell -t '$NVDAx' -a all # Sell entire balance
108
116
  minara swap --dry-run # Simulate without executing
109
117
  ```
110
118
 
119
+ > **Chain abstraction:** The chain is automatically detected from the token. If a token exists on multiple chains (e.g. USDC), you'll be prompted to pick one, sorted by gas cost (lowest first). Sell mode supports `all` to sell full balance, and caps amounts exceeding your balance.
120
+ >
121
+ > **Token input:** All token fields (`-t`) accept a `$TICKER` (e.g. `$BONK`), a token name, or a contract address.
122
+
111
123
  ### Perpetual Futures
112
124
 
113
- | Command | Description |
114
- |---------|-------------|
115
- | `minara perps deposit` | Deposit USDC to Hyperliquid perps account |
116
- | `minara perps withdraw` | Withdraw USDC from perps account |
117
- | `minara perps positions` | View all open positions |
118
- | `minara perps order` | Place an order (interactive builder) |
119
- | `minara perps cancel` | Cancel open orders |
120
- | `minara perps leverage` | Update leverage for a symbol |
121
- | `minara perps trades` | View completed trade history |
122
- | `minara perps fund-records` | View fund deposit/withdrawal records |
125
+ | Command | Description |
126
+ | --------------------------- | ----------------------------------------- |
127
+ | `minara perps deposit` | Deposit USDC to perps (or use `minara deposit perps`) |
128
+ | `minara perps withdraw` | Withdraw USDC from perps account |
129
+ | `minara perps positions` | View all open positions |
130
+ | `minara perps order` | Place an order (interactive builder) |
131
+ | `minara perps cancel` | Cancel open orders |
132
+ | `minara perps leverage` | Update leverage for a symbol |
133
+ | `minara perps trades` | View completed trade history |
134
+ | `minara perps fund-records` | View fund deposit/withdrawal records |
123
135
 
124
136
  ```bash
125
137
  minara perps deposit -a 100 # Deposit 100 USDC to perps
@@ -131,11 +143,11 @@ minara perps leverage # Interactive: set leverage for a trading pai
131
143
 
132
144
  ### Limit Orders
133
145
 
134
- | Command | Description |
135
- |---------|-------------|
136
- | `minara limit-order create` | Create a price-triggered limit order |
137
- | `minara limit-order list` | List all your limit orders |
138
- | `minara limit-order cancel <id>` | Cancel a specific order by ID |
146
+ | Command | Description |
147
+ | -------------------------------- | ------------------------------------ |
148
+ | `minara limit-order create` | Create a price-triggered limit order |
149
+ | `minara limit-order list` | List all your limit orders |
150
+ | `minara limit-order cancel <id>` | Cancel a specific order by ID |
139
151
 
140
152
  ```bash
141
153
  minara limit-order create # Interactive: token, price, side, amount, expiry
@@ -143,48 +155,54 @@ minara limit-order list # Show all orders with status
143
155
  minara limit-order cancel abc123 # Cancel order by ID
144
156
  ```
145
157
 
146
- ### Copy Trading
147
-
148
- | Command | Description |
149
- |---------|-------------|
150
- | `minara copy-trade create` | Create a new copy-trade bot |
151
- | `minara copy-trade list` | List all copy-trade bots |
152
- | `minara copy-trade start <id>` | Start a paused bot |
153
- | `minara copy-trade stop <id>` | Pause a running bot |
154
- | `minara copy-trade delete <id>` | Delete a bot permanently |
155
-
156
- ```bash
157
- minara copy-trade create # Interactive: target wallet, chain, amount, options
158
- minara copy-trade list # Show all bots with status
159
- minara copy-trade start abc123 # Resume a paused bot
160
- minara copy-trade stop abc123 # Pause a running bot
161
- ```
162
-
163
158
  ### AI Chat
164
159
 
165
- | Command | Description |
166
- |---------|-------------|
167
- | `minara chat [message]` | Send a message (or enter interactive mode) |
168
- | `minara chat --list` | List all your conversations |
169
- | `minara chat --history <chatId>` | View messages in a conversation |
160
+ | Command | Description |
161
+ | -------------------------------- | --------------------------------------------- |
162
+ | `minara chat` | Enter interactive REPL (Python/Node.js-style) |
163
+ | `minara chat [message]` | Send a single message and exit |
164
+ | `minara chat --list` | List all your conversations |
165
+ | `minara chat --history <chatId>` | View messages in a conversation |
166
+ | `minara chat -c <chatId>` | Continue an existing conversation |
170
167
 
171
168
  ```bash
172
- minara chat "What is the current BTC price?" # Single question, streamed answer
173
169
  minara chat # Enter interactive REPL mode
170
+ minara chat "What is the current BTC price?" # Single question, streamed answer
171
+ minara chat --quality "Analyze ETH outlook" # Quality mode (default: fast)
174
172
  minara chat --thinking "Analyze ETH outlook" # Enable reasoning mode
175
- minara chat --deep-research "DeFi yield trends"# Deep research mode
173
+ minara chat -c <chatId> # Continue a specific chat in REPL
176
174
  minara chat --list # List past conversations
177
175
  minara chat --history <chatId> # Replay a specific conversation
178
176
  ```
179
177
 
178
+ **Interactive REPL mode** — When launched without a message argument, the chat enters an interactive session:
179
+
180
+ ```
181
+ Minara AI Chat session:a1b2c3d4
182
+ ──────────────────────────────────────────────────
183
+ Type a message to chat. /help for commands, Ctrl+C to exit.
184
+
185
+ >>> What's the price of BTC?
186
+ Minara: Bitcoin is currently trading at $95,432...
187
+
188
+ >>> /help
189
+
190
+ Commands:
191
+ /new Start a new conversation
192
+ /continue Continue an existing conversation
193
+ /list List all historical chats
194
+ /id Show current chat ID
195
+ exit Quit the chat
196
+ ```
197
+
180
198
  ### Market Discovery
181
199
 
182
- | Command | Description |
183
- |---------|-------------|
184
- | `minara discover trending` | View currently trending tokens |
185
- | `minara discover search <keyword>` | Search for tokens or stocks by name |
186
- | `minara discover fear-greed` | View the crypto Fear & Greed Index |
187
- | `minara discover btc-metrics` | View Bitcoin on-chain and market metrics |
200
+ | Command | Description |
201
+ | ---------------------------------- | ---------------------------------------- |
202
+ | `minara discover trending` | View currently trending tokens |
203
+ | `minara discover search <keyword>` | Search for tokens or stocks by name |
204
+ | `minara discover fear-greed` | View the crypto Fear & Greed Index |
205
+ | `minara discover btc-metrics` | View Bitcoin on-chain and market metrics |
188
206
 
189
207
  ```bash
190
208
  minara discover trending # Top trending tokens right now
@@ -193,11 +211,89 @@ minara discover fear-greed # Current market sentiment index
193
211
  minara discover btc-metrics # Bitcoin hashrate, supply, dominance, etc.
194
212
  ```
195
213
 
214
+ ### Premium & Subscription
215
+
216
+ | Command | Description |
217
+ | ---------------------------- | ----------------------------------------------- |
218
+ | `minara premium plans` | View all subscription plans and credit packages |
219
+ | `minara premium status` | View your current subscription status |
220
+ | `minara premium subscribe` | Subscribe or change plan (upgrade / downgrade) |
221
+ | `minara premium buy-credits` | Buy a one-time credit package |
222
+ | `minara premium cancel` | Cancel your current subscription |
223
+
224
+ ```bash
225
+ minara premium plans # Compare Free, Lite, Starter, Pro, Partner plans
226
+ minara premium status # Check your current plan and billing info
227
+ minara premium subscribe # Interactive: select plan → Stripe or Crypto payment
228
+ minara premium buy-credits # Buy additional credits (one-time purchase)
229
+ minara premium cancel # Cancel subscription (keeps access until period ends)
230
+ ```
231
+
232
+ ### Output Format
233
+
234
+ By default, all commands display data using formatted tables, colored text, and human-friendly numbers (e.g. `$1.23M`, `+3.46%`). To get raw JSON output for scripting or piping, add the `--json` flag to any command:
235
+
236
+ ```bash
237
+ minara discover trending --json # Raw JSON array of trending tokens
238
+ minara discover btc-metrics --json # Full BTC metrics with OHLCV data
239
+ minara assets spot --json # Raw JSON asset list
240
+ ```
241
+
196
242
  ### Configuration
197
243
 
198
- | Command | Description |
199
- |---------|-------------|
200
- | `minara config` | View or update CLI settings (e.g. API base URL) |
244
+ | Command | Description |
245
+ | --------------- | ---------------------------------------------------------------------- |
246
+ | `minara config` | View or update CLI settings (base URL, Touch ID, transaction confirm…) |
247
+
248
+ ```bash
249
+ minara config # Interactive settings menu
250
+ ```
251
+
252
+ Available settings:
253
+
254
+ | Setting | Default | Description |
255
+ | ------------------------ | ------- | ---------------------------------------------------- |
256
+ | Base URL | — | API endpoint |
257
+ | Touch ID | Off | Biometric verification for fund operations (macOS) |
258
+ | Transaction Confirmation | **On** | Mandatory second confirmation before fund operations |
259
+
260
+ ### Transaction Safety
261
+
262
+ All fund-related operations go through a multi-layer safety flow:
263
+
264
+ ```
265
+ 1. First confirmation (skippable with -y flag)
266
+ 2. Transaction confirmation (mandatory — configurable in minara config)
267
+ 3. Touch ID verification (optional — macOS only)
268
+ 4. Execute
269
+ ```
270
+
271
+ The **transaction confirmation** shows chain, token, address, side, amount, and operation details before asking for final approval:
272
+
273
+ ```
274
+ ⚠ Transaction confirmation
275
+ Chain : solana
276
+ Token : $BONK — Bonk
277
+ Address : DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263
278
+ Side : BUY
279
+ Amount : 100 USD
280
+ Action : BUY swap · 100 USD · solana
281
+ ? Are you sure you want to proceed? (y/N)
282
+ ```
283
+
284
+ This step is independent of the `-y` flag and Touch ID — it serves as an extra safety net. Disable it via `minara config` if not needed.
285
+
286
+ ### Touch ID (macOS)
287
+
288
+ Minara CLI supports macOS Touch ID to protect all fund-related operations. When enabled, transfers, withdrawals, swaps, orders, and other financial actions require fingerprint verification before execution.
289
+
290
+ ```bash
291
+ minara config # Select "Touch ID" to enable / disable
292
+ ```
293
+
294
+ **Protected operations:** `withdraw`, `transfer`, `swap`, `deposit` (Spot→Perps transfer), `perps deposit`, `perps withdraw`, `perps order`, `limit-order create`
295
+
296
+ > **Note:** Touch ID requires macOS with Touch ID hardware. The `--yes` flag skips the initial confirmation prompt but does **not** bypass transaction confirmation or Touch ID.
201
297
 
202
298
  ## Supported Chains
203
299
 
@@ -235,6 +331,9 @@ npm run test:coverage # With coverage report
235
331
 
236
332
  ## Security
237
333
 
334
+ - **Transaction Confirmation** — Mandatory second confirmation before all fund operations, showing full token details and contract addresses (default: enabled, configurable)
335
+ - **Touch ID** — Optional biometric protection for all fund operations (macOS only). A native Swift helper binary is compiled on first use and cached in `~/.minara/`
336
+ - **Token Verification** — Token ticker, name, and full contract address are always displayed before any transaction to prevent wrong-token mistakes
238
337
  - Credentials are stored in `~/.minara/credentials.json` with `0600` file permissions
239
338
  - The `~/.minara/` directory is created with `0700` permissions
240
339
  - Tokens are never logged or printed to the console
@@ -0,0 +1,17 @@
1
+ import type { PlansResponse, CheckoutSession, CryptoCheckout } from '../types.js';
2
+ /** Get all subscription plans and credit packages */
3
+ export declare function getPlans(): Promise<import("../types.js").ApiResponse<PlansResponse>>;
4
+ /** Create a Stripe checkout session for a subscription plan */
5
+ export declare function checkoutPlan(token: string, planId: string, successUrl: string, cancelUrl: string): Promise<import("../types.js").ApiResponse<CheckoutSession>>;
6
+ /** Create a crypto checkout for a subscription plan */
7
+ export declare function cryptoCheckoutPlan(token: string, planId: string): Promise<import("../types.js").ApiResponse<CryptoCheckout>>;
8
+ /** Get crypto checkout pay amount for a plan */
9
+ export declare function getCryptoPayAmount(token: string, planId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
10
+ /** Simulate crypto checkout for a plan */
11
+ export declare function simulateCryptoCheckout(token: string, planId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
12
+ /** Create a Stripe checkout session for a credit package */
13
+ export declare function checkoutPackage(token: string, packageId: string, successUrl: string, cancelUrl: string): Promise<import("../types.js").ApiResponse<CheckoutSession>>;
14
+ /** Create a crypto checkout for a credit package */
15
+ export declare function cryptoCheckoutPackage(token: string, packageId: string): Promise<import("../types.js").ApiResponse<CryptoCheckout>>;
16
+ /** Cancel current subscription */
17
+ export declare function cancelSubscription(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
@@ -0,0 +1,39 @@
1
+ import { get, del } from './client.js';
2
+ /** Get all subscription plans and credit packages */
3
+ export function getPlans() {
4
+ return get('/payment/plans');
5
+ }
6
+ /** Create a Stripe checkout session for a subscription plan */
7
+ export function checkoutPlan(token, planId, successUrl, cancelUrl) {
8
+ return get(`/payment/plans/${planId}/checkout`, {
9
+ token,
10
+ query: { successUrl, cancelUrl },
11
+ });
12
+ }
13
+ /** Create a crypto checkout for a subscription plan */
14
+ export function cryptoCheckoutPlan(token, planId) {
15
+ return get(`/payment/plans/${planId}/crypto-checkout`, { token });
16
+ }
17
+ /** Get crypto checkout pay amount for a plan */
18
+ export function getCryptoPayAmount(token, planId) {
19
+ return get(`/payment/plans/${planId}/crypto-checkout/payAmount`, { token });
20
+ }
21
+ /** Simulate crypto checkout for a plan */
22
+ export function simulateCryptoCheckout(token, planId) {
23
+ return get(`/payment/plans/${planId}/crypto-checkout/simulate`, { token });
24
+ }
25
+ /** Create a Stripe checkout session for a credit package */
26
+ export function checkoutPackage(token, packageId, successUrl, cancelUrl) {
27
+ return get(`/payment/packages/${packageId}/checkout`, {
28
+ token,
29
+ query: { successUrl, cancelUrl },
30
+ });
31
+ }
32
+ /** Create a crypto checkout for a credit package */
33
+ export function cryptoCheckoutPackage(token, packageId) {
34
+ return get(`/payment/packages/${packageId}/crypto-checkout`, { token });
35
+ }
36
+ /** Cancel current subscription */
37
+ export function cancelSubscription(token) {
38
+ return del('/payment/subscription', { token });
39
+ }
@@ -21,6 +21,8 @@ export declare function getTokenPrices(token: string): Promise<import("../types.
21
21
  export declare function getFundRecords(token: string, page: number, limit: number): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
22
22
  /** Get equity history chart */
23
23
  export declare function getEquityHistory(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
24
+ /** Get perps account summary (balance, equity, positions, PnL) */
25
+ export declare function getAccountSummary(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
24
26
  /** Get all decisions */
25
27
  export declare function getDecisions(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
26
28
  /** Claim rewards */
package/dist/api/perps.js CHANGED
@@ -43,6 +43,10 @@ export function getFundRecords(token, page, limit) {
43
43
  export function getEquityHistory(token) {
44
44
  return get('/v1/tx/perps/equity-history-chart/all', { token });
45
45
  }
46
+ /** Get perps account summary (balance, equity, positions, PnL) */
47
+ export function getAccountSummary(token) {
48
+ return get('/v1/fully-managed/account-summary', { token });
49
+ }
46
50
  /** Get all decisions */
47
51
  export function getDecisions(token) {
48
52
  return get('/v1/tx/perps/decisions/all', { token });
@@ -19,3 +19,7 @@ export declare function getEvents(page: string, pageSize: string, version: strin
19
19
  export declare function getFearGreedIndex(): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
20
20
  /** Get bitcoin metrics */
21
21
  export declare function getBitcoinMetrics(): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
22
+ /** Get ethereum metrics */
23
+ export declare function getEthereumMetrics(): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
24
+ /** Get solana metrics */
25
+ export declare function getSolanaMetrics(): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
@@ -39,3 +39,11 @@ export function getFearGreedIndex() {
39
39
  export function getBitcoinMetrics() {
40
40
  return get('/discover/bitcoin-metrics');
41
41
  }
42
+ /** Get ethereum metrics */
43
+ export function getEthereumMetrics() {
44
+ return get('/discover/ethereum-metrics');
45
+ }
46
+ /** Get solana metrics */
47
+ export function getSolanaMetrics() {
48
+ return get('/discover/solana-metrics');
49
+ }
@@ -1,22 +1,126 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { getAssets } from '../api/crosschain.js';
3
+ import * as perpsApi from '../api/perps.js';
4
+ import { get } from '../api/client.js';
4
5
  import { requireAuth } from '../config.js';
5
- import { spinner, unwrapApi, wrapAction } from '../utils.js';
6
- export const assetsCommand = new Command('assets')
7
- .description('View your wallet assets across chains')
6
+ import { spinner, wrapAction } from '../utils.js';
7
+ import { printTable, SPOT_COLUMNS, POSITION_COLUMNS } from '../formatters.js';
8
+ // ─── spot ────────────────────────────────────────────────────────────────
9
+ const spotCmd = new Command('spot')
10
+ .description('View spot wallet assets across chains')
11
+ .action(wrapAction(async () => {
12
+ const creds = requireAuth();
13
+ await showSpotAssets(creds.accessToken);
14
+ }));
15
+ const MIN_DISPLAY_VALUE = 0.01;
16
+ async function showSpotAssets(token) {
17
+ const spin = spinner('Fetching spot assets…');
18
+ const res = await get('/users/pnls/all', { token });
19
+ spin.stop();
20
+ if (!res.success || !res.data) {
21
+ console.log('');
22
+ console.log(chalk.bold('Spot Wallet Assets:'));
23
+ console.log(chalk.dim(' Could not fetch spot assets.'));
24
+ if (res.error?.message)
25
+ console.log(chalk.dim(` ${res.error.message}`));
26
+ console.log('');
27
+ return;
28
+ }
29
+ const all = res.data;
30
+ const holdings = [];
31
+ let totalValue = 0;
32
+ let totalRealizedPnl = 0;
33
+ let totalUnrealizedPnl = 0;
34
+ let hasUnrealizedPnl = false;
35
+ for (const t of all) {
36
+ const bal = Number(t.balance ?? 0);
37
+ const price = Number(t.marketPrice ?? 0);
38
+ const apiVal = Number(t.portfolioValue ?? 0);
39
+ const value = apiVal > 0 ? apiVal : bal * price;
40
+ const uPnl = Number(t.unrealizedPnl ?? 0);
41
+ const rPnl = Number(t.realizedPnl ?? 0);
42
+ totalValue += value;
43
+ totalRealizedPnl += rPnl;
44
+ if (uPnl !== 0) {
45
+ totalUnrealizedPnl += uPnl;
46
+ hasUnrealizedPnl = true;
47
+ }
48
+ if (bal > 0 && value >= MIN_DISPLAY_VALUE) {
49
+ holdings.push({ ...t, _value: value });
50
+ }
51
+ }
52
+ const fmt = (n) => `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
53
+ const pnlFmt = (n) => {
54
+ if (n === 0)
55
+ return chalk.dim('$0.00');
56
+ const color = n >= 0 ? chalk.green : chalk.red;
57
+ return color(`${n >= 0 ? '+' : ''}${fmt(n)}`);
58
+ };
59
+ console.log('');
60
+ console.log(chalk.bold('Spot Wallet:'));
61
+ console.log(` Portfolio Value : ${fmt(totalValue)}`);
62
+ console.log(` Unrealized PnL : ${pnlFmt(totalUnrealizedPnl)}`);
63
+ console.log(` Realized PnL : ${pnlFmt(totalRealizedPnl)}`);
64
+ console.log('');
65
+ console.log(chalk.bold(`Holdings (${holdings.length}):`));
66
+ if (holdings.length === 0) {
67
+ console.log(chalk.dim(' No spot assets with balance.'));
68
+ }
69
+ else {
70
+ printTable(holdings, SPOT_COLUMNS);
71
+ }
72
+ console.log('');
73
+ }
74
+ // ─── perps ───────────────────────────────────────────────────────────────
75
+ const perpsCmd = new Command('perps')
76
+ .description('View perps account balance and positions')
8
77
  .action(wrapAction(async () => {
9
78
  const creds = requireAuth();
10
- const spin = spinner('Fetching assets…');
11
- const res = await getAssets(creds.accessToken);
79
+ await showPerpsAssets(creds.accessToken);
80
+ }));
81
+ async function showPerpsAssets(token) {
82
+ const spin = spinner('Fetching perps account…');
83
+ const res = await perpsApi.getAccountSummary(token);
12
84
  spin.stop();
13
- const data = unwrapApi(res, 'Failed to fetch assets');
14
- if (Array.isArray(data) && data.length === 0) {
15
- console.log(chalk.dim('No assets found.'));
85
+ if (!res.success || !res.data) {
86
+ console.log(chalk.dim(' Could not fetch perps account.'));
87
+ if (res.error?.message)
88
+ console.log(chalk.dim(` ${res.error.message}`));
16
89
  return;
17
90
  }
91
+ const d = res.data;
92
+ const fmt = (n) => `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
93
+ const pnlFmt = (n) => {
94
+ const color = n >= 0 ? chalk.green : chalk.red;
95
+ return color(`${n >= 0 ? '+' : ''}${fmt(n)}`);
96
+ };
97
+ // ── Account overview ───────────────────────────────────────────────
98
+ console.log('');
99
+ console.log(chalk.bold('Perps Account:'));
100
+ console.log(` Equity : ${fmt(Number(d.equityValue ?? 0))}`);
101
+ console.log(` Available : ${fmt(Number(d.dispatchableValue ?? 0))}`);
102
+ console.log(` Margin Used : ${fmt(Number(d.totalMarginUsed ?? 0))}`);
103
+ console.log(` Unrealized PnL: ${pnlFmt(Number(d.totalUnrealizedPnl ?? 0))}`);
104
+ console.log(` Withdrawable : ${fmt(Number(d.withdrawableValue ?? 0))}`);
105
+ // ── Positions ───────────────────────────────────────────────────────
106
+ const positions = Array.isArray(d.positions) ? d.positions : [];
18
107
  console.log('');
19
- console.log(chalk.bold('Your Assets:'));
20
- console.log(JSON.stringify(data, null, 2));
108
+ console.log(chalk.bold(`Open Positions (${positions.length}):`));
109
+ if (positions.length === 0) {
110
+ console.log(chalk.dim(' No open positions.'));
111
+ }
112
+ else {
113
+ printTable(positions, POSITION_COLUMNS);
114
+ }
21
115
  console.log('');
116
+ }
117
+ // ─── parent ──────────────────────────────────────────────────────────────
118
+ export const assetsCommand = new Command('assets')
119
+ .description('View your wallet assets (spot & perps)')
120
+ .addCommand(spotCmd)
121
+ .addCommand(perpsCmd)
122
+ .action(wrapAction(async () => {
123
+ const creds = requireAuth();
124
+ await showSpotAssets(creds.accessToken);
125
+ await showPerpsAssets(creds.accessToken);
22
126
  }));
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const balanceCommand: Command;
@@ -0,0 +1,43 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { get } from '../api/client.js';
4
+ import * as perpsApi from '../api/perps.js';
5
+ import { requireAuth } from '../config.js';
6
+ import { spinner, wrapAction } from '../utils.js';
7
+ const STABLES = new Set(['usdc', 'usdt']);
8
+ export const balanceCommand = new Command('balance')
9
+ .description('Show combined USDC / USDT balance across spot and perps')
10
+ .action(wrapAction(async () => {
11
+ const creds = requireAuth();
12
+ const spin = spinner('Fetching balances…');
13
+ const [spotRes, perpsRes] = await Promise.all([
14
+ get('/users/pnls/all', { token: creds.accessToken }),
15
+ perpsApi.getAccountSummary(creds.accessToken),
16
+ ]);
17
+ spin.stop();
18
+ const fmt = (n) => `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
19
+ let spotStable = 0;
20
+ if (spotRes.success && Array.isArray(spotRes.data)) {
21
+ for (const t of spotRes.data) {
22
+ const sym = String(t.tokenSymbol ?? '').toLowerCase();
23
+ if (STABLES.has(sym)) {
24
+ const bal = Number(t.balance ?? 0);
25
+ const price = Number(t.marketPrice ?? 1);
26
+ spotStable += bal * price;
27
+ }
28
+ }
29
+ }
30
+ let perpsAvailable = 0;
31
+ if (perpsRes.success && perpsRes.data) {
32
+ const d = perpsRes.data;
33
+ perpsAvailable = Number(d.dispatchableValue ?? 0);
34
+ }
35
+ const total = spotStable + perpsAvailable;
36
+ console.log('');
37
+ console.log(chalk.bold('Balance:'));
38
+ console.log(` Spot (USDC/USDT) : ${fmt(spotStable)}`);
39
+ console.log(` Perps (available) : ${fmt(perpsAvailable)}`);
40
+ console.log(` ${'─'.repeat(30)}`);
41
+ console.log(` Total : ${chalk.bold(fmt(total))}`);
42
+ console.log('');
43
+ }));