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 +21 -0
- package/README.md +176 -77
- package/dist/api/payment.d.ts +17 -0
- package/dist/api/payment.js +39 -0
- package/dist/api/perps.d.ts +2 -0
- package/dist/api/perps.js +4 -0
- package/dist/api/tokens.d.ts +4 -0
- package/dist/api/tokens.js +8 -0
- package/dist/commands/assets.js +115 -11
- package/dist/commands/balance.d.ts +2 -0
- package/dist/commands/balance.js +43 -0
- package/dist/commands/chat.js +77 -53
- package/dist/commands/config.js +82 -5
- package/dist/commands/copy-trade.js +10 -4
- package/dist/commands/deposit.js +134 -59
- package/dist/commands/discover.js +31 -4
- package/dist/commands/limit-order.js +16 -8
- package/dist/commands/login.js +17 -1
- package/dist/commands/perps.js +48 -13
- package/dist/commands/premium.d.ts +2 -0
- package/dist/commands/premium.js +417 -0
- package/dist/commands/swap.js +80 -22
- package/dist/commands/transfer.js +17 -11
- package/dist/commands/withdraw.js +17 -11
- package/dist/config.d.ts +2 -0
- package/dist/config.js +1 -0
- package/dist/formatters.d.ts +54 -0
- package/dist/formatters.js +384 -0
- package/dist/index.js +13 -3
- package/dist/touchid.d.ts +18 -0
- package/dist/touchid.js +181 -0
- package/dist/types.d.ts +55 -41
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +224 -1
- package/package.json +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { input, select } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
3
4
|
import { searchTokens, getTrendingTokens, searchStocks, getFearGreedIndex, getBitcoinMetrics } from '../api/tokens.js';
|
|
4
5
|
import { spinner, assertApiOk, wrapAction } from '../utils.js';
|
|
6
|
+
import { printKV, printTable, printFearGreed, printCryptoMetrics, TOKEN_COLUMNS } from '../formatters.js';
|
|
5
7
|
// ─── trending ────────────────────────────────────────────────────────────
|
|
6
8
|
const trendingCmd = new Command('trending')
|
|
7
9
|
.description('View trending tokens')
|
|
@@ -10,7 +12,15 @@ const trendingCmd = new Command('trending')
|
|
|
10
12
|
const res = await getTrendingTokens();
|
|
11
13
|
spin.stop();
|
|
12
14
|
assertApiOk(res, 'Failed to fetch trending tokens');
|
|
13
|
-
console.log(
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log(chalk.bold('Trending Tokens:'));
|
|
17
|
+
if (Array.isArray(res.data) && res.data.length > 0) {
|
|
18
|
+
printTable(res.data, TOKEN_COLUMNS);
|
|
19
|
+
}
|
|
20
|
+
else if (res.data && typeof res.data === 'object') {
|
|
21
|
+
printKV(res.data);
|
|
22
|
+
}
|
|
23
|
+
console.log('');
|
|
14
24
|
}));
|
|
15
25
|
// ─── search ──────────────────────────────────────────────────────────────
|
|
16
26
|
const searchCmd = new Command('search')
|
|
@@ -31,7 +41,18 @@ const searchCmd = new Command('search')
|
|
|
31
41
|
: await searchStocks(keyword);
|
|
32
42
|
spin.stop();
|
|
33
43
|
assertApiOk(res, `Search for "${keyword}" failed`);
|
|
34
|
-
console.log(
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log(chalk.bold(`Search Results for "${keyword}":`));
|
|
46
|
+
if (Array.isArray(res.data) && res.data.length > 0) {
|
|
47
|
+
printTable(res.data, category === 'tokens' ? TOKEN_COLUMNS : undefined);
|
|
48
|
+
}
|
|
49
|
+
else if (Array.isArray(res.data)) {
|
|
50
|
+
console.log(chalk.dim(' No results found.'));
|
|
51
|
+
}
|
|
52
|
+
else if (res.data && typeof res.data === 'object') {
|
|
53
|
+
printKV(res.data);
|
|
54
|
+
}
|
|
55
|
+
console.log('');
|
|
35
56
|
}));
|
|
36
57
|
// ─── fear-greed ──────────────────────────────────────────────────────────
|
|
37
58
|
const fearGreedCmd = new Command('fear-greed')
|
|
@@ -41,7 +62,10 @@ const fearGreedCmd = new Command('fear-greed')
|
|
|
41
62
|
const res = await getFearGreedIndex();
|
|
42
63
|
spin.stop();
|
|
43
64
|
assertApiOk(res, 'Failed to fetch Fear & Greed Index');
|
|
44
|
-
console.log(
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(chalk.bold('Fear & Greed Index:'));
|
|
67
|
+
printFearGreed(res.data);
|
|
68
|
+
console.log('');
|
|
45
69
|
}));
|
|
46
70
|
// ─── btc metrics ─────────────────────────────────────────────────────────
|
|
47
71
|
const btcCmd = new Command('btc-metrics')
|
|
@@ -51,7 +75,10 @@ const btcCmd = new Command('btc-metrics')
|
|
|
51
75
|
const res = await getBitcoinMetrics();
|
|
52
76
|
spin.stop();
|
|
53
77
|
assertApiOk(res, 'Failed to fetch Bitcoin metrics');
|
|
54
|
-
console.log(
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(chalk.bold('Bitcoin Metrics:'));
|
|
80
|
+
printCryptoMetrics(res.data);
|
|
81
|
+
console.log('');
|
|
55
82
|
}));
|
|
56
83
|
// ─── parent ──────────────────────────────────────────────────────────────
|
|
57
84
|
export const discoverCommand = new Command('discover')
|
|
@@ -3,7 +3,9 @@ import { input, select, confirm, number as numberPrompt } from '@inquirer/prompt
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import * as loApi from '../api/limitorder.js';
|
|
5
5
|
import { requireAuth } from '../config.js';
|
|
6
|
-
import { success, info, spinner, assertApiOk, selectChain, wrapAction } from '../utils.js';
|
|
6
|
+
import { success, info, spinner, assertApiOk, selectChain, wrapAction, requireTransactionConfirmation, lookupToken, formatTokenLabel } from '../utils.js';
|
|
7
|
+
import { requireTouchId } from '../touchid.js';
|
|
8
|
+
import { printTxResult, printTable, LIMIT_ORDER_COLUMNS } from '../formatters.js';
|
|
7
9
|
// ─── create ──────────────────────────────────────────────────────────────
|
|
8
10
|
const createCmd = new Command('create')
|
|
9
11
|
.description('Create a limit order')
|
|
@@ -18,10 +20,11 @@ const createCmd = new Command('create')
|
|
|
18
20
|
{ name: 'Sell', value: 'sell' },
|
|
19
21
|
],
|
|
20
22
|
});
|
|
21
|
-
const
|
|
22
|
-
message: 'Target token contract address:',
|
|
23
|
+
const tokenInput = await input({
|
|
24
|
+
message: 'Target token (contract address or ticker):',
|
|
23
25
|
validate: (v) => (v.length > 0 ? true : 'Required'),
|
|
24
26
|
});
|
|
27
|
+
const tokenInfo = await lookupToken(tokenInput);
|
|
25
28
|
const priceCondition = await select({
|
|
26
29
|
message: 'Trigger when price is:',
|
|
27
30
|
choices: [
|
|
@@ -40,7 +43,8 @@ const createCmd = new Command('create')
|
|
|
40
43
|
console.log(chalk.bold('Limit Order:'));
|
|
41
44
|
console.log(` Chain : ${chalk.cyan(chain)}`);
|
|
42
45
|
console.log(` Side : ${side}`);
|
|
43
|
-
console.log(` Token : ${
|
|
46
|
+
console.log(` Token : ${formatTokenLabel(tokenInfo)}`);
|
|
47
|
+
console.log(` Address : ${chalk.yellow(tokenInfo.address)}`);
|
|
44
48
|
console.log(` Condition : price ${priceCondition} $${targetPrice}`);
|
|
45
49
|
console.log(` Amount : $${amount}`);
|
|
46
50
|
console.log(` Expires : ${new Date(expiredAt * 1000).toLocaleString()}`);
|
|
@@ -50,16 +54,17 @@ const createCmd = new Command('create')
|
|
|
50
54
|
if (!ok)
|
|
51
55
|
return;
|
|
52
56
|
}
|
|
57
|
+
await requireTransactionConfirmation(`Limit ${side} · $${amount} · price ${priceCondition} $${targetPrice} · ${chain}`, tokenInfo, { chain, side, amount: `$${amount}` });
|
|
58
|
+
await requireTouchId();
|
|
53
59
|
const spin = spinner('Creating limit order…');
|
|
54
60
|
const res = await loApi.createLimitOrder(creds.accessToken, {
|
|
55
|
-
chain, side, amount, targetTokenCA,
|
|
61
|
+
chain, side, amount, targetTokenCA: tokenInfo.address,
|
|
56
62
|
priceCondition, targetPrice: targetPrice, expiredAt,
|
|
57
63
|
});
|
|
58
64
|
spin.stop();
|
|
59
65
|
assertApiOk(res, 'Failed to create limit order');
|
|
60
66
|
success('Limit order created!');
|
|
61
|
-
|
|
62
|
-
console.log(JSON.stringify(res.data, null, 2));
|
|
67
|
+
printTxResult(res.data);
|
|
63
68
|
}));
|
|
64
69
|
// ─── list ────────────────────────────────────────────────────────────────
|
|
65
70
|
const listCmd = new Command('list')
|
|
@@ -76,7 +81,10 @@ const listCmd = new Command('list')
|
|
|
76
81
|
console.log(chalk.dim('No limit orders.'));
|
|
77
82
|
return;
|
|
78
83
|
}
|
|
79
|
-
console.log(
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(chalk.bold('Limit Orders:'));
|
|
86
|
+
printTable(orders, LIMIT_ORDER_COLUMNS);
|
|
87
|
+
console.log('');
|
|
80
88
|
}));
|
|
81
89
|
// ─── cancel ──────────────────────────────────────────────────────────────
|
|
82
90
|
const cancelCmd = new Command('cancel')
|
package/dist/commands/login.js
CHANGED
|
@@ -2,10 +2,11 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { input, select, confirm } from '@inquirer/prompts';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { sendEmailCode, verifyEmailCode, getOAuthUrl, getCurrentUser, startDeviceAuth, getDeviceAuthStatus, } from '../api/auth.js';
|
|
5
|
-
import { saveCredentials, loadCredentials } from '../config.js';
|
|
5
|
+
import { saveCredentials, loadCredentials, loadConfig, saveConfig } from '../config.js';
|
|
6
6
|
import { success, error, info, warn, spinner, openBrowser, unwrapApi, wrapAction } from '../utils.js';
|
|
7
7
|
import { startOAuthServer } from '../oauth-server.js';
|
|
8
8
|
import { OAUTH_PROVIDERS } from '../types.js';
|
|
9
|
+
import { isTouchIdAvailable } from '../touchid.js';
|
|
9
10
|
// ─── Email login flow ─────────────────────────────────────────────────────
|
|
10
11
|
async function loginWithEmail(emailOpt) {
|
|
11
12
|
const email = emailOpt ?? await input({
|
|
@@ -219,4 +220,19 @@ export const loginCommand = new Command('login')
|
|
|
219
220
|
else {
|
|
220
221
|
await loginWithOAuth(method);
|
|
221
222
|
}
|
|
223
|
+
// ── Offer Touch ID setup (macOS only, if not already enabled) ────
|
|
224
|
+
const config = loadConfig();
|
|
225
|
+
if (!config.touchId && isTouchIdAvailable()) {
|
|
226
|
+
console.log('');
|
|
227
|
+
const enableTouchId = await confirm({
|
|
228
|
+
message: 'Enable Touch ID to protect fund operations (transfer, withdraw, swap, etc.)?',
|
|
229
|
+
default: true,
|
|
230
|
+
});
|
|
231
|
+
if (enableTouchId) {
|
|
232
|
+
saveConfig({ touchId: true });
|
|
233
|
+
success('Touch ID protection enabled!');
|
|
234
|
+
console.log(chalk.dim(' All fund-related operations now require fingerprint verification.'));
|
|
235
|
+
console.log(chalk.dim(` To disable, run: ${chalk.cyan('minara config')} → Touch ID`));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
222
238
|
}));
|
package/dist/commands/perps.js
CHANGED
|
@@ -3,7 +3,9 @@ import { input, select, confirm, number as numberPrompt } from '@inquirer/prompt
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import * as perpsApi from '../api/perps.js';
|
|
5
5
|
import { requireAuth } from '../config.js';
|
|
6
|
-
import { success, info, warn, spinner, assertApiOk, wrapAction } from '../utils.js';
|
|
6
|
+
import { success, info, warn, spinner, assertApiOk, formatOrderSide, wrapAction, requireTransactionConfirmation } from '../utils.js';
|
|
7
|
+
import { requireTouchId } from '../touchid.js';
|
|
8
|
+
import { printTxResult, printTable, POSITION_COLUMNS } from '../formatters.js';
|
|
7
9
|
// ─── deposit ─────────────────────────────────────────────────────────────
|
|
8
10
|
const depositCmd = new Command('deposit')
|
|
9
11
|
.description('Deposit USDC into Hyperliquid perps (min 5 USDC)')
|
|
@@ -24,13 +26,14 @@ const depositCmd = new Command('deposit')
|
|
|
24
26
|
if (!ok)
|
|
25
27
|
return;
|
|
26
28
|
}
|
|
29
|
+
await requireTransactionConfirmation(`Deposit ${amount} USDC → Perps`);
|
|
30
|
+
await requireTouchId();
|
|
27
31
|
const spin = spinner('Depositing…');
|
|
28
32
|
const res = await perpsApi.deposit(creds.accessToken, { usdcAmount: amount });
|
|
29
33
|
spin.stop();
|
|
30
34
|
assertApiOk(res, 'Deposit failed');
|
|
31
35
|
success(`Deposited ${amount} USDC`);
|
|
32
|
-
|
|
33
|
-
console.log(JSON.stringify(res.data, null, 2));
|
|
36
|
+
printTxResult(res.data);
|
|
34
37
|
}));
|
|
35
38
|
// ─── withdraw ────────────────────────────────────────────────────────────
|
|
36
39
|
const withdrawCmd = new Command('withdraw')
|
|
@@ -54,13 +57,14 @@ const withdrawCmd = new Command('withdraw')
|
|
|
54
57
|
if (!ok)
|
|
55
58
|
return;
|
|
56
59
|
}
|
|
60
|
+
await requireTransactionConfirmation(`Withdraw ${amount} USDC → ${toAddress}`);
|
|
61
|
+
await requireTouchId();
|
|
57
62
|
const spin = spinner('Withdrawing…');
|
|
58
63
|
const res = await perpsApi.withdraw(creds.accessToken, { usdcAmount: amount, toAddress });
|
|
59
64
|
spin.stop();
|
|
60
65
|
assertApiOk(res, 'Withdrawal failed');
|
|
61
66
|
success('Withdrawal submitted');
|
|
62
|
-
|
|
63
|
-
console.log(JSON.stringify(res.data, null, 2));
|
|
67
|
+
printTxResult(res.data);
|
|
64
68
|
}));
|
|
65
69
|
// ─── positions ───────────────────────────────────────────────────────────
|
|
66
70
|
const positionsCmd = new Command('positions')
|
|
@@ -72,7 +76,16 @@ const positionsCmd = new Command('positions')
|
|
|
72
76
|
const res = await perpsApi.getPositions(creds.accessToken);
|
|
73
77
|
spin.stop();
|
|
74
78
|
assertApiOk(res, 'Failed to fetch positions');
|
|
75
|
-
|
|
79
|
+
const positions = res.data;
|
|
80
|
+
if (!positions || (Array.isArray(positions) && positions.length === 0)) {
|
|
81
|
+
console.log(chalk.dim('No open positions.'));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(chalk.bold('Open Positions:'));
|
|
86
|
+
printTable(positions, POSITION_COLUMNS);
|
|
87
|
+
console.log('');
|
|
88
|
+
}
|
|
76
89
|
}));
|
|
77
90
|
// ─── order ───────────────────────────────────────────────────────────────
|
|
78
91
|
const orderCmd = new Command('order')
|
|
@@ -132,7 +145,13 @@ const orderCmd = new Command('order')
|
|
|
132
145
|
};
|
|
133
146
|
console.log('');
|
|
134
147
|
console.log(chalk.bold('Order Preview:'));
|
|
135
|
-
console.log(
|
|
148
|
+
console.log(` Asset : ${chalk.bold(order.a)}`);
|
|
149
|
+
console.log(` Side : ${formatOrderSide(order.b ? 'buy' : 'sell')}`);
|
|
150
|
+
console.log(` Price : ${chalk.yellow(order.p)}`);
|
|
151
|
+
console.log(` Size : ${chalk.bold(order.s)}`);
|
|
152
|
+
console.log(` Reduce Only : ${order.r ? chalk.yellow('Yes') : 'No'}`);
|
|
153
|
+
console.log(` Type : ${'limit' in order.t ? 'Limit (GTC)' : 'Trigger'}`);
|
|
154
|
+
console.log(` Grouping : ${grouping}`);
|
|
136
155
|
console.log('');
|
|
137
156
|
if (!opts.yes) {
|
|
138
157
|
const ok = await confirm({ message: 'Submit order?', default: false });
|
|
@@ -141,13 +160,14 @@ const orderCmd = new Command('order')
|
|
|
141
160
|
return;
|
|
142
161
|
}
|
|
143
162
|
}
|
|
163
|
+
await requireTransactionConfirmation(`Perps ${order.b ? 'LONG' : 'SHORT'} ${order.a} · size ${order.s} @ ${order.p}`);
|
|
164
|
+
await requireTouchId();
|
|
144
165
|
const spin = spinner('Placing order…');
|
|
145
166
|
const res = await perpsApi.placeOrders(creds.accessToken, { orders: [order], grouping });
|
|
146
167
|
spin.stop();
|
|
147
168
|
assertApiOk(res, 'Order placement failed');
|
|
148
169
|
success('Order submitted!');
|
|
149
|
-
|
|
150
|
-
console.log(JSON.stringify(res.data, null, 2));
|
|
170
|
+
printTxResult(res.data);
|
|
151
171
|
}));
|
|
152
172
|
// ─── cancel ──────────────────────────────────────────────────────────────
|
|
153
173
|
const cancelCmd = new Command('cancel')
|
|
@@ -173,8 +193,7 @@ const cancelCmd = new Command('cancel')
|
|
|
173
193
|
spin.stop();
|
|
174
194
|
assertApiOk(res, 'Order cancellation failed');
|
|
175
195
|
success('Order cancelled');
|
|
176
|
-
|
|
177
|
-
console.log(JSON.stringify(res.data, null, 2));
|
|
196
|
+
printTxResult(res.data);
|
|
178
197
|
}));
|
|
179
198
|
// ─── leverage ────────────────────────────────────────────────────────────
|
|
180
199
|
const leverageCmd = new Command('leverage')
|
|
@@ -205,7 +224,15 @@ const tradesCmd = new Command('trades')
|
|
|
205
224
|
const res = await perpsApi.getCompletedTrades(creds.accessToken);
|
|
206
225
|
spin.stop();
|
|
207
226
|
assertApiOk(res, 'Failed to fetch trades');
|
|
208
|
-
console.log(
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log(chalk.bold('Completed Trades:'));
|
|
229
|
+
if (Array.isArray(res.data) && res.data.length > 0) {
|
|
230
|
+
printTable(res.data);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log(chalk.dim(' No completed trades.'));
|
|
234
|
+
}
|
|
235
|
+
console.log('');
|
|
209
236
|
}));
|
|
210
237
|
// ─── fund-records ────────────────────────────────────────────────────────
|
|
211
238
|
const fundRecordsCmd = new Command('fund-records')
|
|
@@ -218,7 +245,15 @@ const fundRecordsCmd = new Command('fund-records')
|
|
|
218
245
|
const res = await perpsApi.getFundRecords(creds.accessToken, parseInt(opts.page, 10), parseInt(opts.limit, 10));
|
|
219
246
|
spin.stop();
|
|
220
247
|
assertApiOk(res, 'Failed to fetch fund records');
|
|
221
|
-
console.log(
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(chalk.bold('Fund Records:'));
|
|
250
|
+
if (Array.isArray(res.data) && res.data.length > 0) {
|
|
251
|
+
printTable(res.data);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log(chalk.dim(' No fund records.'));
|
|
255
|
+
}
|
|
256
|
+
console.log('');
|
|
222
257
|
}));
|
|
223
258
|
// ═════════════════════════════════════════════════════════════════════════
|
|
224
259
|
// Parent
|