minara 0.1.4 → 0.2.0

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** — Balances, deposits, and withdrawals across all supported chains
23
+ - **Trading** — Cross-chain swaps, perpetual futures, limit orders, and copy trading. 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 (accepts ticker or address)
59
+ minara swap -c solana -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,51 @@ 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 assets` | View wallet assets (interactive: spot / perps / both) |
87
+ | `minara assets spot` | View spot wallet balances across all chains |
88
+ | `minara assets perps` | View perps account balance and open positions |
89
+ | `minara deposit` | Show deposit addresses and supported networks |
90
+ | `minara withdraw` | Withdraw tokens to an external wallet |
91
91
 
92
92
  ```bash
93
+ minara assets # Interactive: Spot / Perps / Both
94
+ minara assets spot # Spot wallet across all chains
95
+ minara assets perps # Perps account balance + positions
93
96
  minara deposit
94
- minara withdraw -c solana -t <token> -a 10 --to <address>
95
- minara withdraw # Interactive mode
97
+ minara withdraw -c solana -t '$SOL' -a 10 --to <address>
98
+ minara withdraw # Interactive mode (accepts ticker or address)
96
99
  ```
97
100
 
98
101
  ### Spot Trading
99
102
 
100
- | Command | Description |
101
- |---------|-------------|
102
- | `minara swap` | Swap tokens (cross-chain) |
103
+ | Command | Description |
104
+ | ----------------- | ---------------------------------- |
105
+ | `minara swap` | Swap tokens (cross-chain) |
103
106
  | `minara transfer` | Transfer tokens to another address |
104
107
 
105
108
  ```bash
106
109
  minara swap # Interactive
107
- minara swap -c solana -s buy -t <token> -a 100
110
+ minara swap -c solana -s buy -t '$BONK' -a 100 # By ticker
111
+ minara swap -c solana -s buy -t <address> -a 100 # By contract address
108
112
  minara swap --dry-run # Simulate without executing
109
113
  ```
110
114
 
115
+ > **Token input:** All token fields (`-t`) accept a `$TICKER` (e.g. `$BONK`), a token name, or a contract address. When multiple tokens match, you'll be prompted to select the correct one with full contract addresses displayed.
116
+
111
117
  ### Perpetual Futures
112
118
 
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 |
119
+ | Command | Description |
120
+ | --------------------------- | ----------------------------------------- |
121
+ | `minara perps deposit` | Deposit USDC to Hyperliquid perps account |
122
+ | `minara perps withdraw` | Withdraw USDC from perps account |
123
+ | `minara perps positions` | View all open positions |
124
+ | `minara perps order` | Place an order (interactive builder) |
125
+ | `minara perps cancel` | Cancel open orders |
126
+ | `minara perps leverage` | Update leverage for a symbol |
127
+ | `minara perps trades` | View completed trade history |
128
+ | `minara perps fund-records` | View fund deposit/withdrawal records |
123
129
 
124
130
  ```bash
125
131
  minara perps deposit -a 100 # Deposit 100 USDC to perps
@@ -131,11 +137,11 @@ minara perps leverage # Interactive: set leverage for a trading pai
131
137
 
132
138
  ### Limit Orders
133
139
 
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 |
140
+ | Command | Description |
141
+ | -------------------------------- | ------------------------------------ |
142
+ | `minara limit-order create` | Create a price-triggered limit order |
143
+ | `minara limit-order list` | List all your limit orders |
144
+ | `minara limit-order cancel <id>` | Cancel a specific order by ID |
139
145
 
140
146
  ```bash
141
147
  minara limit-order create # Interactive: token, price, side, amount, expiry
@@ -145,13 +151,13 @@ minara limit-order cancel abc123 # Cancel order by ID
145
151
 
146
152
  ### Copy Trading
147
153
 
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 |
154
+ | Command | Description |
155
+ | ------------------------------- | --------------------------- |
156
+ | `minara copy-trade create` | Create a new copy-trade bot |
157
+ | `minara copy-trade list` | List all copy-trade bots |
158
+ | `minara copy-trade start <id>` | Start a paused bot |
159
+ | `minara copy-trade stop <id>` | Pause a running bot |
160
+ | `minara copy-trade delete <id>` | Delete a bot permanently |
155
161
 
156
162
  ```bash
157
163
  minara copy-trade create # Interactive: target wallet, chain, amount, options
@@ -162,29 +168,52 @@ minara copy-trade stop abc123 # Pause a running bot
162
168
 
163
169
  ### AI Chat
164
170
 
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 |
171
+ | Command | Description |
172
+ | -------------------------------- | --------------------------------------------- |
173
+ | `minara chat` | Enter interactive REPL (Python/Node.js-style) |
174
+ | `minara chat [message]` | Send a single message and exit |
175
+ | `minara chat --list` | List all your conversations |
176
+ | `minara chat --history <chatId>` | View messages in a conversation |
177
+ | `minara chat -c <chatId>` | Continue an existing conversation |
170
178
 
171
179
  ```bash
172
- minara chat "What is the current BTC price?" # Single question, streamed answer
173
180
  minara chat # Enter interactive REPL mode
181
+ minara chat "What is the current BTC price?" # Single question, streamed answer
182
+ minara chat --quality "Analyze ETH outlook" # Quality mode (default: fast)
174
183
  minara chat --thinking "Analyze ETH outlook" # Enable reasoning mode
175
- minara chat --deep-research "DeFi yield trends"# Deep research mode
184
+ minara chat -c <chatId> # Continue a specific chat in REPL
176
185
  minara chat --list # List past conversations
177
186
  minara chat --history <chatId> # Replay a specific conversation
178
187
  ```
179
188
 
189
+ **Interactive REPL mode** — When launched without a message argument, the chat enters an interactive session:
190
+
191
+ ```
192
+ Minara AI Chat session:a1b2c3d4
193
+ ──────────────────────────────────────────────────
194
+ Type a message to chat. /help for commands, Ctrl+C to exit.
195
+
196
+ >>> What's the price of BTC?
197
+ Minara: Bitcoin is currently trading at $95,432...
198
+
199
+ >>> /help
200
+
201
+ Commands:
202
+ /new Start a new conversation
203
+ /continue Continue an existing conversation
204
+ /list List all historical chats
205
+ /id Show current chat ID
206
+ exit Quit the chat
207
+ ```
208
+
180
209
  ### Market Discovery
181
210
 
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 |
211
+ | Command | Description |
212
+ | ---------------------------------- | ---------------------------------------- |
213
+ | `minara discover trending` | View currently trending tokens |
214
+ | `minara discover search <keyword>` | Search for tokens or stocks by name |
215
+ | `minara discover fear-greed` | View the crypto Fear & Greed Index |
216
+ | `minara discover btc-metrics` | View Bitcoin on-chain and market metrics |
188
217
 
189
218
  ```bash
190
219
  minara discover trending # Top trending tokens right now
@@ -193,11 +222,86 @@ minara discover fear-greed # Current market sentiment index
193
222
  minara discover btc-metrics # Bitcoin hashrate, supply, dominance, etc.
194
223
  ```
195
224
 
225
+ ### Premium & Subscription
226
+
227
+ | Command | Description |
228
+ | ---------------------------- | ----------------------------------------------- |
229
+ | `minara premium plans` | View all subscription plans and credit packages |
230
+ | `minara premium status` | View your current subscription status |
231
+ | `minara premium subscribe` | Subscribe or change plan (upgrade / downgrade) |
232
+ | `minara premium buy-credits` | Buy a one-time credit package |
233
+ | `minara premium cancel` | Cancel your current subscription |
234
+
235
+ ```bash
236
+ minara premium plans # Compare Free, Lite, Starter, Pro, Partner plans
237
+ minara premium status # Check your current plan and billing info
238
+ minara premium subscribe # Interactive: select plan → Stripe or Crypto payment
239
+ minara premium buy-credits # Buy additional credits (one-time purchase)
240
+ minara premium cancel # Cancel subscription (keeps access until period ends)
241
+ ```
242
+
243
+ ### Output Format
244
+
245
+ 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:
246
+
247
+ ```bash
248
+ minara discover trending --json # Raw JSON array of trending tokens
249
+ minara discover btc-metrics --json # Full BTC metrics with OHLCV data
250
+ minara assets spot --json # Raw JSON asset list
251
+ ```
252
+
196
253
  ### Configuration
197
254
 
198
- | Command | Description |
199
- |---------|-------------|
200
- | `minara config` | View or update CLI settings (e.g. API base URL) |
255
+ | Command | Description |
256
+ | --------------- | ---------------------------------------------------------------------- |
257
+ | `minara config` | View or update CLI settings (base URL, Touch ID, transaction confirm…) |
258
+
259
+ ```bash
260
+ minara config # Interactive settings menu
261
+ ```
262
+
263
+ Available settings:
264
+
265
+ | Setting | Default | Description |
266
+ | ------------------------ | ------- | ---------------------------------------------------- |
267
+ | Base URL | — | API endpoint |
268
+ | Touch ID | Off | Biometric verification for fund operations (macOS) |
269
+ | Transaction Confirmation | **On** | Mandatory second confirmation before fund operations |
270
+
271
+ ### Transaction Safety
272
+
273
+ All fund-related operations go through a multi-layer safety flow:
274
+
275
+ ```
276
+ 1. First confirmation (skippable with -y flag)
277
+ 2. Transaction confirmation (mandatory — configurable in minara config)
278
+ 3. Touch ID verification (optional — macOS only)
279
+ 4. Execute
280
+ ```
281
+
282
+ The **transaction confirmation** shows the token ticker, name, full contract address, and operation details before asking for final approval:
283
+
284
+ ```
285
+ ⚠ Transaction confirmation
286
+ Token : $BONK — Bonk
287
+ Address : DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263
288
+ Action : BUY swap · 100 USD · solana
289
+ ? Are you sure you want to proceed? (y/N)
290
+ ```
291
+
292
+ 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.
293
+
294
+ ### Touch ID (macOS)
295
+
296
+ 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.
297
+
298
+ ```bash
299
+ minara config # Select "Touch ID" to enable / disable
300
+ ```
301
+
302
+ **Protected operations:** `withdraw`, `transfer`, `swap`, `perps deposit`, `perps withdraw`, `perps order`, `limit-order create`, `copy-trade create`
303
+
304
+ > **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
305
 
202
306
  ## Supported Chains
203
307
 
@@ -235,6 +339,9 @@ npm run test:coverage # With coverage report
235
339
 
236
340
  ## Security
237
341
 
342
+ - **Transaction Confirmation** — Mandatory second confirmation before all fund operations, showing full token details and contract addresses (default: enabled, configurable)
343
+ - **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/`
344
+ - **Token Verification** — Token ticker, name, and full contract address are always displayed before any transaction to prevent wrong-token mistakes
238
345
  - Credentials are stored in `~/.minara/credentials.json` with `0600` file permissions
239
346
  - The `~/.minara/` directory is created with `0700` permissions
240
347
  - Tokens are never logged or printed to the console
@@ -1,4 +1,4 @@
1
- import type { EmailCodeDto, EmailVerifyDto, AuthUser, FavoriteTokensPayload, OAuthProvider } from '../types.js';
1
+ import type { EmailCodeDto, EmailVerifyDto, AuthUser, FavoriteTokensPayload, OAuthProvider, DeviceAuthStartResponse, DeviceAuthStatusResponse } from '../types.js';
2
2
  /** Send email verification code */
3
3
  export declare function sendEmailCode(dto: EmailCodeDto): Promise<import("../types.js").ApiResponse<void>>;
4
4
  /** Verify email code → returns user + access_token */
@@ -28,3 +28,7 @@ export declare function addFavoriteTokens(token: string, payload: FavoriteTokens
28
28
  }>>;
29
29
  /** Get invite history */
30
30
  export declare function getInviteHistory(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
31
+ /** Start device authorization flow */
32
+ export declare function startDeviceAuth(): Promise<import("../types.js").ApiResponse<DeviceAuthStartResponse>>;
33
+ /** Poll device authorization status */
34
+ export declare function getDeviceAuthStatus(deviceCode: string): Promise<import("../types.js").ApiResponse<DeviceAuthStatusResponse>>;
package/dist/api/auth.js CHANGED
@@ -44,3 +44,14 @@ export function addFavoriteTokens(token, payload) {
44
44
  export function getInviteHistory(token) {
45
45
  return get('/auth/invite-history', { token });
46
46
  }
47
+ // ─── Device Authorization Flow (RFC 8628) ─────────────────────────────────
48
+ /** Start device authorization flow */
49
+ export function startDeviceAuth() {
50
+ return post('/auth/device/start');
51
+ }
52
+ /** Poll device authorization status */
53
+ export function getDeviceAuthStatus(deviceCode) {
54
+ return post('/auth/device/status', {
55
+ body: { device_code: deviceCode },
56
+ });
57
+ }
@@ -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
+ }
@@ -11,6 +11,8 @@ export declare function cancelOrders(token: string, dto: PerpsCancelOrdersDto):
11
11
  export declare function modifyOrders(token: string, dto: PerpsCancelOrdersDto): Promise<import("../types.js").ApiResponse<TransactionResult>>;
12
12
  /** Update leverage */
13
13
  export declare function updateLeverage(token: string, dto: UpdateLeverageDto): Promise<import("../types.js").ApiResponse<void>>;
14
+ /** Get perps account state (balance, equity, margin) */
15
+ export declare function getAccountState(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
14
16
  /** Get all positions */
15
17
  export declare function getPositions(token: string): Promise<import("../types.js").ApiResponse<PerpsPosition[]>>;
16
18
  /** Get completed trades */
package/dist/api/perps.js CHANGED
@@ -23,6 +23,10 @@ export function modifyOrders(token, dto) {
23
23
  export function updateLeverage(token, dto) {
24
24
  return post('/v1/tx/perps/update-leverage', { token, body: dto });
25
25
  }
26
+ /** Get perps account state (balance, equity, margin) */
27
+ export function getAccountState(token) {
28
+ return get('/v1/tx/perps/account-state', { token });
29
+ }
26
30
  /** Get all positions */
27
31
  export function getPositions(token) {
28
32
  return get('/v1/tx/perps/positions/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,103 @@
1
1
  import { Command } from 'commander';
2
+ import { select } from '@inquirer/prompts';
2
3
  import chalk from 'chalk';
3
4
  import { getAssets } from '../api/crosschain.js';
5
+ import * as perpsApi from '../api/perps.js';
4
6
  import { requireAuth } from '../config.js';
5
7
  import { spinner, unwrapApi, wrapAction } from '../utils.js';
6
- export const assetsCommand = new Command('assets')
7
- .description('View your wallet assets across chains')
8
+ import { printKV, printTable, ASSET_COLUMNS, POSITION_COLUMNS } from '../formatters.js';
9
+ // ─── spot ────────────────────────────────────────────────────────────────
10
+ const spotCmd = new Command('spot')
11
+ .description('View spot wallet assets across chains')
8
12
  .action(wrapAction(async () => {
9
13
  const creds = requireAuth();
10
- const spin = spinner('Fetching assets…');
11
- const res = await getAssets(creds.accessToken);
14
+ await showSpotAssets(creds.accessToken);
15
+ }));
16
+ async function showSpotAssets(token) {
17
+ const spin = spinner('Fetching spot assets…');
18
+ const res = await getAssets(token);
12
19
  spin.stop();
13
- const data = unwrapApi(res, 'Failed to fetch assets');
20
+ const data = unwrapApi(res, 'Failed to fetch spot assets');
14
21
  if (Array.isArray(data) && data.length === 0) {
15
- console.log(chalk.dim('No assets found.'));
22
+ console.log(chalk.dim('No spot assets found.'));
16
23
  return;
17
24
  }
18
25
  console.log('');
19
- console.log(chalk.bold('Your Assets:'));
20
- console.log(JSON.stringify(data, null, 2));
26
+ console.log(chalk.bold('Spot Wallet Assets:'));
27
+ if (Array.isArray(data)) {
28
+ printTable(data, ASSET_COLUMNS);
29
+ }
30
+ else {
31
+ printKV(data);
32
+ }
33
+ console.log('');
34
+ }
35
+ // ─── perps ───────────────────────────────────────────────────────────────
36
+ const perpsCmd = new Command('perps')
37
+ .description('View perps account balance and positions')
38
+ .action(wrapAction(async () => {
39
+ const creds = requireAuth();
40
+ await showPerpsAssets(creds.accessToken);
41
+ }));
42
+ async function showPerpsAssets(token) {
43
+ // Fetch account state and positions in parallel
44
+ const spin = spinner('Fetching perps account…');
45
+ const [accountRes, positionsRes] = await Promise.all([
46
+ perpsApi.getAccountState(token),
47
+ perpsApi.getPositions(token),
48
+ ]);
49
+ spin.stop();
50
+ // ── Account state ───────────────────────────────────────────────────
51
+ console.log('');
52
+ console.log(chalk.bold('Perps Account:'));
53
+ if (accountRes.success && accountRes.data) {
54
+ printKV(accountRes.data);
55
+ }
56
+ else {
57
+ console.log(chalk.dim(' Could not fetch account state.'));
58
+ if (accountRes.error?.message) {
59
+ console.log(chalk.dim(` ${accountRes.error.message}`));
60
+ }
61
+ }
62
+ // ── Positions ───────────────────────────────────────────────────────
63
+ console.log('');
64
+ console.log(chalk.bold('Open Positions:'));
65
+ if (!positionsRes.success) {
66
+ console.log(chalk.dim(' Could not fetch positions.'));
67
+ if (positionsRes.error?.message) {
68
+ console.log(chalk.dim(` ${positionsRes.error.message}`));
69
+ }
70
+ }
71
+ else {
72
+ const positions = positionsRes.data;
73
+ if (!positions || (Array.isArray(positions) && positions.length === 0)) {
74
+ console.log(chalk.dim(' No open positions.'));
75
+ }
76
+ else {
77
+ printTable(positions, POSITION_COLUMNS);
78
+ }
79
+ }
21
80
  console.log('');
81
+ }
82
+ // ─── parent ──────────────────────────────────────────────────────────────
83
+ export const assetsCommand = new Command('assets')
84
+ .description('View your wallet assets (spot & perps)')
85
+ .addCommand(spotCmd)
86
+ .addCommand(perpsCmd)
87
+ .action(wrapAction(async () => {
88
+ const action = await select({
89
+ message: 'View assets:',
90
+ choices: [
91
+ { name: 'Spot wallet', value: 'spot' },
92
+ { name: 'Perps account', value: 'perps' },
93
+ { name: 'Both', value: 'both' },
94
+ ],
95
+ });
96
+ const creds = requireAuth();
97
+ if (action === 'spot' || action === 'both') {
98
+ await showSpotAssets(creds.accessToken);
99
+ }
100
+ if (action === 'perps' || action === 'both') {
101
+ await showPerpsAssets(creds.accessToken);
102
+ }
22
103
  }));