@darksol/terminal 0.3.6 → 0.4.0-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darksol/terminal",
3
- "version": "0.3.6",
3
+ "version": "0.4.0-beta.2",
4
4
  "description": "DARKSOL Terminal — unified CLI for all DARKSOL services. Market intel, trading, oracle, casino, and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,6 +26,7 @@
26
26
  "author": "DARKSOL <chris00claw@gmail.com>",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
+ "agentmail": "^0.4.2",
29
30
  "blessed": "^0.1.81",
30
31
  "blessed-contrib": "^4.11.0",
31
32
  "boxen": "^8.0.1",
package/src/cli.js CHANGED
@@ -4,6 +4,11 @@ import { theme } from './ui/theme.js';
4
4
  import { kvDisplay, success, error, warn, info } from './ui/components.js';
5
5
  import { getConfig, setConfig, getAllConfig, getRPC, setRPC, configPath } from './config/store.js';
6
6
  import { createWallet, importWallet, showWallets, getBalance, useWallet, exportWallet } from './wallet/manager.js';
7
+ import { showPortfolio } from './wallet/portfolio.js';
8
+ import { showHistory } from './wallet/history.js';
9
+ import { showGas } from './services/gas.js';
10
+ import { watchPrice, checkPrices } from './services/watch.js';
11
+ import { mailSetup, mailCreate, mailInboxes, mailSend, mailList, mailRead, mailReply, mailForward, mailThreads, mailDelete, mailUse, mailStats, mailStatus } from './services/mail.js';
7
12
  import { executeSwap } from './trading/swap.js';
8
13
  import { snipeToken, watchSnipe } from './trading/snipe.js';
9
14
  import { createDCA, listDCA, cancelDCA, runDCA } from './trading/dca.js';
@@ -69,6 +74,18 @@ export function cli(argv) {
69
74
  .description('Export wallet details')
70
75
  .action((name) => exportWallet(name));
71
76
 
77
+ wallet
78
+ .command('portfolio [name]')
79
+ .description('View balances across all EVM chains')
80
+ .action((name) => showPortfolio(name));
81
+
82
+ wallet
83
+ .command('history [name]')
84
+ .description('Recent transaction history')
85
+ .option('-c, --chain <chain>', 'Chain to check')
86
+ .option('-l, --limit <n>', 'Number of transactions', '10')
87
+ .action((name, opts) => showHistory(name, opts));
88
+
72
89
  // ═══════════════════════════════════════
73
90
  // TRADING COMMANDS
74
91
  // ═══════════════════════════════════════
@@ -294,6 +311,126 @@ export function cli(argv) {
294
311
  .description('Settle payment on-chain')
295
312
  .action((payment) => facilitatorSettle(payment));
296
313
 
314
+ // ═══════════════════════════════════════
315
+ // MAIL COMMANDS
316
+ // ═══════════════════════════════════════
317
+ const mail = program
318
+ .command('mail')
319
+ .description('📧 AgentMail — email for your agent');
320
+
321
+ mail
322
+ .command('setup')
323
+ .description('Set up AgentMail (API key, browser registration)')
324
+ .action(() => mailSetup());
325
+
326
+ mail
327
+ .command('status')
328
+ .description('Show AgentMail connection status')
329
+ .action(() => mailStatus());
330
+
331
+ mail
332
+ .command('create')
333
+ .description('Create a new email inbox')
334
+ .option('-u, --username <name>', 'Custom username')
335
+ .option('-d, --display-name <name>', 'Display name')
336
+ .action((opts) => mailCreate(opts));
337
+
338
+ mail
339
+ .command('inboxes')
340
+ .description('List all inboxes')
341
+ .action(() => mailInboxes());
342
+
343
+ mail
344
+ .command('use <inbox-id>')
345
+ .description('Set active inbox')
346
+ .action((id) => mailUse(id));
347
+
348
+ mail
349
+ .command('send')
350
+ .description('Send an email')
351
+ .option('--to <email>', 'Recipient email')
352
+ .option('--subject <subject>', 'Email subject')
353
+ .option('--text <body>', 'Email body')
354
+ .option('--inbox <id>', 'Inbox ID to send from')
355
+ .action((opts) => mailSend(opts));
356
+
357
+ mail
358
+ .command('inbox')
359
+ .description('List received messages')
360
+ .option('-l, --limit <n>', 'Number of messages', '10')
361
+ .option('--inbox <id>', 'Inbox ID')
362
+ .action((opts) => mailList(opts));
363
+
364
+ mail
365
+ .command('read <message>')
366
+ .description('Read a message (by number or ID)')
367
+ .option('--inbox <id>', 'Inbox ID')
368
+ .action((ref, opts) => mailRead(ref, opts));
369
+
370
+ mail
371
+ .command('reply <message>')
372
+ .description('Reply to a message')
373
+ .option('--text <body>', 'Reply text')
374
+ .option('--inbox <id>', 'Inbox ID')
375
+ .action((ref, opts) => mailReply(ref, opts));
376
+
377
+ mail
378
+ .command('forward <message>')
379
+ .description('Forward a message')
380
+ .option('--to <email>', 'Forward to email')
381
+ .option('--inbox <id>', 'Inbox ID')
382
+ .action((ref, opts) => mailForward(ref, opts));
383
+
384
+ mail
385
+ .command('threads')
386
+ .description('List email threads')
387
+ .option('--inbox <id>', 'Inbox ID')
388
+ .action((opts) => mailThreads(opts));
389
+
390
+ mail
391
+ .command('stats')
392
+ .description('Inbox metrics and stats')
393
+ .option('--inbox <id>', 'Inbox ID')
394
+ .action((opts) => mailStats(opts));
395
+
396
+ mail
397
+ .command('delete [inbox-id]')
398
+ .description('Delete an inbox')
399
+ .action((id) => mailDelete(id));
400
+
401
+ // ═══════════════════════════════════════
402
+ // PORTFOLIO SHORTCUT
403
+ // ═══════════════════════════════════════
404
+ program
405
+ .command('portfolio [name]')
406
+ .description('Multi-chain balance view (shortcut for: wallet portfolio)')
407
+ .action((name) => showPortfolio(name));
408
+
409
+ // ═══════════════════════════════════════
410
+ // GAS COMMAND
411
+ // ═══════════════════════════════════════
412
+ program
413
+ .command('gas [chain]')
414
+ .description('Show current gas prices and estimated costs')
415
+ .action((chain) => showGas(chain));
416
+
417
+ // ═══════════════════════════════════════
418
+ // PRICE COMMANDS
419
+ // ═══════════════════════════════════════
420
+ program
421
+ .command('price <tokens...>')
422
+ .description('Quick price check for one or more tokens')
423
+ .action((tokens) => checkPrices(tokens));
424
+
425
+ program
426
+ .command('watch <token>')
427
+ .description('Live price monitoring with alerts')
428
+ .option('-i, --interval <sec>', 'Poll interval in seconds', '10')
429
+ .option('--above <price>', 'Alert when price goes above')
430
+ .option('--below <price>', 'Alert when price drops below')
431
+ .option('-d, --duration <min>', 'Run for N minutes then stop')
432
+ .action((token, opts) => watchPrice(token, opts));
433
+
297
434
  // ═══════════════════════════════════════
298
435
  // CHAT SHORTCUT (darksol chat = darksol ai chat)
299
436
  // ═══════════════════════════════════════
@@ -776,6 +913,10 @@ function showCommandList() {
776
913
  showSection('COMMANDS');
777
914
  const commands = [
778
915
  ['wallet', 'Create, import, manage wallets'],
916
+ ['portfolio', 'Multi-chain balance view'],
917
+ ['price', 'Quick token price check'],
918
+ ['watch', 'Live price monitoring + alerts'],
919
+ ['gas', 'Gas prices & cost estimates'],
779
920
  ['trade', 'Swap tokens, snipe, trading'],
780
921
  ['dca', 'Dollar-cost averaging orders'],
781
922
  ['ai chat', 'Standalone AI chat session'],
@@ -788,6 +929,7 @@ function showCommandList() {
788
929
  ['casino', 'The Clawsino — betting'],
789
930
  ['cards', 'Prepaid Visa/MC cards'],
790
931
  ['builders', 'ERC-8021 builder index'],
932
+ ['mail', 'AgentMail — email for your agent'],
791
933
  ['facilitator', 'x402 payment facilitator'],
792
934
  ['skills', 'Agent skill directory'],
793
935
  ['setup', 'Re-run setup wizard'],
@@ -154,6 +154,14 @@ export const SERVICES = {
154
154
  docsUrl: 'https://portal.1inch.dev',
155
155
  validate: (key) => key.length > 10,
156
156
  },
157
+ agentmail: {
158
+ name: 'AgentMail',
159
+ category: 'email',
160
+ description: 'Email for AI agents — create inboxes, send/receive',
161
+ envVar: 'AGENTMAIL_API_KEY',
162
+ docsUrl: 'https://console.agentmail.to',
163
+ validate: (key) => key.startsWith('am_'),
164
+ },
157
165
  paraswap: {
158
166
  name: 'ParaSwap',
159
167
  category: 'trading',
@@ -287,8 +295,8 @@ export function listKeys() {
287
295
 
288
296
  showSection('API KEY VAULT');
289
297
 
290
- const categories = ['llm', 'data', 'rpc', 'trading'];
291
- const catNames = { llm: '🧠 LLM PROVIDERS', data: '📊 DATA PROVIDERS', rpc: '🌐 RPC PROVIDERS', trading: '📈 TRADING' };
298
+ const categories = ['llm', 'data', 'rpc', 'trading', 'email'];
299
+ const catNames = { llm: '🧠 LLM PROVIDERS', data: '📊 DATA PROVIDERS', rpc: '🌐 RPC PROVIDERS', trading: '📈 TRADING', email: '📧 EMAIL' };
292
300
 
293
301
  for (const cat of categories) {
294
302
  console.log('');
@@ -0,0 +1,116 @@
1
+ import { ethers } from 'ethers';
2
+ import { getConfig, getRPC } from '../config/store.js';
3
+ import { theme } from '../ui/theme.js';
4
+ import { spinner, kvDisplay, success, error, warn, info } from '../ui/components.js';
5
+ import { showSection } from '../ui/banner.js';
6
+
7
+ // ══════════════════════════════════════════════════
8
+ // GAS ESTIMATOR
9
+ // ══════════════════════════════════════════════════
10
+
11
+ /**
12
+ * Show current gas prices for active chain
13
+ */
14
+ export async function showGas(chain) {
15
+ chain = chain || getConfig('chain') || 'base';
16
+ const rpc = getRPC(chain);
17
+ const provider = new ethers.JsonRpcProvider(rpc);
18
+
19
+ const spin = spinner(`Fetching gas on ${chain}...`).start();
20
+
21
+ try {
22
+ const feeData = await provider.getFeeData();
23
+ const block = await provider.getBlock('latest');
24
+
25
+ spin.succeed('Gas data fetched');
26
+
27
+ const gasPrice = feeData.gasPrice ? parseFloat(ethers.formatUnits(feeData.gasPrice, 'gwei')) : 0;
28
+ const maxFee = feeData.maxFeePerGas ? parseFloat(ethers.formatUnits(feeData.maxFeePerGas, 'gwei')) : 0;
29
+ const maxPriority = feeData.maxPriorityFeePerGas ? parseFloat(ethers.formatUnits(feeData.maxPriorityFeePerGas, 'gwei')) : 0;
30
+ const baseFee = block?.baseFeePerGas ? parseFloat(ethers.formatUnits(block.baseFeePerGas, 'gwei')) : 0;
31
+
32
+ console.log('');
33
+ showSection(`GAS — ${chain.toUpperCase()}`);
34
+ kvDisplay([
35
+ ['Gas Price', `${gasPrice.toFixed(4)} gwei`],
36
+ ['Base Fee', `${baseFee.toFixed(4)} gwei`],
37
+ ['Max Fee', `${maxFee.toFixed(4)} gwei`],
38
+ ['Priority Fee', `${maxPriority.toFixed(4)} gwei`],
39
+ ['Block', `#${block?.number || '?'}`],
40
+ ]);
41
+
42
+ // Estimate common operations
43
+ console.log('');
44
+ showSection('ESTIMATED COSTS');
45
+
46
+ const ethPrice = await getETHPrice();
47
+ const estimates = [
48
+ { name: 'ETH Transfer', gas: 21000n },
49
+ { name: 'ERC-20 Transfer', gas: 65000n },
50
+ { name: 'ERC-20 Approve', gas: 46000n },
51
+ { name: 'Uniswap Swap', gas: 200000n },
52
+ { name: 'Uniswap + Approve', gas: 250000n },
53
+ { name: 'Contract Deploy (small)', gas: 500000n },
54
+ ];
55
+
56
+ estimates.forEach(({ name, gas }) => {
57
+ const costWei = gas * (feeData.gasPrice || 0n);
58
+ const costETH = parseFloat(ethers.formatEther(costWei));
59
+ const costUSD = costETH * ethPrice;
60
+ const label = name.padEnd(24);
61
+ const ethStr = costETH < 0.000001 ? '<$0.01' : `${costETH.toFixed(6)} ETH`;
62
+ const usdStr = costUSD < 0.01 ? '<$0.01' : `$${costUSD.toFixed(2)}`;
63
+ console.log(` ${theme.dim(label)} ${ethStr.padEnd(16)} ${theme.gold(usdStr)}`);
64
+ });
65
+
66
+ console.log('');
67
+ info(`ETH price: $${ethPrice.toFixed(2)}`);
68
+ console.log('');
69
+
70
+ return { chain, gasPrice, baseFee, maxFee, maxPriority, ethPrice };
71
+ } catch (err) {
72
+ spin.fail('Failed to fetch gas data');
73
+ error(err.message);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get ETH price (CoinGecko, fallback estimate)
79
+ */
80
+ async function getETHPrice() {
81
+ try {
82
+ const fetch = (await import('node-fetch')).default;
83
+ const resp = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd');
84
+ const data = await resp.json();
85
+ return data.ethereum?.usd || 3000;
86
+ } catch {
87
+ return 3000;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Estimate gas for a specific transaction (pre-trade check)
93
+ */
94
+ export async function estimateTradeGas(txParams, chain) {
95
+ chain = chain || getConfig('chain') || 'base';
96
+ const rpc = getRPC(chain);
97
+ const provider = new ethers.JsonRpcProvider(rpc);
98
+
99
+ try {
100
+ const gasEstimate = await provider.estimateGas(txParams);
101
+ const feeData = await provider.getFeeData();
102
+ const costWei = gasEstimate * (feeData.gasPrice || 0n);
103
+ const costETH = parseFloat(ethers.formatEther(costWei));
104
+ const ethPrice = await getETHPrice();
105
+ const costUSD = costETH * ethPrice;
106
+
107
+ return {
108
+ gas: gasEstimate.toString(),
109
+ costETH: costETH.toFixed(6),
110
+ costUSD: costUSD.toFixed(2),
111
+ gasPrice: feeData.gasPrice ? ethers.formatUnits(feeData.gasPrice, 'gwei') : '0',
112
+ };
113
+ } catch (err) {
114
+ return { error: err.message };
115
+ }
116
+ }