@darksol/terminal 0.5.4 → 0.5.6

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.5.4",
3
+ "version": "0.5.6",
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": {
package/src/llm/intent.js CHANGED
@@ -9,17 +9,36 @@ import { showSection } from '../ui/banner.js';
9
9
  // INTENT SYSTEM PROMPT
10
10
  // ──────────────────────────────────────────────────
11
11
 
12
- const INTENT_SYSTEM_PROMPT = `You are DARKSOL Terminal's trading AI assistant. You help users execute trades, send/receive tokens, analyze markets, manage DCA strategies, and navigate the DARKSOL ecosystem.
12
+ const INTENT_SYSTEM_PROMPT = `You are DARKSOL Terminal's trading AI assistant. You help users execute trades, send/receive tokens, analyze markets, manage DCA strategies, order prepaid cards, and navigate the DARKSOL ecosystem.
13
+
14
+ You are embedded in a CLI/web terminal. Your responses become actions — when you output structured JSON, the terminal executes real on-chain transactions, card orders, and wallet operations. Be precise. Be careful. Real money is at stake.
13
15
 
14
16
  CAPABILITIES:
15
- - Parse natural language into structured trade/transfer commands
17
+ - Parse natural language into structured trade/transfer/card-order commands
18
+ - Execute swaps via Uniswap V3 (Base SwapRouter02, V1 on ETH/Arb/OP/Polygon)
19
+ - Send ETH and ERC-20 tokens to any address
20
+ - Snipe tokens via Uniswap V2 (Base, Ethereum)
21
+ - Order prepaid Visa/Mastercard cards with crypto (via Trocador)
16
22
  - Analyze token prices, liquidity, and market conditions
17
23
  - Suggest DCA strategies based on user goals
18
24
  - Explain transaction results and gas costs
19
25
  - Warn about risks (low liquidity, high slippage, unverified contracts)
20
26
 
21
27
  SUPPORTED CHAINS: Base (default), Ethereum, Polygon, Arbitrum, Optimism
22
- KNOWN TOKENS: ETH, USDC, USDT, DAI, WETH, AERO, VIRTUAL, ARB, OP, WMATIC
28
+ KNOWN TOKENS PER CHAIN:
29
+ - Base: ETH, WETH, USDC, USDbC, DAI, AERO, VIRTUAL
30
+ - Ethereum: ETH, WETH, USDC, USDT, DAI
31
+ - Arbitrum: ETH, WETH, USDC, USDT, ARB
32
+ - Optimism: ETH, WETH, USDC, OP
33
+ - Polygon: MATIC/POL (native), WETH, WMATIC, USDC, USDT
34
+
35
+ WEB3 KNOWLEDGE:
36
+ - Uniswap V3 uses fee tiers: 500 (0.05%), 3000 (0.3%), 10000 (1%). Default: 3000.
37
+ - Slippage protection: Quoter V2 gets expected output, then applies tolerance (default 0.5%).
38
+ - Token approvals: ERC-20 tokens need approval before swapping (approve → swap is 2 TXs).
39
+ - Gas: Base is very cheap (~$0.01-0.05/TX), Ethereum is expensive ($2-20+/TX).
40
+ - Never send ETH/tokens to a contract address unless you know what you're doing.
41
+ - Always verify contract addresses — don't guess or hallucinate them.
23
42
 
24
43
  USER CONTEXT:
25
44
  - Active chain: {{chain}}
@@ -45,25 +64,80 @@ ACTIONS (use the most specific one):
45
64
  - "info" — general question about a token or protocol
46
65
  - "analyze" — deep analysis of a token
47
66
  - "gas" — gas price check
67
+ - "cards" — order a prepaid Visa/Mastercard with crypto (e.g. "order a $50 card", "get me a prepaid card")
48
68
  - "unknown" — can't determine what the user wants
49
69
 
70
+ CARDS ORDERING:
71
+ When the user wants to order a prepaid card, you MUST collect:
72
+ 1. amount — ONLY these denominations: $10, $25, $50, $100, $250, $500, $1000
73
+ 2. email — delivery address for the card activation link
74
+ 3. provider — default "swype" (Global Mastercard). Also: "mpc" (US Mastercard), "reward" (US Visa)
75
+ 4. ticker — payment crypto, default "usdc"
76
+
77
+ VERIFIED PAYMENT METHODS (ONLY these work — reject anything else):
78
+ - usdc on base (DEFAULT — cheapest, fastest)
79
+ - usdc on ERC20 (Ethereum — higher gas)
80
+ - usdt on trc20 (Tron — cheap)
81
+ - btc on Mainnet (Bitcoin)
82
+ - eth on ERC20 (Ethereum)
83
+ - sol on Mainnet (Solana)
84
+ - xmr on Mainnet (Monero)
85
+
86
+ ⚠ DO NOT accept: eth/base, sol/sol, usdc/polygon, usdt/eth, or any combo not listed above.
87
+ If the user asks for an unsupported combo (like "pay with ETH on Base"), tell them it's not available and suggest alternatives.
88
+
89
+ If the user says "order me a $50 card" but doesn't provide an email, set "needsInfo": ["email"] and ask: "What email should I send the card activation link to?"
90
+ If they mention AgentMail or "my email", suggest using their configured agent email.
91
+
50
92
  When parsing, respond with ONLY valid JSON:
51
93
  {
52
- "action": "swap|send|snipe|dca|price|balance|info|analyze|gas|unknown",
94
+ "action": "swap|send|snipe|dca|price|balance|info|analyze|gas|cards|unknown",
53
95
  "tokenIn": "symbol or address (for swaps)",
54
96
  "tokenOut": "symbol or address (for swaps)",
55
97
  "token": "symbol (for send/price/analyze)",
56
98
  "amount": "number as string",
57
99
  "to": "recipient address (for send)",
100
+ "email": "delivery email (for cards)",
101
+ "provider": "card provider (for cards, default: swype)",
102
+ "ticker": "payment crypto (for cards, default: usdc)",
58
103
  "chain": "chain name if specified, null if not",
59
104
  "interval": "for DCA: 1h, 4h, 1d, etc.",
60
105
  "orders": "for DCA: number of orders",
61
106
  "confidence": 0.0-1.0,
62
107
  "reasoning": "brief explanation of interpretation",
63
108
  "warnings": ["array of risk warnings"],
109
+ "needsInfo": ["array of missing fields the AI should ask about"],
110
+ "followUp": "natural language question to ask the user if info is missing",
64
111
  "command": "the exact darksol CLI command to run"
65
112
  }
66
113
 
114
+ CONVERSATIONAL RULES:
115
+ - 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.
116
+ - Be conversational — "What email should I send the card to?" not "Error: email required"
117
+ - If they mention AgentMail or "my email", suggest using their configured agent email
118
+ - For cards without a specified provider, default to "swype" (global Mastercard)
119
+ - For swaps without a specified chain, use the user's active chain ({{chain}})
120
+ - Never hallucinate contract addresses — if you don't know it, say so
121
+ - When a user asks "how do I..." give them the exact darksol CLI command
122
+
123
+ AGENT/TOOL-USE BEHAVIOR:
124
+ When an AI agent (like OpenClaw) is using this terminal programmatically:
125
+ - Always return structured JSON for actionable intents
126
+ - Include the exact "command" field so the agent can run it
127
+ - Include "warnings" for anything risky (high value, unverified token, etc.)
128
+ - If confidence < 0.6, ask for clarification rather than guessing
129
+ - For card orders: validate amount is in [10,25,50,100,250,500,1000] and ticker is verified
130
+ - For swaps: validate both tokens are known symbols or valid 0x addresses
131
+ - For sends: validate "to" looks like a valid address (0x + 40 hex chars)
132
+
133
+ ERROR GUIDANCE:
134
+ When something fails, help the user fix it:
135
+ - "CALL_EXCEPTION" → likely an RPC issue, suggest switching RPCs
136
+ - "insufficient funds" → tell them their balance, suggest a lower amount
137
+ - "coin not found" → the crypto/network combo isn't supported, list what works
138
+ - "nonce" → pending transaction, wait and retry
139
+ - Don't just say "error" — explain what went wrong and what to do next
140
+
67
141
  COMMAND MAPPING:
68
142
  - swap → darksol trade swap -i <tokenIn> -o <tokenOut> -a <amount>
69
143
  - send → darksol send --to <address> --amount <amount> --token <token>
@@ -72,6 +146,7 @@ COMMAND MAPPING:
72
146
  - price → darksol price <token>
73
147
  - balance → darksol wallet balance
74
148
  - gas → darksol gas <chain>
149
+ - cards → darksol cards order -p <provider> -a <amount> -e <email> --ticker <crypto>
75
150
  - analyze → darksol ai analyze <token>`;
76
151
 
77
152
  // ──────────────────────────────────────────────────
@@ -217,7 +292,7 @@ export async function startChat(opts = {}) {
217
292
  }
218
293
 
219
294
  // Try to detect actionable intent
220
- const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas)\b/i;
295
+ const actionKeywords = /\b(swap|send|transfer|buy|sell|snipe|dca|price|balance|gas|card|cards|order|prepaid|visa|mastercard)\b/i;
221
296
  const isActionable = actionKeywords.test(input);
222
297
 
223
298
  let result;
@@ -456,8 +531,27 @@ export async function executeIntent(intent, opts = {}) {
456
531
  }
457
532
 
458
533
  try {
534
+ // Check if the AI needs more info before executing
535
+ if (intent.needsInfo?.length > 0) {
536
+ if (intent.followUp) {
537
+ console.log('');
538
+ console.log(theme.gold(' DARKSOL AI:'));
539
+ console.log(theme.dim(' ') + intent.followUp);
540
+ console.log('');
541
+ }
542
+ return { success: false, reason: 'needs_info', needsInfo: intent.needsInfo, followUp: intent.followUp };
543
+ }
544
+
459
545
  switch (intent.action) {
460
546
  case 'swap': {
547
+ if (!intent.tokenIn || !intent.tokenOut) {
548
+ info('I need to know what tokens to swap. Example: "swap 0.1 ETH to USDC"');
549
+ return { success: false, reason: 'Missing token pair — tell me what to swap from and to.' };
550
+ }
551
+ if (!intent.amount) {
552
+ info('How much do you want to swap? Example: "swap 0.1 ETH to USDC"');
553
+ return { success: false, reason: 'Missing amount — how much do you want to swap?' };
554
+ }
461
555
  const { executeSwap } = await import('../trading/swap.js');
462
556
  return await executeSwap({
463
557
  tokenIn: intent.tokenIn,
@@ -469,29 +563,40 @@ export async function executeIntent(intent, opts = {}) {
469
563
  }
470
564
 
471
565
  case 'snipe': {
472
- const { executeSnipe } = await import('../trading/snipe.js');
473
- return await executeSnipe({
474
- token: intent.tokenOut || intent.tokenIn,
475
- amount: intent.amount,
566
+ const snipeToken = intent.tokenOut || intent.tokenIn;
567
+ if (!snipeToken || !snipeToken.startsWith('0x')) {
568
+ info('Snipe needs a contract address. Example: "snipe 0x1234... with 0.1 ETH"');
569
+ return { success: false, reason: 'I need a token contract address to snipe.' };
570
+ }
571
+ const { snipeToken: doSnipe } = await import('../trading/snipe.js');
572
+ return await doSnipe(snipeToken, intent.amount || '0.01', {
476
573
  chain: intent.chain,
477
- gasMultiplier: intent.gasMultiplier,
478
- password: opts.password,
574
+ slippage: intent.slippage,
575
+ gas: intent.gasMultiplier,
479
576
  });
480
577
  }
481
578
 
482
579
  case 'dca': {
483
- const { createDCAOrder } = await import('../trading/dca.js');
484
- return await createDCAOrder({
485
- token: intent.tokenOut || intent.tokenIn,
580
+ const { createDCA } = await import('../trading/dca.js');
581
+ return await createDCA({
582
+ tokenOut: intent.tokenOut || intent.tokenIn,
486
583
  amount: intent.amount,
487
584
  interval: intent.interval || '1h',
488
- orders: intent.orders || 10,
585
+ totalOrders: intent.orders || 10,
489
586
  chain: intent.chain,
490
587
  });
491
588
  }
492
589
 
493
590
  case 'send':
494
591
  case 'transfer': {
592
+ if (!intent.to) {
593
+ info('Where should I send it? Give me a wallet address (0x...)');
594
+ return { success: false, reason: 'Missing recipient address — who are you sending to?' };
595
+ }
596
+ if (!intent.amount) {
597
+ info('How much? Example: "send 10 USDC to 0x..."');
598
+ return { success: false, reason: 'Missing amount — how much do you want to send?' };
599
+ }
495
600
  const { sendFunds } = await import('../wallet/manager.js');
496
601
  return await sendFunds({
497
602
  to: intent.to,
@@ -507,8 +612,8 @@ export async function executeIntent(intent, opts = {}) {
507
612
  await checkPrices([token]);
508
613
  return { success: true, action: 'price' };
509
614
  }
510
- info('No token specified');
511
- return { success: false, reason: 'no token specified' };
615
+ info('Which token? Example: "price ETH" or "how much is AERO"');
616
+ return { success: false, reason: 'Which token do you want the price for?' };
512
617
  }
513
618
 
514
619
  case 'balance': {
@@ -523,6 +628,23 @@ export async function executeIntent(intent, opts = {}) {
523
628
  return { success: true, action: 'gas' };
524
629
  }
525
630
 
631
+ case 'cards': {
632
+ if (!intent.amount) {
633
+ info('What denomination? We have $10, $25, $50, $100, $250, $500, $1000');
634
+ return { success: false, reason: 'What card amount do you want?' };
635
+ }
636
+ if (!intent.email) {
637
+ info('I need an email to deliver the card activation link to.');
638
+ return { success: false, reason: 'What email should I send the card to?' };
639
+ }
640
+ const { cardsOrder } = await import('../services/cards.js');
641
+ return await cardsOrder(intent.provider || 'swype', intent.amount, {
642
+ email: intent.email,
643
+ ticker: intent.ticker || 'usdc',
644
+ network: intent.network,
645
+ });
646
+ }
647
+
526
648
  case 'info':
527
649
  case 'analyze': {
528
650
  const token = intent.tokenOut || intent.tokenIn || intent.token;
@@ -530,20 +652,37 @@ export async function executeIntent(intent, opts = {}) {
530
652
  await analyzeToken(token, opts);
531
653
  return { success: true, action: 'analyze' };
532
654
  }
533
- info('No token specified for analysis');
534
- return { success: false, reason: 'no token specified' };
655
+ info('Which token do you want me to analyze?');
656
+ return { success: false, reason: 'Tell me which token to look at.' };
535
657
  }
536
658
 
537
659
  default:
538
- warn(`Action "${intent.action}" not yet wired for execution`);
660
+ warn(`I don't know how to do "${intent.action}" yet.`);
539
661
  if (intent.command) {
540
- info(`Suggested command: ${theme.gold(intent.command)}`);
662
+ info(`Try running: ${theme.gold(intent.command)}`);
541
663
  }
542
- return { success: false, reason: `unhandled action: ${intent.action}` };
664
+ return { success: false, reason: `Action "${intent.action}" isn't wired up yet.` };
543
665
  }
544
666
  } catch (err) {
545
- error(`Execution failed: ${err.message}`);
546
- return { success: false, error: err.message };
667
+ // Human-readable error messages
668
+ const msg = err.message || String(err);
669
+ if (msg.includes('CALL_EXCEPTION')) {
670
+ error('The on-chain call failed. This usually means the RPC is having issues or the contract call reverted.');
671
+ info('Try again in a moment, or switch to a different RPC with: darksol config set rpcs.base <url>');
672
+ } else if (msg.includes('insufficient funds') || msg.includes('Insufficient')) {
673
+ error('Not enough funds in your wallet for this transaction (including gas fees).');
674
+ } else if (msg.includes('nonce')) {
675
+ error('Transaction nonce conflict. You may have a pending transaction — wait for it to confirm or try again.');
676
+ } else if (msg.includes('timeout') || msg.includes('ETIMEDOUT')) {
677
+ error('Network timeout — the RPC server didn\'t respond in time. Try again or switch RPCs.');
678
+ } else if (msg.includes('could not detect network')) {
679
+ error('Can\'t connect to the blockchain. Check your internet connection and RPC settings.');
680
+ } else if (msg.includes('password') || msg.includes('decrypt')) {
681
+ error('Wrong wallet password. The private key couldn\'t be decrypted.');
682
+ } else {
683
+ error(`Failed: ${msg}`);
684
+ }
685
+ return { success: false, error: msg };
547
686
  }
548
687
  }
549
688
 
@@ -6,40 +6,80 @@ import { showSection } from '../ui/banner.js';
6
6
 
7
7
  const BASE = () => getServiceURL('cards') || 'https://acp.darksol.net';
8
8
 
9
- // Verified working ticker/network combos (tested against Trocador API)
10
- const VALID_CRYPTO = {
11
- 'usdc': { network: 'base', display: 'USDC on Base' },
12
- 'usdc_base': { ticker: 'usdc', network: 'base', display: 'USDC on Base' },
13
- 'usdc_erc20': { ticker: 'usdc', network: 'ERC20', display: 'USDC on Ethereum' },
14
- 'usdt': { network: 'trc20', display: 'USDT on Tron' },
15
- 'usdt_trc20': { ticker: 'usdt', network: 'trc20', display: 'USDT on Tron' },
16
- 'btc': { network: 'Mainnet', display: 'Bitcoin' },
17
- 'eth': { network: 'ERC20', display: 'ETH on Ethereum' },
18
- 'sol': { network: 'Mainnet', display: 'Solana' },
19
- 'xmr': { network: 'Mainnet', display: 'Monero' },
9
+ // ══════════════════════════════════════════════════
10
+ // VERIFIED CRYPTO COMBOS (tested against Trocador API 2026-03-09)
11
+ // Only these combos are allowed everything else is rejected before hitting the API
12
+ // ══════════════════════════════════════════════════
13
+ const VERIFIED_COMBOS = [
14
+ { ticker: 'usdc', network: 'base', display: 'USDC on Base', default: true },
15
+ { ticker: 'usdc', network: 'ERC20', display: 'USDC on Ethereum' },
16
+ { ticker: 'usdt', network: 'trc20', display: 'USDT on Tron (TRC-20)' },
17
+ { ticker: 'btc', network: 'Mainnet', display: 'Bitcoin (BTC)' },
18
+ { ticker: 'eth', network: 'ERC20', display: 'Ethereum (ETH)' },
19
+ { ticker: 'sol', network: 'Mainnet', display: 'Solana (SOL)' },
20
+ { ticker: 'xmr', network: 'Mainnet', display: 'Monero (XMR)' },
21
+ ];
22
+
23
+ // Aliases: what users might type → what Trocador expects
24
+ const TICKER_ALIASES = {
25
+ 'ethereum': 'eth', 'ether': 'eth',
26
+ 'bitcoin': 'btc',
27
+ 'solana': 'sol',
28
+ 'monero': 'xmr',
29
+ 'tether': 'usdt',
30
+ 'usd coin': 'usdc', 'usd-c': 'usdc',
20
31
  };
21
32
 
22
- // Map user-friendly network names to Trocador-compatible ones
23
- const NETWORK_MAP = {
33
+ const NETWORK_ALIASES = {
24
34
  'base': 'base',
25
- 'ethereum': 'ERC20', 'eth': 'ERC20', 'erc20': 'ERC20',
26
- 'tron': 'trc20', 'trc20': 'trc20',
27
- 'mainnet': 'Mainnet', 'btc': 'Mainnet', 'sol': 'Mainnet', 'xmr': 'Mainnet',
35
+ 'ethereum': 'ERC20', 'eth': 'ERC20', 'erc20': 'ERC20', 'erc-20': 'ERC20',
36
+ 'tron': 'trc20', 'trc20': 'trc20', 'trc-20': 'trc20',
37
+ 'mainnet': 'Mainnet', 'main': 'Mainnet',
28
38
  };
29
39
 
40
+ const VALID_AMOUNTS = [10, 25, 50, 100, 250, 500, 1000];
41
+
42
+ /**
43
+ * Resolve user input to a verified ticker/network combo.
44
+ * Returns { ticker, network, display } or null if invalid.
45
+ */
30
46
  function resolveTickerNetwork(ticker, network) {
31
- const t = (ticker || '').toLowerCase();
32
- const n = (network || '').toLowerCase();
47
+ const t = TICKER_ALIASES[(ticker || '').toLowerCase()] || (ticker || '').toLowerCase();
33
48
 
34
- // If just ticker provided, use known defaults
35
- if (t && !n) {
36
- const known = VALID_CRYPTO[t];
37
- if (known) return { ticker: known.ticker || t, network: known.network };
49
+ // If network specified, try to match exactly
50
+ if (network) {
51
+ const n = NETWORK_ALIASES[(network || '').toLowerCase()] || network;
52
+ const match = VERIFIED_COMBOS.find(c => c.ticker === t && c.network === n);
53
+ if (match) return match;
54
+ // Try just the ticker with its default network
38
55
  }
39
56
 
40
- // Map network to Trocador format
41
- const mappedNetwork = NETWORK_MAP[n] || network;
42
- return { ticker: t, network: mappedNetwork };
57
+ // Just ticker find the verified default for it
58
+ const match = VERIFIED_COMBOS.find(c => c.ticker === t);
59
+ if (match) return match;
60
+
61
+ return null; // Not a verified combo
62
+ }
63
+
64
+ /**
65
+ * Get all verified combos (for menus / agent listings)
66
+ */
67
+ export function getVerifiedCombos() {
68
+ return VERIFIED_COMBOS;
69
+ }
70
+
71
+ /**
72
+ * Get the default combo
73
+ */
74
+ export function getDefaultCombo() {
75
+ return VERIFIED_COMBOS.find(c => c.default) || VERIFIED_COMBOS[0];
76
+ }
77
+
78
+ /**
79
+ * Get valid amounts
80
+ */
81
+ export function getValidAmounts() {
82
+ return VALID_AMOUNTS;
43
83
  }
44
84
 
45
85
  export async function cardsCatalog() {
@@ -87,19 +127,40 @@ export async function cardsOrder(provider, amount, opts = {}) {
87
127
  return;
88
128
  }
89
129
 
130
+ // Validate amount
131
+ const numAmount = Number(amount);
132
+ if (!VALID_AMOUNTS.includes(numAmount)) {
133
+ error(`Invalid card amount: $${amount}. Valid amounts: ${VALID_AMOUNTS.map(a => '$' + a).join(', ')}`);
134
+ return;
135
+ }
136
+
137
+ // Validate provider
138
+ const validProviders = ['swype', 'mpc', 'reward'];
139
+ if (!validProviders.includes((provider || '').toLowerCase())) {
140
+ error(`Invalid provider: ${provider}. Options: swype (Global MC), mpc (US MC), reward (US Visa)`);
141
+ return;
142
+ }
143
+
90
144
  const spin = spinner('Processing card order...').start();
91
145
  try {
92
146
  const body = {
93
- provider,
94
- amount: Number(amount),
147
+ provider: provider.toLowerCase(),
148
+ amount: numAmount,
95
149
  email: opts.email,
96
150
  };
97
- // Resolve ticker/network to Trocador-compatible values
151
+
152
+ // Resolve and validate crypto
98
153
  if (opts.ticker) {
99
154
  const resolved = resolveTickerNetwork(opts.ticker, opts.network);
155
+ if (!resolved) {
156
+ spin.fail('Invalid payment method');
157
+ error(`"${opts.ticker}${opts.network ? '/' + opts.network : ''}" is not a supported payment option.`);
158
+ info('Supported: ' + VERIFIED_COMBOS.map(c => c.display).join(', '));
159
+ return;
160
+ }
100
161
  body.tickerFrom = resolved.ticker;
101
162
  body.networkFrom = resolved.network;
102
- info(`Payment: ${resolved.ticker.toUpperCase()} on ${resolved.network}`);
163
+ info(`Payment: ${resolved.display}`);
103
164
  }
104
165
 
105
166
  const data = await fetchJSON(`${BASE()}/api/cards/order`, {
@@ -215,9 +215,12 @@ export async function handleMenuSelect(id, value, item, ws) {
215
215
 
216
216
  case 'cards_crypto':
217
217
  if (value === 'back') return {};
218
- // Execute the order
218
+ // Execute the order with verified combo
219
219
  return await executeCardOrder(item?.meta || {}, ws);
220
220
 
221
+ case 'cards_status_check':
222
+ return await showCardStatus(value, ws);
223
+
221
224
  case 'agent_action':
222
225
  if (value === 'start') {
223
226
  const { listWallets } = await import('../wallet/keystore.js');
@@ -454,7 +457,7 @@ export async function handleCommand(cmd, ws) {
454
457
  return await cmdChatLogs(args, ws);
455
458
  default: {
456
459
  // 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;
460
+ 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
461
  if (nlKeywords.test(cmd)) {
459
462
  return await cmdAI(cmd.split(/\s+/), ws);
460
463
  }