@darksol/terminal 0.5.4 → 0.5.5
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 +1 -1
- package/src/llm/intent.js +110 -21
- package/src/web/commands.js +1 -1
package/package.json
CHANGED
package/src/llm/intent.js
CHANGED
|
@@ -45,25 +45,49 @@ ACTIONS (use the most specific one):
|
|
|
45
45
|
- "info" — general question about a token or protocol
|
|
46
46
|
- "analyze" — deep analysis of a token
|
|
47
47
|
- "gas" — gas price check
|
|
48
|
+
- "cards" — order a prepaid Visa/Mastercard with crypto (e.g. "order a $50 card", "get me a prepaid card")
|
|
48
49
|
- "unknown" — can't determine what the user wants
|
|
49
50
|
|
|
51
|
+
CARDS ORDERING:
|
|
52
|
+
When the user wants to order a prepaid card, you MUST collect:
|
|
53
|
+
1. amount (USD denomination: 10, 25, 50, 100, 250, 500, 1000)
|
|
54
|
+
2. email (delivery address for the card activation link)
|
|
55
|
+
3. provider (default: "swype" for global, "mpc" or "reward" for US-only)
|
|
56
|
+
4. ticker (payment crypto, default: "usdc")
|
|
57
|
+
|
|
58
|
+
If the user says "order me a $50 card" but doesn't provide an email, set "needsInfo": ["email"] and ask naturally.
|
|
59
|
+
If they have AgentMail configured, suggest using their agent email as an option.
|
|
60
|
+
Providers: swype (Mastercard, Global), mpc (Mastercard, US), reward (Visa, US).
|
|
61
|
+
Accepted crypto: usdc (default), usdt, btc, eth, sol, xmr.
|
|
62
|
+
|
|
50
63
|
When parsing, respond with ONLY valid JSON:
|
|
51
64
|
{
|
|
52
|
-
"action": "swap|send|snipe|dca|price|balance|info|analyze|gas|unknown",
|
|
65
|
+
"action": "swap|send|snipe|dca|price|balance|info|analyze|gas|cards|unknown",
|
|
53
66
|
"tokenIn": "symbol or address (for swaps)",
|
|
54
67
|
"tokenOut": "symbol or address (for swaps)",
|
|
55
68
|
"token": "symbol (for send/price/analyze)",
|
|
56
69
|
"amount": "number as string",
|
|
57
70
|
"to": "recipient address (for send)",
|
|
71
|
+
"email": "delivery email (for cards)",
|
|
72
|
+
"provider": "card provider (for cards, default: swype)",
|
|
73
|
+
"ticker": "payment crypto (for cards, default: usdc)",
|
|
58
74
|
"chain": "chain name if specified, null if not",
|
|
59
75
|
"interval": "for DCA: 1h, 4h, 1d, etc.",
|
|
60
76
|
"orders": "for DCA: number of orders",
|
|
61
77
|
"confidence": 0.0-1.0,
|
|
62
78
|
"reasoning": "brief explanation of interpretation",
|
|
63
79
|
"warnings": ["array of risk warnings"],
|
|
80
|
+
"needsInfo": ["array of missing fields the AI should ask about"],
|
|
81
|
+
"followUp": "natural language question to ask the user if info is missing",
|
|
64
82
|
"command": "the exact darksol CLI command to run"
|
|
65
83
|
}
|
|
66
84
|
|
|
85
|
+
CONVERSATIONAL RULES:
|
|
86
|
+
- If the user's request is missing required info (email for cards, address for send, amount for swap), DON'T set action to "unknown". Set the correct action, list what's missing in "needsInfo", and write a natural "followUp" question.
|
|
87
|
+
- Be conversational — "What email should I send the card to?" not "Error: email required"
|
|
88
|
+
- If they mention AgentMail or "my email", suggest using their configured agent email
|
|
89
|
+
- For cards without a specified provider, default to "swype" (global Mastercard)
|
|
90
|
+
|
|
67
91
|
COMMAND MAPPING:
|
|
68
92
|
- swap → darksol trade swap -i <tokenIn> -o <tokenOut> -a <amount>
|
|
69
93
|
- send → darksol send --to <address> --amount <amount> --token <token>
|
|
@@ -72,6 +96,7 @@ COMMAND MAPPING:
|
|
|
72
96
|
- price → darksol price <token>
|
|
73
97
|
- balance → darksol wallet balance
|
|
74
98
|
- gas → darksol gas <chain>
|
|
99
|
+
- cards → darksol cards order -p <provider> -a <amount> -e <email> --ticker <crypto>
|
|
75
100
|
- analyze → darksol ai analyze <token>`;
|
|
76
101
|
|
|
77
102
|
// ──────────────────────────────────────────────────
|
|
@@ -217,7 +242,7 @@ export async function startChat(opts = {}) {
|
|
|
217
242
|
}
|
|
218
243
|
|
|
219
244
|
// Try to detect actionable intent
|
|
220
|
-
const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas)\b/i;
|
|
245
|
+
const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas|card|cards|order|prepaid|visa|mastercard)\b/i;
|
|
221
246
|
const isActionable = actionKeywords.test(input);
|
|
222
247
|
|
|
223
248
|
let result;
|
|
@@ -456,8 +481,27 @@ export async function executeIntent(intent, opts = {}) {
|
|
|
456
481
|
}
|
|
457
482
|
|
|
458
483
|
try {
|
|
484
|
+
// Check if the AI needs more info before executing
|
|
485
|
+
if (intent.needsInfo?.length > 0) {
|
|
486
|
+
if (intent.followUp) {
|
|
487
|
+
console.log('');
|
|
488
|
+
console.log(theme.gold(' DARKSOL AI:'));
|
|
489
|
+
console.log(theme.dim(' ') + intent.followUp);
|
|
490
|
+
console.log('');
|
|
491
|
+
}
|
|
492
|
+
return { success: false, reason: 'needs_info', needsInfo: intent.needsInfo, followUp: intent.followUp };
|
|
493
|
+
}
|
|
494
|
+
|
|
459
495
|
switch (intent.action) {
|
|
460
496
|
case 'swap': {
|
|
497
|
+
if (!intent.tokenIn || !intent.tokenOut) {
|
|
498
|
+
info('I need to know what tokens to swap. Example: "swap 0.1 ETH to USDC"');
|
|
499
|
+
return { success: false, reason: 'Missing token pair — tell me what to swap from and to.' };
|
|
500
|
+
}
|
|
501
|
+
if (!intent.amount) {
|
|
502
|
+
info('How much do you want to swap? Example: "swap 0.1 ETH to USDC"');
|
|
503
|
+
return { success: false, reason: 'Missing amount — how much do you want to swap?' };
|
|
504
|
+
}
|
|
461
505
|
const { executeSwap } = await import('../trading/swap.js');
|
|
462
506
|
return await executeSwap({
|
|
463
507
|
tokenIn: intent.tokenIn,
|
|
@@ -469,29 +513,40 @@ export async function executeIntent(intent, opts = {}) {
|
|
|
469
513
|
}
|
|
470
514
|
|
|
471
515
|
case 'snipe': {
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
516
|
+
const snipeToken = intent.tokenOut || intent.tokenIn;
|
|
517
|
+
if (!snipeToken || !snipeToken.startsWith('0x')) {
|
|
518
|
+
info('Snipe needs a contract address. Example: "snipe 0x1234... with 0.1 ETH"');
|
|
519
|
+
return { success: false, reason: 'I need a token contract address to snipe.' };
|
|
520
|
+
}
|
|
521
|
+
const { snipeToken: doSnipe } = await import('../trading/snipe.js');
|
|
522
|
+
return await doSnipe(snipeToken, intent.amount || '0.01', {
|
|
476
523
|
chain: intent.chain,
|
|
477
|
-
|
|
478
|
-
|
|
524
|
+
slippage: intent.slippage,
|
|
525
|
+
gas: intent.gasMultiplier,
|
|
479
526
|
});
|
|
480
527
|
}
|
|
481
528
|
|
|
482
529
|
case 'dca': {
|
|
483
|
-
const {
|
|
484
|
-
return await
|
|
485
|
-
|
|
530
|
+
const { createDCA } = await import('../trading/dca.js');
|
|
531
|
+
return await createDCA({
|
|
532
|
+
tokenOut: intent.tokenOut || intent.tokenIn,
|
|
486
533
|
amount: intent.amount,
|
|
487
534
|
interval: intent.interval || '1h',
|
|
488
|
-
|
|
535
|
+
totalOrders: intent.orders || 10,
|
|
489
536
|
chain: intent.chain,
|
|
490
537
|
});
|
|
491
538
|
}
|
|
492
539
|
|
|
493
540
|
case 'send':
|
|
494
541
|
case 'transfer': {
|
|
542
|
+
if (!intent.to) {
|
|
543
|
+
info('Where should I send it? Give me a wallet address (0x...)');
|
|
544
|
+
return { success: false, reason: 'Missing recipient address — who are you sending to?' };
|
|
545
|
+
}
|
|
546
|
+
if (!intent.amount) {
|
|
547
|
+
info('How much? Example: "send 10 USDC to 0x..."');
|
|
548
|
+
return { success: false, reason: 'Missing amount — how much do you want to send?' };
|
|
549
|
+
}
|
|
495
550
|
const { sendFunds } = await import('../wallet/manager.js');
|
|
496
551
|
return await sendFunds({
|
|
497
552
|
to: intent.to,
|
|
@@ -507,8 +562,8 @@ export async function executeIntent(intent, opts = {}) {
|
|
|
507
562
|
await checkPrices([token]);
|
|
508
563
|
return { success: true, action: 'price' };
|
|
509
564
|
}
|
|
510
|
-
info('
|
|
511
|
-
return { success: false, reason: '
|
|
565
|
+
info('Which token? Example: "price ETH" or "how much is AERO"');
|
|
566
|
+
return { success: false, reason: 'Which token do you want the price for?' };
|
|
512
567
|
}
|
|
513
568
|
|
|
514
569
|
case 'balance': {
|
|
@@ -523,6 +578,23 @@ export async function executeIntent(intent, opts = {}) {
|
|
|
523
578
|
return { success: true, action: 'gas' };
|
|
524
579
|
}
|
|
525
580
|
|
|
581
|
+
case 'cards': {
|
|
582
|
+
if (!intent.amount) {
|
|
583
|
+
info('What denomination? We have $10, $25, $50, $100, $250, $500, $1000');
|
|
584
|
+
return { success: false, reason: 'What card amount do you want?' };
|
|
585
|
+
}
|
|
586
|
+
if (!intent.email) {
|
|
587
|
+
info('I need an email to deliver the card activation link to.');
|
|
588
|
+
return { success: false, reason: 'What email should I send the card to?' };
|
|
589
|
+
}
|
|
590
|
+
const { cardsOrder } = await import('../services/cards.js');
|
|
591
|
+
return await cardsOrder(intent.provider || 'swype', intent.amount, {
|
|
592
|
+
email: intent.email,
|
|
593
|
+
ticker: intent.ticker || 'usdc',
|
|
594
|
+
network: intent.network,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
526
598
|
case 'info':
|
|
527
599
|
case 'analyze': {
|
|
528
600
|
const token = intent.tokenOut || intent.tokenIn || intent.token;
|
|
@@ -530,20 +602,37 @@ export async function executeIntent(intent, opts = {}) {
|
|
|
530
602
|
await analyzeToken(token, opts);
|
|
531
603
|
return { success: true, action: 'analyze' };
|
|
532
604
|
}
|
|
533
|
-
info('
|
|
534
|
-
return { success: false, reason: '
|
|
605
|
+
info('Which token do you want me to analyze?');
|
|
606
|
+
return { success: false, reason: 'Tell me which token to look at.' };
|
|
535
607
|
}
|
|
536
608
|
|
|
537
609
|
default:
|
|
538
|
-
warn(`
|
|
610
|
+
warn(`I don't know how to do "${intent.action}" yet.`);
|
|
539
611
|
if (intent.command) {
|
|
540
|
-
info(`
|
|
612
|
+
info(`Try running: ${theme.gold(intent.command)}`);
|
|
541
613
|
}
|
|
542
|
-
return { success: false, reason: `
|
|
614
|
+
return { success: false, reason: `Action "${intent.action}" isn't wired up yet.` };
|
|
543
615
|
}
|
|
544
616
|
} catch (err) {
|
|
545
|
-
error
|
|
546
|
-
|
|
617
|
+
// Human-readable error messages
|
|
618
|
+
const msg = err.message || String(err);
|
|
619
|
+
if (msg.includes('CALL_EXCEPTION')) {
|
|
620
|
+
error('The on-chain call failed. This usually means the RPC is having issues or the contract call reverted.');
|
|
621
|
+
info('Try again in a moment, or switch to a different RPC with: darksol config set rpcs.base <url>');
|
|
622
|
+
} else if (msg.includes('insufficient funds') || msg.includes('Insufficient')) {
|
|
623
|
+
error('Not enough funds in your wallet for this transaction (including gas fees).');
|
|
624
|
+
} else if (msg.includes('nonce')) {
|
|
625
|
+
error('Transaction nonce conflict. You may have a pending transaction — wait for it to confirm or try again.');
|
|
626
|
+
} else if (msg.includes('timeout') || msg.includes('ETIMEDOUT')) {
|
|
627
|
+
error('Network timeout — the RPC server didn\'t respond in time. Try again or switch RPCs.');
|
|
628
|
+
} else if (msg.includes('could not detect network')) {
|
|
629
|
+
error('Can\'t connect to the blockchain. Check your internet connection and RPC settings.');
|
|
630
|
+
} else if (msg.includes('password') || msg.includes('decrypt')) {
|
|
631
|
+
error('Wrong wallet password. The private key couldn\'t be decrypted.');
|
|
632
|
+
} else {
|
|
633
|
+
error(`Failed: ${msg}`);
|
|
634
|
+
}
|
|
635
|
+
return { success: false, error: msg };
|
|
547
636
|
}
|
|
548
637
|
}
|
|
549
638
|
|
package/src/web/commands.js
CHANGED
|
@@ -454,7 +454,7 @@ export async function handleCommand(cmd, ws) {
|
|
|
454
454
|
return await cmdChatLogs(args, ws);
|
|
455
455
|
default: {
|
|
456
456
|
// Fuzzy: if it looks like natural language, route to AI
|
|
457
|
-
const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca)\b/i;
|
|
457
|
+
const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca|order|card|prepaid|visa|mastercard)\b/i;
|
|
458
458
|
if (nlKeywords.test(cmd)) {
|
|
459
459
|
return await cmdAI(cmd.split(/\s+/), ws);
|
|
460
460
|
}
|