@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 +2 -1
- package/src/cli.js +142 -0
- package/src/config/keys.js +10 -2
- package/src/services/gas.js +116 -0
- package/src/services/mail.js +705 -0
- package/src/services/watch.js +159 -0
- package/src/ui/banner.js +4 -2
- package/src/wallet/history.js +96 -0
- package/src/wallet/portfolio.js +169 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darksol/terminal",
|
|
3
|
-
"version": "0.
|
|
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'],
|
package/src/config/keys.js
CHANGED
|
@@ -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
|
+
}
|