minara 0.2.0 → 0.2.2

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.
@@ -124,6 +124,7 @@ const statusCmd = new Command('status')
124
124
  spin.stop();
125
125
  assertApiOk(userRes, 'Failed to fetch account info');
126
126
  const user = userRes.data;
127
+ const plans = plansRes.success && plansRes.data ? plansRes.data.plans : [];
127
128
  if (isRawJson()) {
128
129
  console.log(JSON.stringify({
129
130
  subscription: user.subscription ?? null,
@@ -134,18 +135,37 @@ const statusCmd = new Command('status')
134
135
  console.log('');
135
136
  console.log(chalk.bold('Subscription Status:'));
136
137
  console.log('');
137
- // Try to extract subscription info from various possible response shapes
138
138
  const sub = user.subscription;
139
139
  const userPlan = user.plan;
140
140
  if (sub && Object.keys(sub).length > 0) {
141
141
  const planName = sub.planName ?? sub.plan ?? sub.name ?? '—';
142
- const status = sub.status ?? '—';
143
- const interval = sub.interval ?? '—';
142
+ const planId = sub.planId ?? sub.id;
143
+ // Cross-reference with plans list for interval and credits
144
+ const matchedPlan = planId != null
145
+ ? plans.find((p) => p.id === Number(planId) || p._id === String(planId))
146
+ : plans.find((p) => p.name === planName);
147
+ const status = sub.status;
148
+ const interval = sub.interval ?? matchedPlan?.interval;
144
149
  const cancelAt = sub.cancelAtPeriodEnd;
145
150
  const periodEnd = sub.currentPeriodEnd;
146
151
  console.log(` ${chalk.dim('Plan'.padEnd(16))} : ${chalk.bold(String(planName))}`);
147
- console.log(` ${chalk.dim('Status'.padEnd(16))} : ${status === 'active' ? chalk.green('Active') : chalk.yellow(String(status))}`);
148
- console.log(` ${chalk.dim('Billing'.padEnd(16))} : ${String(interval)}`);
152
+ if (status) {
153
+ console.log(` ${chalk.dim('Status'.padEnd(16))} : ${status === 'active' ? chalk.green('Active') : chalk.yellow(status)}`);
154
+ }
155
+ else {
156
+ console.log(` ${chalk.dim('Status'.padEnd(16))} : ${chalk.green('Active')}`);
157
+ }
158
+ if (interval) {
159
+ const label = interval === 'month' ? 'Monthly' : interval === 'year' ? 'Yearly' : String(interval);
160
+ console.log(` ${chalk.dim('Billing'.padEnd(16))} : ${label}`);
161
+ }
162
+ if (matchedPlan) {
163
+ console.log(` ${chalk.dim('Price'.padEnd(16))} : ${formatPrice(matchedPlan)}`);
164
+ const credits = formatCredits(matchedPlan.rules);
165
+ if (credits !== '—') {
166
+ console.log(` ${chalk.dim('Credits'.padEnd(16))} : ${credits}`);
167
+ }
168
+ }
149
169
  if (periodEnd) {
150
170
  console.log(` ${chalk.dim('Renews On'.padEnd(16))} : ${new Date(periodEnd).toLocaleDateString()}`);
151
171
  }
@@ -157,7 +177,6 @@ const statusCmd = new Command('status')
157
177
  printKV(userPlan);
158
178
  }
159
179
  else {
160
- // No subscription info found — assume free plan
161
180
  console.log(` ${chalk.dim('Plan'.padEnd(16))} : ${chalk.bold('Free')}`);
162
181
  console.log(` ${chalk.dim('Status'.padEnd(16))} : ${chalk.green('Active')}`);
163
182
  console.log('');
@@ -1,14 +1,14 @@
1
1
  import { Command } from 'commander';
2
- import { input, select, confirm } from '@inquirer/prompts';
2
+ import { input, select } from '@inquirer/prompts';
3
3
  import chalk from 'chalk';
4
- import { swap, swapsSimulate } from '../api/crosschain.js';
4
+ import { swaps, swapsSimulate } from '../api/crosschain.js';
5
+ import { get } from '../api/client.js';
5
6
  import { requireAuth } from '../config.js';
6
- import { success, info, spinner, formatOrderSide, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
7
+ import { success, info, warn, spinner, assertApiOk, wrapAction, requireTransactionConfirmation, lookupToken, normalizeChain } from '../utils.js';
7
8
  import { requireTouchId } from '../touchid.js';
8
9
  import { printTxResult, printKV } from '../formatters.js';
9
10
  export const swapCommand = new Command('swap')
10
11
  .description('Swap tokens (cross-chain spot trading)')
11
- .option('-c, --chain <chain>', 'Blockchain (e.g. solana, base, ethereum)')
12
12
  .option('-s, --side <side>', 'buy or sell')
13
13
  .option('-t, --token <address|ticker>', 'Token contract address or ticker symbol')
14
14
  .option('-a, --amount <amount>', 'USD amount (buy) or token amount (sell)')
@@ -16,9 +16,7 @@ export const swapCommand = new Command('swap')
16
16
  .option('--dry-run', 'Simulate without executing')
17
17
  .action(wrapAction(async (opts) => {
18
18
  const creds = requireAuth();
19
- // ── 1. Chain ─────────────────────────────────────────────────────────
20
- const chain = opts.chain ?? await selectChain();
21
- // ── 2. Side ──────────────────────────────────────────────────────────
19
+ // ── 1. Side ──────────────────────────────────────────────────────────
22
20
  let side = opts.side;
23
21
  if (!side) {
24
22
  side = await select({
@@ -29,31 +27,60 @@ export const swapCommand = new Command('swap')
29
27
  ],
30
28
  });
31
29
  }
32
- // ── 3. Token ───────────────────────────────────────────────────────────
30
+ // ── 2. Token ───────────────────────────────────────────────────────────
33
31
  const tokenInput = opts.token ?? await input({
34
32
  message: 'Token (contract address or ticker):',
35
33
  validate: (v) => (v.length > 0 ? true : 'Please enter a token address or ticker'),
36
34
  });
37
35
  const tokenInfo = await lookupToken(tokenInput);
36
+ // ── 3. Chain (derived from token) ────────────────────────────────────
37
+ const chain = normalizeChain(tokenInfo.chain);
38
+ if (!chain) {
39
+ warn(`Unable to determine chain for token. Raw chain value: ${tokenInfo.chain ?? 'unknown'}`);
40
+ return;
41
+ }
38
42
  // ── 4. Amount ────────────────────────────────────────────────────────
39
- const amountLabel = side === 'buy' ? 'USD amount to spend' : 'Token amount to sell';
40
- const amount = opts.amount ?? await input({
43
+ let maxBalance;
44
+ if (side === 'sell') {
45
+ const pnlRes = await get('/users/pnls/all', { token: creds.accessToken });
46
+ if (pnlRes.success && Array.isArray(pnlRes.data)) {
47
+ const match = pnlRes.data.find((t) => {
48
+ const addr = String(t.tokenAddress ?? '').toLowerCase();
49
+ const cid = String(t.chainId ?? '').toLowerCase();
50
+ const targetChain = chain.toLowerCase();
51
+ return addr === tokenInfo.address.toLowerCase() && cid === targetChain;
52
+ });
53
+ if (match) {
54
+ maxBalance = Number(match.balance ?? 0);
55
+ info(`Available balance: ${chalk.bold(String(maxBalance))} ${tokenInfo.symbol ?? ''}`);
56
+ }
57
+ }
58
+ }
59
+ const amountLabel = side === 'buy' ? 'USD amount to spend' : `Token amount to sell${maxBalance ? ' ("all" for max)' : ''}`;
60
+ let amount = opts.amount ?? await input({
41
61
  message: `${amountLabel}:`,
42
62
  validate: (v) => {
63
+ if (side === 'sell' && v.toLowerCase() === 'all')
64
+ return true;
43
65
  const n = parseFloat(v);
44
66
  return (isNaN(n) || n <= 0) ? 'Enter a valid positive number' : true;
45
67
  },
46
68
  });
47
- // ── 5. Summary ───────────────────────────────────────────────────────
48
- console.log('');
49
- console.log(chalk.bold('Swap Summary:'));
50
- console.log(` Chain : ${chalk.cyan(chain)}`);
51
- console.log(` Action : ${formatOrderSide(side)}`);
52
- console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
53
- console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
54
- console.log(` Amount : ${chalk.bold(amount)} ${side === 'buy' ? 'USD' : '(token)'}`);
55
- console.log('');
56
- // ── 6. Dry run ───────────────────────────────────────────────────────
69
+ if (side === 'sell') {
70
+ if (amount.toLowerCase() === 'all') {
71
+ if (!maxBalance || maxBalance <= 0) {
72
+ warn('Could not determine balance. Please enter an amount manually.');
73
+ return;
74
+ }
75
+ amount = String(maxBalance);
76
+ info(`Selling all: ${chalk.bold(amount)} ${tokenInfo.symbol ?? ''}`);
77
+ }
78
+ else if (maxBalance && parseFloat(amount) > maxBalance) {
79
+ info(`Amount exceeds balance (${maxBalance}), using max balance`);
80
+ amount = String(maxBalance);
81
+ }
82
+ }
83
+ // ── 5. Dry run ───────────────────────────────────────────────────────
57
84
  if (opts.dryRun) {
58
85
  info('Simulating swap (dry-run)…');
59
86
  const spin = spinner('Simulating…');
@@ -75,27 +102,24 @@ export const swapCommand = new Command('swap')
75
102
  }
76
103
  return;
77
104
  }
78
- // ── 7. Confirm ───────────────────────────────────────────────────────
105
+ // ── 7. Confirm & Touch ID ──────────────────────────────────────────
79
106
  if (!opts.yes) {
80
- const confirmed = await confirm({
81
- message: `Confirm ${side.toUpperCase()} swap?`,
82
- default: false,
83
- });
84
- if (!confirmed) {
85
- console.log(chalk.dim('Swap cancelled.'));
86
- return;
87
- }
107
+ await requireTransactionConfirmation(`${side.toUpperCase()} swap · ${amount} ${side === 'buy' ? 'USD' : 'tokens'} · ${chain}`, tokenInfo, { chain, side, amount: `${amount} ${side === 'buy' ? 'USD' : '(token)'}` });
88
108
  }
89
- // ── 8. Transaction confirmation & Touch ID ────────────────────────────
90
- await requireTransactionConfirmation(`${side.toUpperCase()} swap · ${amount} ${side === 'buy' ? 'USD' : 'tokens'} · ${chain}`, tokenInfo);
91
109
  await requireTouchId();
92
110
  // ── 9. Execute ───────────────────────────────────────────────────────
93
111
  const spin = spinner('Executing swap…');
94
- const res = await swap(creds.accessToken, {
95
- chain, side, tokenAddress: tokenInfo.address, buyUsdAmountOrSellTokenAmount: amount,
96
- });
112
+ const res = await swaps(creds.accessToken, [{
113
+ chain, side, tokenAddress: tokenInfo.address, buyUsdAmountOrSellTokenAmount: amount,
114
+ }]);
97
115
  spin.stop();
98
116
  assertApiOk(res, 'Swap failed');
99
117
  success('Swap submitted!');
100
- printTxResult(res.data);
118
+ if (Array.isArray(res.data)) {
119
+ for (const tx of res.data)
120
+ printTxResult(tx);
121
+ }
122
+ else {
123
+ printTxResult(res.data);
124
+ }
101
125
  }));
@@ -1,9 +1,8 @@
1
1
  import { Command } from 'commander';
2
- import { input, confirm } from '@inquirer/prompts';
3
- import chalk from 'chalk';
2
+ import { input } from '@inquirer/prompts';
4
3
  import { transfer } from '../api/crosschain.js';
5
4
  import { requireAuth } from '../config.js';
6
- import { success, warn, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
5
+ import { success, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken } from '../utils.js';
7
6
  import { requireTouchId } from '../touchid.js';
8
7
  import { printTxResult } from '../formatters.js';
9
8
  export const transferCommand = new Command('transfer')
@@ -36,25 +35,10 @@ export const transferCommand = new Command('transfer')
36
35
  message: 'Recipient address:',
37
36
  validate: (v) => (v.length > 5 ? true : 'Enter a valid address'),
38
37
  });
39
- // ── 5. Summary ───────────────────────────────────────────────────────
40
- console.log('');
41
- console.log(chalk.bold.red('⚠ Transfer Summary:'));
42
- console.log(` Chain : ${chalk.cyan(chain)}`);
43
- console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
44
- console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
45
- console.log(` Amount : ${chalk.bold(amount)}`);
46
- console.log(` To : ${chalk.yellow(recipient)}`);
47
- console.log('');
48
- warn('Transfers cannot be reversed. Double-check the recipient address!');
38
+ // ── 5. Confirm & Touch ID ──────────────────────────────────────────
49
39
  if (!opts.yes) {
50
- const confirmed = await confirm({ message: 'Confirm transfer?', default: false });
51
- if (!confirmed) {
52
- console.log(chalk.dim('Transfer cancelled.'));
53
- return;
54
- }
40
+ await requireTransactionConfirmation(`Transfer ${amount} ${recipient} · ${chain}`, tokenInfo, { chain, amount, destination: recipient });
55
41
  }
56
- // ── 6. Transaction confirmation & Touch ID ────────────────────────────
57
- await requireTransactionConfirmation(`Transfer ${amount} tokens → ${recipient} · ${chain}`, tokenInfo);
58
42
  await requireTouchId();
59
43
  // ── 7. Execute ───────────────────────────────────────────────────────
60
44
  const spin = spinner('Processing transfer…');
@@ -1,9 +1,9 @@
1
1
  import { Command } from 'commander';
2
- import { input, confirm } from '@inquirer/prompts';
2
+ import { input } from '@inquirer/prompts';
3
3
  import chalk from 'chalk';
4
4
  import { transfer, getAssets } from '../api/crosschain.js';
5
5
  import { requireAuth } from '../config.js';
6
- import { success, warn, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
6
+ import { success, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken } from '../utils.js';
7
7
  import { requireTouchId } from '../touchid.js';
8
8
  import { printTxResult } from '../formatters.js';
9
9
  export const withdrawCommand = new Command('withdraw')
@@ -59,30 +59,10 @@ export const withdrawCommand = new Command('withdraw')
59
59
  message: 'Destination address (your external wallet):',
60
60
  validate: (v) => (v.length > 5 ? true : 'Enter a valid address'),
61
61
  });
62
- // ── 6. Summary ───────────────────────────────────────────────────────
63
- console.log('');
64
- console.log(chalk.bold.red('⚠ Withdrawal Summary'));
65
- console.log(` Chain : ${chalk.cyan(chain)}`);
66
- console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
67
- console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
68
- console.log(` Amount : ${chalk.bold(amount)}`);
69
- console.log(` Destination : ${chalk.yellow(recipient)}`);
70
- console.log('');
71
- warn('Withdrawals are irreversible. Please double-check the network and address!');
72
- warn('Sending to the wrong chain or address will result in permanent loss of funds.');
73
- console.log('');
62
+ // ── 6. Confirm & Touch ID ──────────────────────────────────────────
74
63
  if (!opts.yes) {
75
- const confirmed = await confirm({
76
- message: 'I have verified the address and network. Proceed with withdrawal?',
77
- default: false,
78
- });
79
- if (!confirmed) {
80
- console.log(chalk.dim('Withdrawal cancelled.'));
81
- return;
82
- }
64
+ await requireTransactionConfirmation(`Withdraw ${amount} ${recipient} · ${chain}`, tokenInfo, { chain, amount, destination: recipient });
83
65
  }
84
- // ── 7. Transaction confirmation & Touch ID ────────────────────────────
85
- await requireTransactionConfirmation(`Withdraw ${amount} tokens → ${recipient} · ${chain}`, tokenInfo);
86
66
  await requireTouchId();
87
67
  // ── 8. Execute ───────────────────────────────────────────────────────
88
68
  const spin = spinner('Processing withdrawal…');
@@ -37,13 +37,11 @@ export declare function printTable(data: Record<string, unknown>[] | object[], c
37
37
  */
38
38
  export declare function printTxResult(data: unknown): void;
39
39
  /** Spot wallet assets (WalletAsset[]) */
40
- export declare const ASSET_COLUMNS: ColumnDef[];
41
- /** Perps positions (PerpsPosition[]) */
40
+ export declare const SPOT_COLUMNS: ColumnDef[];
41
+ /** Perps positions API uses snake_case field names */
42
42
  export declare const POSITION_COLUMNS: ColumnDef[];
43
43
  /** Limit orders (LimitOrderInfo[]) */
44
44
  export declare const LIMIT_ORDER_COLUMNS: ColumnDef[];
45
- /** Copy trades (CopyTradeInfo[]) */
46
- export declare const COPY_TRADE_COLUMNS: ColumnDef[];
47
45
  /** Trending / search tokens (TokenInfo[]) */
48
46
  export declare const TOKEN_COLUMNS: ColumnDef[];
49
47
  /**
@@ -55,6 +55,9 @@ export function formatValue(value, key) {
55
55
  return value.toLocaleString('en-US', { maximumFractionDigits: 6 });
56
56
  }
57
57
  if (typeof value === 'string') {
58
+ // Hex addresses — must check before numeric coercion (0x… is valid Number)
59
+ if (/^0x[0-9a-fA-F]{20,}$/.test(value))
60
+ return chalk.yellow(value);
58
61
  // Numeric string that looks like a price / amount
59
62
  const num = Number(value);
60
63
  if (!isNaN(num) && value.trim() !== '') {
@@ -64,9 +67,6 @@ export function formatValue(value, key) {
64
67
  if (/^\d{4}-\d{2}-\d{2}T/.test(value)) {
65
68
  return new Date(value).toLocaleString();
66
69
  }
67
- // Hex addresses
68
- if (/^0x[0-9a-fA-F]{20,}$/.test(value))
69
- return chalk.yellow(value);
70
70
  // URLs
71
71
  if (value.startsWith('http'))
72
72
  return chalk.cyan.underline(value);
@@ -171,10 +171,14 @@ export function printTable(data, columns) {
171
171
  console.log(JSON.stringify(data, null, 2));
172
172
  return;
173
173
  }
174
- if (!data || data.length === 0) {
174
+ if (!data || (Array.isArray(data) && data.length === 0)) {
175
175
  console.log(chalk.dim(' No data.'));
176
176
  return;
177
177
  }
178
+ if (!Array.isArray(data)) {
179
+ printKV(data);
180
+ return;
181
+ }
178
182
  const rows = data;
179
183
  const cols = columns ?? autoColumns(rows);
180
184
  if (cols.length === 0) {
@@ -228,14 +232,27 @@ export function printTxResult(data) {
228
232
  // Pre-built column configs for known data types
229
233
  // ═══════════════════════════════════════════════════════════════════════════
230
234
  /** Spot wallet assets (WalletAsset[]) */
231
- export const ASSET_COLUMNS = [
232
- { key: 'symbol', label: 'Symbol', format: (v, row) => chalk.bold(String(v ?? row.tokenSymbol ?? '—')) },
233
- { key: 'balance', label: 'Balance', format: (v, row) => String(v ?? row.amount ?? '—') },
234
- { key: 'chain', label: 'Chain', format: (v, row) => chalk.cyan(String(v ?? row.chainName ?? '—')) },
235
- { key: 'usdValue', label: 'USD Value', format: (v) => v ? `$${Number(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : chalk.dim('—') },
236
- { key: 'tokenAddress', label: 'Token Address', format: (v) => v ? chalk.dim(truncate(String(v), 16)) : chalk.dim('—') },
235
+ export const SPOT_COLUMNS = [
236
+ { key: 'tokenSymbol', label: 'Token', format: (v) => chalk.bold(String(v ?? '—')) },
237
+ { key: 'chainId', label: 'Chain', format: (v) => {
238
+ const s = String(v ?? '—');
239
+ return chalk.cyan(s.charAt(0).toUpperCase() + s.slice(1));
240
+ } },
241
+ { key: 'balance', label: 'Balance' },
242
+ { key: 'marketPrice', label: 'Price', format: (v) => v ? `$${Number(v).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 6 })}` : chalk.dim('—') },
243
+ { key: '_value', label: 'Value', format: (v) => {
244
+ const n = Number(v ?? 0);
245
+ return n > 0 ? `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : chalk.dim('—');
246
+ } },
247
+ { key: 'unrealizedPnl', label: 'PnL', format: (v) => {
248
+ const n = Number(v ?? 0);
249
+ if (n === 0)
250
+ return chalk.dim('—');
251
+ const color = n >= 0 ? chalk.green : chalk.red;
252
+ return color(`${n >= 0 ? '+' : ''}$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`);
253
+ } },
237
254
  ];
238
- /** Perps positions (PerpsPosition[]) */
255
+ /** Perps positions API uses snake_case field names */
239
256
  export const POSITION_COLUMNS = [
240
257
  { key: 'symbol', label: 'Symbol', format: (v) => chalk.bold(String(v ?? '—')) },
241
258
  { key: 'side', label: 'Side', format: (v) => {
@@ -244,8 +261,8 @@ export const POSITION_COLUMNS = [
244
261
  } },
245
262
  { key: 'size', label: 'Size' },
246
263
  { key: 'entryPrice', label: 'Entry', format: (v) => formatValue(v, 'price') },
247
- { key: 'markPrice', label: 'Mark', format: (v) => formatValue(v, 'price') },
248
- { key: 'pnl', label: 'PnL', format: (v) => {
264
+ { key: 'positionValue', label: 'Value', format: (v) => formatValue(v, 'price') },
265
+ { key: 'unrealizedPnl', label: 'PnL', format: (v) => {
249
266
  if (!v && v !== 0)
250
267
  return chalk.dim('—');
251
268
  const n = Number(v);
@@ -253,6 +270,7 @@ export const POSITION_COLUMNS = [
253
270
  return color(`${n >= 0 ? '+' : ''}$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`);
254
271
  } },
255
272
  { key: 'leverage', label: 'Lev', format: (v) => v ? `${v}x` : chalk.dim('—') },
273
+ { key: 'marginUsed', label: 'Margin', format: (v) => formatValue(v, 'price') },
256
274
  ];
257
275
  /** Limit orders (LimitOrderInfo[]) */
258
276
  export const LIMIT_ORDER_COLUMNS = [
@@ -267,16 +285,6 @@ export const LIMIT_ORDER_COLUMNS = [
267
285
  { key: 'amount', label: 'Amount', format: (v) => v ? `$${v}` : chalk.dim('—') },
268
286
  { key: 'status', label: 'Status', format: (v) => formatValue(v, 'status') },
269
287
  ];
270
- /** Copy trades (CopyTradeInfo[]) */
271
- export const COPY_TRADE_COLUMNS = [
272
- { key: 'id', label: 'ID', format: (v) => chalk.dim(truncate(String(v ?? ''), 12)) },
273
- { key: 'name', label: 'Name', format: (v) => String(v ?? chalk.dim('—')) },
274
- { key: 'chain', label: 'Chain', format: (v) => chalk.cyan(String(v ?? '—')) },
275
- { key: 'targetAddress', label: 'Target', format: (v) => v ? chalk.yellow(truncate(String(v), 14)) : chalk.dim('—') },
276
- { key: 'fixedAmount', label: 'Amount', format: (v) => v ? `$${v}` : chalk.dim('—') },
277
- { key: 'copySell', label: 'Copy Sell', format: (v) => v ? chalk.green('Yes') : chalk.dim('No') },
278
- { key: 'status', label: 'Status', format: (v) => formatValue(v, 'status') },
279
- ];
280
288
  /** Format large numbers as $1.23B / $456.78M / $12.3K */
281
289
  function compactUsd(v) {
282
290
  if (!v && v !== 0)
package/dist/index.js CHANGED
@@ -8,13 +8,13 @@ const { version } = require('../package.json');
8
8
  import { logoutCommand } from './commands/logout.js';
9
9
  import { accountCommand } from './commands/account.js';
10
10
  import { assetsCommand } from './commands/assets.js';
11
+ import { balanceCommand } from './commands/balance.js';
11
12
  import { depositCommand } from './commands/deposit.js';
12
13
  import { withdrawCommand } from './commands/withdraw.js';
13
14
  import { swapCommand } from './commands/swap.js';
14
15
  import { transferCommand } from './commands/transfer.js';
15
16
  import { perpsCommand } from './commands/perps.js';
16
17
  import { limitOrderCommand } from './commands/limit-order.js';
17
- import { copyTradeCommand } from './commands/copy-trade.js';
18
18
  import { chatCommand } from './commands/chat.js';
19
19
  import { discoverCommand } from './commands/discover.js';
20
20
  import { configCommand } from './commands/config.js';
@@ -27,7 +27,7 @@ program
27
27
  .option('--json', 'Output raw JSON instead of formatted tables')
28
28
  .description(chalk.bold('Minara CLI') +
29
29
  ' — Your AI-powered digital finance assistant in the terminal.\n\n' +
30
- ' Login, swap, trade perps, copy-trade, and chat with Minara AI.')
30
+ ' Login, swap, trade perps, and chat with Minara AI.')
31
31
  .hook('preAction', (thisCommand) => {
32
32
  const opts = thisCommand.optsWithGlobals();
33
33
  if (opts.json)
@@ -38,6 +38,7 @@ program.addCommand(loginCommand);
38
38
  program.addCommand(logoutCommand);
39
39
  program.addCommand(accountCommand);
40
40
  // ── Wallet & Funds ───────────────────────────────────────────────────────
41
+ program.addCommand(balanceCommand);
41
42
  program.addCommand(assetsCommand);
42
43
  program.addCommand(depositCommand);
43
44
  program.addCommand(withdrawCommand);
@@ -48,7 +49,6 @@ program.addCommand(transferCommand);
48
49
  program.addCommand(perpsCommand);
49
50
  // ── Bots ─────────────────────────────────────────────────────────────────
50
51
  program.addCommand(limitOrderCommand);
51
- program.addCommand(copyTradeCommand);
52
52
  // ── AI Chat ──────────────────────────────────────────────────────────────
53
53
  program.addCommand(chatCommand);
54
54
  // ── Market ───────────────────────────────────────────────────────────────
package/dist/types.d.ts CHANGED
@@ -256,41 +256,6 @@ export interface UpdateLimitOrderDto {
256
256
  targetPrice?: number;
257
257
  expiredAt?: number;
258
258
  }
259
- export interface CreateCopyTradeDto {
260
- targetAddress: string;
261
- chain: string;
262
- name?: string;
263
- mode?: 'fixedAmount';
264
- copySell?: boolean;
265
- copySellSamePercentage?: boolean;
266
- copySellQuitPercentage?: number;
267
- fixedAmount?: number;
268
- status?: 'running' | 'paused';
269
- expiredAt?: number;
270
- }
271
- export interface CopyTradeInfo {
272
- id: string;
273
- name?: string;
274
- targetAddress: string;
275
- chain: string;
276
- mode?: string;
277
- fixedAmount?: number;
278
- copySell?: boolean;
279
- status?: string;
280
- createdAt?: string;
281
- }
282
- export interface UpdateCopyTradeDto {
283
- chain: string;
284
- name?: string;
285
- mode?: 'fixedAmount';
286
- copySell?: boolean;
287
- copySellSamePercentage?: boolean;
288
- copySellQuitPercentage?: number;
289
- fixedAmount?: number;
290
- targetAddress?: string;
291
- status?: 'running' | 'paused';
292
- expiredAt?: number;
293
- }
294
259
  export interface UserTradeConfig {
295
260
  slippage?: string;
296
261
  priorityFee?: string;
package/dist/utils.d.ts CHANGED
@@ -34,7 +34,13 @@ export interface TokenDisplayInfo {
34
34
  symbol?: string;
35
35
  name?: string;
36
36
  address: string;
37
+ chain?: string;
37
38
  }
39
+ /**
40
+ * Normalize a chain identifier from the token search API to a supported
41
+ * `Chain` value used by the swap / transfer APIs.
42
+ */
43
+ export declare function normalizeChain(raw?: string): Chain | undefined;
38
44
  /**
39
45
  * Look up token metadata by address, ticker, or name.
40
46
  *
@@ -63,6 +69,11 @@ export declare function formatTokenLabel(token: TokenDisplayInfo): string;
63
69
  *
64
70
  * Exits the process if the user declines.
65
71
  */
66
- export declare function requireTransactionConfirmation(description: string, token?: TokenDisplayInfo): Promise<void>;
72
+ export declare function requireTransactionConfirmation(description: string, token?: TokenDisplayInfo, details?: {
73
+ chain?: string;
74
+ side?: string;
75
+ amount?: string;
76
+ destination?: string;
77
+ }): Promise<void>;
67
78
  /** Open a URL in the user's default browser (cross-platform). */
68
79
  export declare function openBrowser(url: string): void;