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.
- package/README.md +33 -41
- package/dist/api/perps.d.ts +2 -2
- package/dist/api/perps.js +4 -4
- package/dist/commands/assets.js +77 -54
- package/dist/commands/balance.d.ts +2 -0
- package/dist/commands/balance.js +43 -0
- package/dist/commands/deposit.js +129 -62
- package/dist/commands/limit-order.js +1 -1
- package/dist/commands/premium.js +25 -6
- package/dist/commands/swap.js +59 -35
- package/dist/commands/transfer.js +4 -20
- package/dist/commands/withdraw.js +4 -24
- package/dist/formatters.d.ts +2 -4
- package/dist/formatters.js +31 -23
- package/dist/index.js +3 -3
- package/dist/types.d.ts +0 -35
- package/dist/utils.d.ts +12 -1
- package/dist/utils.js +129 -9
- package/package.json +1 -1
package/dist/commands/premium.js
CHANGED
|
@@ -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
|
|
143
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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('');
|
package/dist/commands/swap.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { input, select
|
|
2
|
+
import { input, select } from '@inquirer/prompts';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import {
|
|
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,
|
|
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.
|
|
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
|
-
// ──
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
|
95
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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…');
|
package/dist/formatters.d.ts
CHANGED
|
@@ -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
|
|
41
|
-
/** Perps positions
|
|
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
|
/**
|
package/dist/formatters.js
CHANGED
|
@@ -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
|
|
232
|
-
{ key: '
|
|
233
|
-
{ key: '
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
|
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: '
|
|
248
|
-
{ key: '
|
|
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,
|
|
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
|
|
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;
|