@darksol/terminal 0.3.0 → 0.3.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 +1 -1
- package/src/cli.js +192 -40
- package/src/setup/wizard.js +3 -16
- package/src/ui/banner.js +4 -2
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -294,6 +294,16 @@ export function cli(argv) {
|
|
|
294
294
|
.description('Settle payment on-chain')
|
|
295
295
|
.action((payment) => facilitatorSettle(payment));
|
|
296
296
|
|
|
297
|
+
// ═══════════════════════════════════════
|
|
298
|
+
// CHAT SHORTCUT (darksol chat = darksol ai chat)
|
|
299
|
+
// ═══════════════════════════════════════
|
|
300
|
+
program
|
|
301
|
+
.command('chat')
|
|
302
|
+
.description('Start AI trading chat (shortcut for: darksol ai chat)')
|
|
303
|
+
.option('-p, --provider <name>', 'LLM provider')
|
|
304
|
+
.option('-m, --model <model>', 'Model name')
|
|
305
|
+
.action((opts) => startChat(opts));
|
|
306
|
+
|
|
297
307
|
// ═══════════════════════════════════════
|
|
298
308
|
// SETUP COMMAND
|
|
299
309
|
// ═══════════════════════════════════════
|
|
@@ -591,7 +601,7 @@ export function cli(argv) {
|
|
|
591
601
|
});
|
|
592
602
|
|
|
593
603
|
// ═══════════════════════════════════════
|
|
594
|
-
// DASHBOARD (default)
|
|
604
|
+
// DASHBOARD (default) — commands + optional AI
|
|
595
605
|
// ═══════════════════════════════════════
|
|
596
606
|
program
|
|
597
607
|
.command('dashboard', { isDefault: true })
|
|
@@ -599,54 +609,196 @@ export function cli(argv) {
|
|
|
599
609
|
.action(async () => {
|
|
600
610
|
showBanner();
|
|
601
611
|
|
|
602
|
-
// First-run detection — offer setup wizard
|
|
603
|
-
const ranSetup = await checkFirstRun();
|
|
604
|
-
if (ranSetup) return;
|
|
605
|
-
|
|
606
612
|
const cfg = getAllConfig();
|
|
607
613
|
const wallet = cfg.activeWallet;
|
|
614
|
+
const { hasKey } = await import('./config/keys.js');
|
|
615
|
+
const hasLLM = ['openai', 'anthropic', 'openrouter', 'ollama'].some(s => hasKey(s));
|
|
616
|
+
|
|
617
|
+
// ── Status bar ──
|
|
618
|
+
const statusParts = [
|
|
619
|
+
wallet ? theme.success(`● ${wallet}`) : theme.dim('○ no wallet'),
|
|
620
|
+
theme.dim(`${cfg.chain}`),
|
|
621
|
+
theme.dim(`${cfg.slippage}% slip`),
|
|
622
|
+
hasLLM ? theme.success('● AI ready') : theme.dim('○ no AI'),
|
|
623
|
+
];
|
|
624
|
+
console.log(` ${statusParts.join(theme.dim(' │ '))}`);
|
|
625
|
+
console.log('');
|
|
626
|
+
|
|
627
|
+
// ── Commands (always shown) ──
|
|
628
|
+
showCommandList();
|
|
629
|
+
|
|
630
|
+
// ── AI nudge or chat prompt ──
|
|
631
|
+
if (hasLLM) {
|
|
632
|
+
console.log(theme.gold(' 💬 AI is ready — run ') + theme.label('darksol ai chat') + theme.gold(' or just ') + theme.label('darksol chat'));
|
|
633
|
+
console.log(theme.dim(' "swap 0.1 ETH for USDC" • "what\'s AERO at?" • any question'));
|
|
634
|
+
console.log('');
|
|
635
|
+
} else {
|
|
636
|
+
console.log(theme.dim(' 💡 Want AI-powered trading? Run ') + theme.label('darksol setup') + theme.dim(' to connect an LLM'));
|
|
637
|
+
console.log(theme.dim(' Supports OpenAI, Anthropic, OpenRouter, or Ollama (free/local)'));
|
|
638
|
+
console.log('');
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
program.parse(argv);
|
|
643
|
+
}
|
|
608
644
|
|
|
609
|
-
|
|
645
|
+
// ═══════════════════════════════════════
|
|
646
|
+
// CHAT-FIRST LOOP (default experience)
|
|
647
|
+
// ═══════════════════════════════════════
|
|
648
|
+
|
|
649
|
+
async function startChatLoop(cfg) {
|
|
650
|
+
const { createLLM } = await import('./llm/engine.js');
|
|
651
|
+
const { quickPrice } = await import('./utils/helpers.js');
|
|
652
|
+
const { executeIntent, parseIntent, INTENT_SYSTEM_PROMPT } = await import('./llm/intent.js');
|
|
653
|
+
|
|
654
|
+
let llm;
|
|
655
|
+
try {
|
|
656
|
+
llm = await createLLM({});
|
|
657
|
+
const chain = cfg.chain || 'base';
|
|
658
|
+
const wallet = cfg.activeWallet || '(not set)';
|
|
659
|
+
const slippage = cfg.slippage || 0.5;
|
|
660
|
+
|
|
661
|
+
const systemPrompt = INTENT_SYSTEM_PROMPT
|
|
662
|
+
.replace('{{chain}}', chain)
|
|
663
|
+
.replace('{{wallet}}', wallet)
|
|
664
|
+
.replace('{{slippage}}', slippage);
|
|
665
|
+
|
|
666
|
+
llm.setSystemPrompt(systemPrompt);
|
|
667
|
+
} catch (err) {
|
|
668
|
+
error(`AI init failed: ${err.message}`);
|
|
669
|
+
info('Run: darksol setup');
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const inquirerMod = await import('inquirer');
|
|
674
|
+
const inquirerDefault = inquirerMod.default;
|
|
675
|
+
|
|
676
|
+
while (true) {
|
|
677
|
+
const { input } = await inquirerDefault.prompt([{
|
|
678
|
+
type: 'input',
|
|
679
|
+
name: 'input',
|
|
680
|
+
message: theme.gold('🌑'),
|
|
681
|
+
validate: (v) => v.length > 0 || ' ',
|
|
682
|
+
}]);
|
|
683
|
+
|
|
684
|
+
const trimmed = input.trim().toLowerCase();
|
|
685
|
+
|
|
686
|
+
// Meta-commands within chat
|
|
687
|
+
if (['exit', 'quit', 'q'].includes(trimmed)) {
|
|
688
|
+
const usage = llm.getUsage();
|
|
689
|
+
info(`Session: ${usage.calls} calls, ${usage.totalTokens} tokens`);
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (['commands', 'help', 'cmds', '?'].includes(trimmed)) {
|
|
694
|
+
showCommandList();
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (trimmed === 'status') {
|
|
610
699
|
kvDisplay([
|
|
611
|
-
['Wallet',
|
|
700
|
+
['Wallet', cfg.activeWallet || theme.dim('(none)')],
|
|
612
701
|
['Chain', cfg.chain],
|
|
702
|
+
['Provider', `${llm.provider}/${llm.model}`],
|
|
613
703
|
['Slippage', `${cfg.slippage}%`],
|
|
614
704
|
]);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
615
707
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
['wallet', 'Create, import, manage wallets'],
|
|
620
|
-
['trade', 'Swap tokens, snipe, trading'],
|
|
621
|
-
['dca', 'Dollar-cost averaging orders'],
|
|
622
|
-
['ai', 'AI trading assistant & analysis'],
|
|
623
|
-
['agent', 'Secure agent signer (PK-isolated)'],
|
|
624
|
-
['keys', 'API key vault (LLMs, data, RPCs)'],
|
|
625
|
-
['script', 'Execution scripts & strategies'],
|
|
626
|
-
['market', 'Market intel & token data'],
|
|
627
|
-
['oracle', 'On-chain random oracle'],
|
|
628
|
-
['casino', 'The Clawsino — betting'],
|
|
629
|
-
['cards', 'Prepaid Visa/MC cards'],
|
|
630
|
-
['builders', 'ERC-8021 builder index'],
|
|
631
|
-
['facilitator', 'x402 payment facilitator'],
|
|
632
|
-
['skills', 'Agent skill directory & install'],
|
|
633
|
-
['config', 'Terminal configuration'],
|
|
634
|
-
['tips', 'Trading & scripting tips'],
|
|
635
|
-
['networks', 'Chain reference & explorers'],
|
|
636
|
-
['quickstart', 'Getting started guide'],
|
|
637
|
-
['lookup', 'Look up any address on-chain'],
|
|
638
|
-
['setup', 'First-run setup wizard'],
|
|
639
|
-
];
|
|
708
|
+
// Check if it looks like a trade intent
|
|
709
|
+
const tradeWords = ['buy', 'sell', 'swap', 'snipe', 'transfer', 'send', 'trade'];
|
|
710
|
+
const isTradeIntent = tradeWords.some(w => trimmed.startsWith(w));
|
|
640
711
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
});
|
|
712
|
+
if (isTradeIntent) {
|
|
713
|
+
// Parse as trade intent with confirmation
|
|
714
|
+
const intent = await parseIntent(input, {});
|
|
715
|
+
if (intent.action !== 'error' && intent.action !== 'unknown') {
|
|
716
|
+
showSection('PARSED INTENT');
|
|
717
|
+
kvDisplay(Object.entries(intent)
|
|
718
|
+
.filter(([k]) => !['raw', 'model', 'reasoning'].includes(k))
|
|
719
|
+
.map(([k, v]) => [k, Array.isArray(v) ? v.join(', ') : String(v)])
|
|
720
|
+
);
|
|
721
|
+
if (intent.warnings?.length) intent.warnings.forEach(w => warn(w));
|
|
722
|
+
if (intent.command) info(`Command: ${theme.gold(intent.command)}`);
|
|
723
|
+
console.log('');
|
|
724
|
+
} else {
|
|
725
|
+
// Fall through to regular chat
|
|
726
|
+
await chatResponse(llm, input);
|
|
727
|
+
}
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
644
730
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
});
|
|
731
|
+
// Regular chat
|
|
732
|
+
await chatResponse(llm, input);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
650
735
|
|
|
651
|
-
|
|
736
|
+
async function chatResponse(llm, input) {
|
|
737
|
+
const { quickPrice } = await import('./utils/helpers.js');
|
|
738
|
+
const { spinner: spin } = await import('./ui/components.js');
|
|
739
|
+
const s = spin('Thinking...').start();
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
// Enrich with live price data
|
|
743
|
+
let enriched = input;
|
|
744
|
+
const tokenPattern = /\b([A-Z]{2,10})\b/g;
|
|
745
|
+
const tokens = [...new Set(input.toUpperCase().match(tokenPattern) || [])];
|
|
746
|
+
const skipTokens = ['ETH', 'THE', 'FOR', 'AND', 'BUY', 'SELL', 'DCA', 'SWAP', 'WHAT', 'PRICE', 'HOW', 'MUCH', 'NOT', 'CAN', 'YOU', 'HELP'];
|
|
747
|
+
|
|
748
|
+
const priceData = [];
|
|
749
|
+
for (const t of tokens.filter(t => !skipTokens.includes(t)).slice(0, 3)) {
|
|
750
|
+
const p = await quickPrice(t);
|
|
751
|
+
if (p) priceData.push(`${p.symbol}: $${p.price} (liq: $${p.liquidity})`);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (priceData.length > 0) {
|
|
755
|
+
enriched += `\n\n[Live data: ${priceData.join(', ')}]`;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const result = await llm.chat(enriched);
|
|
759
|
+
s.succeed('');
|
|
760
|
+
|
|
761
|
+
// Display response
|
|
762
|
+
console.log('');
|
|
763
|
+
const lines = result.content.split('\n');
|
|
764
|
+
for (const line of lines) {
|
|
765
|
+
console.log(theme.dim(' ') + line);
|
|
766
|
+
}
|
|
767
|
+
console.log('');
|
|
768
|
+
} catch (err) {
|
|
769
|
+
s.fail('Error');
|
|
770
|
+
error(err.message);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function showCommandList() {
|
|
775
|
+
console.log('');
|
|
776
|
+
showSection('COMMANDS');
|
|
777
|
+
const commands = [
|
|
778
|
+
['wallet', 'Create, import, manage wallets'],
|
|
779
|
+
['trade', 'Swap tokens, snipe, trading'],
|
|
780
|
+
['dca', 'Dollar-cost averaging orders'],
|
|
781
|
+
['ai chat', 'Standalone AI chat session'],
|
|
782
|
+
['ai execute', 'Parse + execute a trade via AI'],
|
|
783
|
+
['agent start', 'Start secure agent signer'],
|
|
784
|
+
['keys', 'API key vault'],
|
|
785
|
+
['script', 'Execution scripts & strategies'],
|
|
786
|
+
['market', 'Market intel & token data'],
|
|
787
|
+
['oracle', 'On-chain random oracle'],
|
|
788
|
+
['casino', 'The Clawsino — betting'],
|
|
789
|
+
['cards', 'Prepaid Visa/MC cards'],
|
|
790
|
+
['builders', 'ERC-8021 builder index'],
|
|
791
|
+
['facilitator', 'x402 payment facilitator'],
|
|
792
|
+
['skills', 'Agent skill directory'],
|
|
793
|
+
['setup', 'Re-run setup wizard'],
|
|
794
|
+
['config', 'Terminal configuration'],
|
|
795
|
+
];
|
|
796
|
+
|
|
797
|
+
commands.forEach(([cmd, desc]) => {
|
|
798
|
+
console.log(` ${theme.gold(cmd.padEnd(16))} ${theme.dim(desc)}`);
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
console.log('');
|
|
802
|
+
console.log(theme.dim(' Run any command: darksol <command> --help'));
|
|
803
|
+
console.log('');
|
|
652
804
|
}
|
package/src/setup/wizard.js
CHANGED
|
@@ -494,23 +494,10 @@ function showPostSetup() {
|
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
/**
|
|
497
|
-
* Quick check on startup —
|
|
497
|
+
* Quick check on startup — nudge about AI, never block
|
|
498
|
+
* Returns false always so dashboard continues to render
|
|
498
499
|
*/
|
|
499
500
|
export async function checkFirstRun() {
|
|
500
|
-
|
|
501
|
-
console.log('');
|
|
502
|
-
warn('No AI provider configured yet.');
|
|
503
|
-
const { runSetup } = await inquirer.prompt([{
|
|
504
|
-
type: 'confirm',
|
|
505
|
-
name: 'runSetup',
|
|
506
|
-
message: theme.gold('Run setup wizard?'),
|
|
507
|
-
default: true,
|
|
508
|
-
}]);
|
|
509
|
-
if (runSetup) {
|
|
510
|
-
await runSetupWizard();
|
|
511
|
-
return true;
|
|
512
|
-
}
|
|
513
|
-
info('Skip for now. Run later: darksol setup');
|
|
514
|
-
}
|
|
501
|
+
// Never block — just return false and let dashboard handle the nudge
|
|
515
502
|
return false;
|
|
516
503
|
}
|
package/src/ui/banner.js
CHANGED
|
@@ -26,7 +26,7 @@ export function showBanner(opts = {}) {
|
|
|
26
26
|
);
|
|
27
27
|
console.log(
|
|
28
28
|
theme.dim(' ║ ') +
|
|
29
|
-
theme.subtle(' v0.3.
|
|
29
|
+
theme.subtle(' v0.3.2') +
|
|
30
30
|
theme.dim(' ') +
|
|
31
31
|
theme.gold('🌑') +
|
|
32
32
|
theme.dim(' ║')
|
|
@@ -44,7 +44,7 @@ export function showBanner(opts = {}) {
|
|
|
44
44
|
|
|
45
45
|
export function showMiniBanner() {
|
|
46
46
|
console.log('');
|
|
47
|
-
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.3.
|
|
47
|
+
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.3.2'));
|
|
48
48
|
console.log(theme.dim(' ─────────────────────────────'));
|
|
49
49
|
console.log('');
|
|
50
50
|
}
|
|
@@ -63,3 +63,5 @@ export function showDivider() {
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
|
|
67
|
+
|