minara 0.2.5 → 0.2.7

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 CHANGED
@@ -19,7 +19,7 @@
19
19
  ## Features
20
20
 
21
21
  - **AI Chat** — Crypto-native AI for on-chain analysis, market research, and DeFi due diligence. Interactive REPL & single-shot queries with `fast` / `quality` / `thinking` modes
22
- - **Wallet & Balance** — Unified balance view, spot holdings with PnL, perps account overview, deposits and withdrawals
22
+ - **Wallet & Balance** — Unified balance view, spot holdings with PnL, perps account overview, deposits, withdrawals, and credit card on-ramp via MoonPay
23
23
  - **Chain-Abstracted Trading** — Cross-chain swaps with automatic chain detection, perpetual futures, and limit orders. Accepts `$TICKER`, token name, or contract address
24
24
  - **AI Autopilot & Analysis** — Fully managed AI trading strategies for perps, plus on-demand long/short analysis with one-click quick order
25
25
  - **Market Discovery** — Trending tokens & stocks, Fear & Greed Index, on-chain metrics, and search
@@ -87,7 +87,8 @@ minara login -e user@mail.com # Email verification code
87
87
  | `minara assets` | Full overview: spot holdings + perps account |
88
88
  | `minara assets spot` | Spot wallet: portfolio value, cost, PnL, holdings |
89
89
  | `minara assets perps` | Perps account: equity, margin, positions |
90
- | `minara deposit` | Deposit to spot (view addresses) or perps (direct / from spot) |
90
+ | `minara deposit` | Deposit to spot, perps, or buy crypto with credit card |
91
+ | `minara deposit buy` | Buy crypto with credit card via MoonPay |
91
92
  | `minara withdraw` | Withdraw tokens to an external wallet |
92
93
 
93
94
  ```bash
@@ -95,9 +96,10 @@ minara balance # Quick total: Spot + Perps available balance
95
96
  minara assets # Full overview (spot + perps)
96
97
  minara assets spot # Spot wallet with PnL breakdown
97
98
  minara assets perps # Perps equity, margin, positions
98
- minara deposit # Interactive: Spot (addresses) or Perps (address / transfer)
99
+ minara deposit # Interactive: Spot / Perps / Buy with credit card
99
100
  minara deposit spot # Show spot wallet deposit addresses (EVM + Solana)
100
101
  minara deposit perps # Perps: show Arbitrum deposit address, or transfer from Spot → Perps
102
+ minara deposit buy # Buy crypto with credit card via MoonPay (opens browser)
101
103
  minara withdraw -c solana -t '$SOL' -a 10 --to <address>
102
104
  minara withdraw # Interactive mode (accepts ticker or address)
103
105
  ```
@@ -15,3 +15,7 @@ export declare function checkoutPackage(token: string, packageId: string, succes
15
15
  export declare function cryptoCheckoutPackage(token: string, packageId: string): Promise<import("../types.js").ApiResponse<CryptoCheckout>>;
16
16
  /** Cancel current subscription */
17
17
  export declare function cancelSubscription(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
18
+ /** Sign a MoonPay widget URL (backend appends HMAC signature). */
19
+ export declare function getMoonPaySignature(token: string, url: string): Promise<import("../types.js").ApiResponse<{
20
+ signature: string;
21
+ }>>;
@@ -1,4 +1,4 @@
1
- import { get, del } from './client.js';
1
+ import { get, post, del } from './client.js';
2
2
  /** Get all subscription plans and credit packages */
3
3
  export function getPlans() {
4
4
  return get('/payment/plans');
@@ -37,3 +37,7 @@ export function cryptoCheckoutPackage(token, packageId) {
37
37
  export function cancelSubscription(token) {
38
38
  return del('/payment/subscription', { token });
39
39
  }
40
+ /** Sign a MoonPay widget URL (backend appends HMAC signature). */
41
+ export function getMoonPaySignature(token, url) {
42
+ return post('/payment/moonpay/signature', { token, body: { url } });
43
+ }
@@ -5,7 +5,7 @@ import { getAccount } from '../api/crosschain.js';
5
5
  import { getCurrentUser } from '../api/auth.js';
6
6
  import * as perpsApi from '../api/perps.js';
7
7
  import { requireAuth } from '../config.js';
8
- import { info, success, spinner, assertApiOk, wrapAction, requireTransactionConfirmation } from '../utils.js';
8
+ import { info, success, warn, spinner, assertApiOk, wrapAction, requireTransactionConfirmation, openBrowser } from '../utils.js';
9
9
  import { requireTouchId } from '../touchid.js';
10
10
  import { printTxResult } from '../formatters.js';
11
11
  const EVM_CHAINS = 'Ethereum, Base, Arbitrum, Optimism, Polygon, Avalanche, BSC, Berachain, Blast';
@@ -52,6 +52,55 @@ async function showSpotDeposit(token) {
52
52
  console.log(chalk.red(' • Sending tokens on the wrong network may result in permanent loss.'));
53
53
  console.log('');
54
54
  }
55
+ // ─── moonpay (credit card on-ramp) ───────────────────────────────────────
56
+ const MOONPAY_PK = 'pk_live_yIf64w79W6ufwip4j51PWbymdwGtI';
57
+ const MOONPAY_CURRENCIES = [
58
+ { name: 'USDC (Base)', code: 'usdc_base', network: 'base' },
59
+ { name: 'USDC (Ethereum)', code: 'usdc', network: 'ethereum' },
60
+ { name: 'USDC (Arbitrum)', code: 'usdc_arbitrum', network: 'arbitrum' },
61
+ { name: 'USDC (Polygon)', code: 'usdc_polygon', network: 'polygon' },
62
+ { name: 'ETH', code: 'eth', network: 'ethereum' },
63
+ { name: 'ETH (Base)', code: 'eth_base', network: 'base' },
64
+ { name: 'SOL', code: 'sol', network: 'solana' },
65
+ ];
66
+ async function moonPayOnRamp(token) {
67
+ const addrSpin = spinner('Fetching wallet address…');
68
+ const accountRes = await getAccount(token);
69
+ addrSpin.stop();
70
+ const account = accountRes.data;
71
+ const evmAddr = account?.evmAddress;
72
+ const solAddr = account?.solanaAddress;
73
+ if (!evmAddr && !solAddr) {
74
+ warn('No wallet address found. Your account may not be fully initialized.');
75
+ return;
76
+ }
77
+ const currency = await select({
78
+ message: 'Currency to buy:',
79
+ choices: MOONPAY_CURRENCIES
80
+ .filter((c) => c.network === 'solana' ? !!solAddr : !!evmAddr)
81
+ .map((c) => ({ name: c.name, value: c })),
82
+ });
83
+ const walletAddress = currency.network === 'solana' ? solAddr : evmAddr;
84
+ // Build MoonPay buy URL (no walletAddress in URL — requires server-side signing
85
+ // which depends on backend MoonPay secret key config).
86
+ // User pastes their address in MoonPay's form instead.
87
+ const params = new URLSearchParams();
88
+ params.set('apiKey', MOONPAY_PK);
89
+ params.set('currencyCode', currency.code);
90
+ params.set('defaultCurrencyCode', currency.code);
91
+ const buyUrl = `https://buy.moonpay.com?${params.toString()}`;
92
+ console.log('');
93
+ console.log(chalk.bold('Buy Crypto with Credit Card (MoonPay)'));
94
+ console.log('');
95
+ console.log(` Currency : ${chalk.cyan(currency.name)}`);
96
+ console.log(` Wallet : ${chalk.yellow(walletAddress)}`);
97
+ console.log(chalk.dim(' ↑ Copy this address and paste it in MoonPay when prompted.'));
98
+ console.log('');
99
+ info('Opening MoonPay in your browser…');
100
+ openBrowser(buyUrl);
101
+ console.log(chalk.dim(' Complete the purchase in your browser. Funds will arrive in your Minara wallet.'));
102
+ console.log('');
103
+ }
55
104
  // ─── perps ───────────────────────────────────────────────────────────────
56
105
  const perpsCmd = new Command('perps')
57
106
  .description('Deposit USDC to perps account')
@@ -128,23 +177,34 @@ async function transferSpotToPerps(token, opts) {
128
177
  printTxResult(res.data);
129
178
  }
130
179
  // ─── parent ──────────────────────────────────────────────────────────────
180
+ const buyCmd = new Command('buy')
181
+ .description('Buy crypto with credit card via MoonPay')
182
+ .action(wrapAction(async () => {
183
+ const creds = requireAuth();
184
+ await moonPayOnRamp(creds.accessToken);
185
+ }));
131
186
  export const depositCommand = new Command('deposit')
132
- .description('Deposit to spot wallet or perps account')
187
+ .description('Deposit to spot wallet or perps account, or buy with credit card')
133
188
  .addCommand(spotCmd)
134
189
  .addCommand(perpsCmd)
190
+ .addCommand(buyCmd)
135
191
  .action(wrapAction(async () => {
136
192
  const action = await select({
137
193
  message: 'Deposit to:',
138
194
  choices: [
139
195
  { name: 'Spot wallet — view deposit addresses', value: 'spot' },
140
196
  { name: 'Perps wallet — view deposit address or transfer from Spot', value: 'perps' },
197
+ { name: `Buy crypto with credit card ${chalk.dim('(MoonPay)')}`, value: 'buy' },
141
198
  ],
142
199
  });
143
200
  const creds = requireAuth();
144
201
  if (action === 'spot') {
145
202
  await showSpotDeposit(creds.accessToken);
146
203
  }
147
- else {
204
+ else if (action === 'perps') {
148
205
  await perpsDepositFlow(creds.accessToken);
149
206
  }
207
+ else {
208
+ await moonPayOnRamp(creds.accessToken);
209
+ }
150
210
  }));
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { Command } from 'commander';
4
4
  import chalk from 'chalk';
5
5
  import { setRawJson } from './formatters.js';
6
+ import { checkForUpdate } from './update-check.js';
6
7
  // Auth & Account
7
8
  import { loginCommand } from './commands/login.js';
8
9
  import { logoutCommand } from './commands/logout.js';
@@ -24,6 +25,8 @@ import { premiumCommand } from './commands/premium.js';
24
25
  import { configCommand } from './commands/config.js';
25
26
  const require = createRequire(import.meta.url);
26
27
  const { version } = require('../package.json');
28
+ // Fire update check early (non-blocking); result printed after command completes
29
+ const updateCheckPromise = checkForUpdate(version);
27
30
  const program = new Command();
28
31
  program
29
32
  .name('minara')
@@ -65,8 +68,14 @@ program.addCommand(configCommand);
65
68
  program.action(() => {
66
69
  program.outputHelp();
67
70
  });
68
- program.parseAsync(process.argv).catch((err) => {
71
+ program.parseAsync(process.argv)
72
+ .catch((err) => {
69
73
  const message = err instanceof Error ? err.message : String(err);
70
74
  console.error(chalk.red('Error:'), message);
71
75
  process.exit(1);
76
+ })
77
+ .finally(async () => {
78
+ const notice = await updateCheckPromise;
79
+ if (notice)
80
+ console.log(notice);
72
81
  });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Non-blocking update check. Returns a formatted notice string if a newer
3
+ * version is available on npm, or `null` if up-to-date / check skipped.
4
+ */
5
+ export declare function checkForUpdate(currentVersion: string): Promise<string | null>;
@@ -0,0 +1,77 @@
1
+ import { join } from 'node:path';
2
+ import { homedir } from 'node:os';
3
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
4
+ import chalk from 'chalk';
5
+ const PKG_NAME = 'minara';
6
+ const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
7
+ const CACHE_DIR = join(homedir(), '.minara');
8
+ const CACHE_FILE = join(CACHE_DIR, 'update-check.json');
9
+ const FETCH_TIMEOUT_MS = 3000;
10
+ function readCache() {
11
+ try {
12
+ return JSON.parse(readFileSync(CACHE_FILE, 'utf-8'));
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function writeCache(data) {
19
+ try {
20
+ mkdirSync(CACHE_DIR, { recursive: true, mode: 0o700 });
21
+ writeFileSync(CACHE_FILE, JSON.stringify(data), { mode: 0o600 });
22
+ }
23
+ catch {
24
+ // best-effort
25
+ }
26
+ }
27
+ function isNewer(remote, local) {
28
+ const r = remote.split('.').map(Number);
29
+ const l = local.split('.').map(Number);
30
+ for (let i = 0; i < 3; i++) {
31
+ if ((r[i] ?? 0) > (l[i] ?? 0))
32
+ return true;
33
+ if ((r[i] ?? 0) < (l[i] ?? 0))
34
+ return false;
35
+ }
36
+ return false;
37
+ }
38
+ /**
39
+ * Non-blocking update check. Returns a formatted notice string if a newer
40
+ * version is available on npm, or `null` if up-to-date / check skipped.
41
+ */
42
+ export async function checkForUpdate(currentVersion) {
43
+ try {
44
+ // Use cache to avoid hitting npm on every invocation
45
+ const cached = readCache();
46
+ if (cached && Date.now() - cached.checkedAt < CHECK_INTERVAL_MS) {
47
+ return isNewer(cached.latest, currentVersion)
48
+ ? formatNotice(currentVersion, cached.latest)
49
+ : null;
50
+ }
51
+ const controller = new AbortController();
52
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
53
+ const res = await fetch(`https://registry.npmjs.org/${PKG_NAME}/latest`, {
54
+ signal: controller.signal,
55
+ headers: { Accept: 'application/json' },
56
+ });
57
+ clearTimeout(timer);
58
+ if (!res.ok)
59
+ return null;
60
+ const { version: latest } = (await res.json());
61
+ if (!latest)
62
+ return null;
63
+ writeCache({ latest, checkedAt: Date.now() });
64
+ return isNewer(latest, currentVersion)
65
+ ? formatNotice(currentVersion, latest)
66
+ : null;
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ function formatNotice(current, latest) {
73
+ const line = ` Update available: ${chalk.dim(current)} → ${chalk.green.bold(latest)}`;
74
+ const cmd = ` Run ${chalk.cyan('npm i -g minara')} to update`;
75
+ const border = chalk.yellow('─'.repeat(48));
76
+ return `\n${border}\n${line}\n${cmd}\n${border}`;
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minara",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "CLI client for Minara.ai — login, trade, deposit/withdraw, chat and more from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",