UNPKG

behemoth-cli

Version:

šŸŒ BEHEMOTH CLIv3.760.4 - Level 50+ POST-SINGULARITY Intelligence Trading AI

376 lines (324 loc) • 15.2 kB
#!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; import { render } from 'ink'; import React from 'react'; import { Agent } from './agent.js'; import App from '../ui/App.js'; import { initializeBehemothMCP } from '../tools/behemoth-tools.js'; import { CLI_FEATURES } from '../utils/cli-config.js'; import { parseNaturalLanguage, generateParsingResponse } from './nlp.js'; import { MultiProviderConfigManager } from '../utils/multi-provider-config.js'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import * as readline from 'readline'; const program = new Command(); /** * Prompt user for input */ function askQuestion(question: string): Promise<string> { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }); }); } /** * Ensure first-run setup is completed */ async function ensureSetup(): Promise<void> { const configDir = path.join(os.homedir(), '.cliv2'); const providerConfigPath = path.join(configDir, 'providers.json'); // Check if config exists or needs updating let needsSetup = false; let existingConfig = null; if (!fs.existsSync(providerConfigPath)) { needsSetup = true; } else { // Check if existing config needs API key prompts try { const configData = fs.readFileSync(providerConfigPath, 'utf8'); existingConfig = JSON.parse(configData); // Check if we need to prompt for API keys (empty keys or old default model) const hasEmptyKeys = Object.values(existingConfig.providers || {}).some( (provider: any) => provider.apiKey === "" ); const hasOldDefault = existingConfig.providers?.openrouter?.defaultModel !== 'deepseek/deepseek-chat-v3-0324:free'; if (hasEmptyKeys || hasOldDefault) { console.log(chalk.cyan('šŸ”§ Updating BEHEMOTH CLI configuration...')); needsSetup = true; } } catch (error) { needsSetup = true; } } if (needsSetup) { console.log(chalk.cyan('šŸš€ First run detected - setting up BEHEMOTH CLI...')); console.log(chalk.yellow('šŸ“‹ Quick setup - you can change these later with /keys and /model commands')); console.log(''); // Create config directory if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir, { recursive: true }); } // Ask for preferred provider console.log(chalk.cyan('šŸ”§ Choose your preferred AI provider:')); console.log('1. OpenRouter (recommended - includes many free models)'); console.log('2. Groq (fast, good free tier)'); console.log('3. DeepSeek (direct access, good free tier)'); console.log(''); const providerChoice = await askQuestion('Enter choice (1-3) or press Enter for OpenRouter: '); let defaultProvider = 'openrouter'; let needsApiKey = false; switch (providerChoice) { case '2': defaultProvider = 'groq'; needsApiKey = true; break; case '3': defaultProvider = 'deepseek'; needsApiKey = true; break; default: defaultProvider = 'openrouter'; break; } // Create or update config const defaultConfig = existingConfig || { defaultProvider, providers: { groq: { apiKey: "", defaultModel: "llama-3.3-70b-versatile", enabled: true }, openrouter: { apiKey: "", defaultModel: "deepseek/deepseek-chat-v3-0324:free", enabled: true }, deepseek: { apiKey: "", defaultModel: "deepseek-chat", enabled: true } }, exchanges: {} }; // Update default provider if user made a choice if (defaultProvider) { defaultConfig.defaultProvider = defaultProvider; } // Ensure all providers exist and update OpenRouter default model if (!defaultConfig.providers) defaultConfig.providers = {}; if (!defaultConfig.providers.groq) defaultConfig.providers.groq = { apiKey: "", defaultModel: "llama-3.3-70b-versatile", enabled: true }; if (!defaultConfig.providers.openrouter) defaultConfig.providers.openrouter = { apiKey: "", defaultModel: "deepseek/deepseek-chat-v3-0324:free", enabled: true }; if (!defaultConfig.providers.deepseek) defaultConfig.providers.deepseek = { apiKey: "", defaultModel: "deepseek-chat", enabled: true }; // Always update OpenRouter default model defaultConfig.providers.openrouter.defaultModel = "deepseek/deepseek-chat-v3-0324:free"; // Ask for API key based on selected provider if (needsApiKey) { console.log(''); console.log(chalk.yellow('šŸ”‘ API Key Setup:')); console.log(chalk.cyan(`šŸ”§ ${defaultProvider.charAt(0).toUpperCase() + defaultProvider.slice(1)} API Key:`)); if (defaultProvider === 'groq') { console.log('Get your free Groq API key at: https://console.groq.com/keys'); } else if (defaultProvider === 'deepseek') { console.log('Get your free DeepSeek API key at: https://platform.deepseek.com/'); } const apiKey = await askQuestion(`Enter your ${defaultProvider} API key (or press Enter to skip): `); if (apiKey) { if (defaultProvider === 'groq') { defaultConfig.providers.groq.apiKey = apiKey; } else if (defaultProvider === 'deepseek') { defaultConfig.providers.deepseek.apiKey = apiKey; } } } else { console.log(''); console.log(chalk.green('✨ OpenRouter selected - free models available without API key')); console.log(chalk.yellow('šŸ’” For premium models, get an API key at: https://openrouter.ai/keys')); // Ask if they want to add OpenRouter API key for premium models const wantApiKey = await askQuestion('Add OpenRouter API key now for premium models? (y/n): '); if (wantApiKey.toLowerCase() === 'y' || wantApiKey.toLowerCase() === 'yes') { const apiKey = await askQuestion('Enter your OpenRouter API key: '); if (apiKey) { defaultConfig.providers.openrouter.apiKey = apiKey; } } } fs.writeFileSync(providerConfigPath, JSON.stringify(defaultConfig, null, 2)); console.log(''); console.log(chalk.green('āœ… Configuration initialized')); console.log(chalk.yellow('šŸ’” Run "/howto setup" for complete setup instructions')); console.log(chalk.cyan('šŸ”§ Use /keys and /model commands to update configuration anytime')); } } /** * Execute a single prompt and exit - useful for testing MCP tools */ async function executePrompt( prompt: string, temperature: number, system: string | null, debug?: boolean ): Promise<void> { console.log(chalk.cyan('šŸ”§ BEHEMOTH MCP Tool Testing Mode')); console.log(chalk.gray('━'.repeat(60))); console.log(chalk.white(`šŸ“ Prompt: ${prompt}`)); console.log(chalk.gray('━'.repeat(60))); try { // Ensure setup is completed await ensureSetup(); // Initialize BEHEMOTH MCP servers console.log(chalk.cyan('šŸ”Œ Initializing BEHEMOTH MCP servers...')); const mcpInitialized = await initializeBehemothMCP(); if (mcpInitialized) { console.log(chalk.green('āœ… BEHEMOTH MCP servers initialized successfully')); } else { console.log(chalk.yellow('āš ļø BEHEMOTH MCP initialization failed - some tools may not be available')); } // Create agent const configManager = new MultiProviderConfigManager(); const defaultProvider = configManager.getDefaultProvider(); const defaultModel = configManager.getProviderModel(defaultProvider); const agent = await Agent.create(defaultModel, defaultProvider, temperature, system, debug); // Parse natural language to see if it maps to specific commands console.log(chalk.cyan('🧠 Processing natural language...')); const parsed = await parseNaturalLanguage(prompt); if (parsed.confidence > 0.6) { console.log(chalk.green(`āœ… NLP Parsing successful (confidence: ${(parsed.confidence * 100).toFixed(1)}%)`)); console.log(chalk.blue(`šŸŽÆ Interpreted as: ${parsed.command}`)); console.log(chalk.gray(generateParsingResponse(parsed))); } else { console.log(chalk.yellow(`āš ļø Low confidence NLP parsing (${(parsed.confidence * 100).toFixed(1)}%)`)); console.log(chalk.gray('Will process as general conversation...')); } console.log(chalk.gray('━'.repeat(60))); console.log(chalk.cyan('šŸ¤– BEHEMOTH AI Response:')); console.log(chalk.gray('━'.repeat(60))); // Set up response handlers for single prompt mode let responseText = ''; let toolCallCount = 0; agent.setToolCallbacks({ onThinkingText: (text: string, title?: string) => { if (title) { console.log(chalk.magenta(`\nšŸ’­ ${title}:`)); } console.log(chalk.gray(text)); }, onToolStart: (toolName: string, args: any) => { toolCallCount++; console.log(chalk.blue(`\nšŸ”§ Tool Call #${toolCallCount}: ${toolName}`)); if (Object.keys(args).length > 0) { console.log(chalk.gray(` Args: ${JSON.stringify(args, null, 2)}`)); } }, onToolEnd: (toolName: string, result: any) => { console.log(chalk.green(`āœ… Tool Result: ${toolName}`)); if (result && typeof result === 'object') { console.log(chalk.gray(` Result: ${JSON.stringify(result, null, 2).substring(0, 200)}...`)); } else { console.log(chalk.gray(` Result: ${String(result).substring(0, 200)}...`)); } }, onStreamUpdate: (content: string, isFinal: boolean) => { process.stdout.write(chalk.white(content)); responseText += content; }, onFinalMessage: (content: string) => { console.log(chalk.white(content)); responseText += content; }, onApiUsage: (usage) => { console.log(chalk.blue(`\nšŸ“Š API Usage: ${usage.prompt_tokens} prompt + ${usage.completion_tokens} completion = ${usage.total_tokens} total tokens`)); } }); // Execute the prompt await agent.chat(prompt); console.log(chalk.gray('\n━'.repeat(60))); console.log(chalk.green(`āœ… Prompt execution completed`)); console.log(chalk.blue(`šŸ“Š Tools called: ${toolCallCount}`)); console.log(chalk.blue(`šŸ“ Response length: ${responseText.length} characters`)); console.log(chalk.gray('━'.repeat(60))); } catch (error) { console.log(chalk.red(`āŒ Error executing prompt: ${error}`)); throw error; // Allow caller to handle exit } // Successful completion - no need for process.exit() } async function startChat( temperature: number, system: string | null, debug?: boolean ): Promise<void> { // Beautiful BEHEMOTH ASCII art with blue to red gradient console.log(chalk.hex('#0066FF').bold(` ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—`)); console.log(chalk.hex('#3377FF').bold(` ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā•šā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘`)); console.log(chalk.hex('#6688FF').bold(` ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘`)); console.log(chalk.hex('#9999FF').bold(` ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘`)); console.log(chalk.hex('#CC66FF').bold(` ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā•šā•ā• ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘`)); console.log(chalk.hex('#FF3333').bold(` ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā• ā•šā•ā•`)); console.log(chalk.hex('#FFD700').bold(` 🌌 COSMIC CRYPTO TRADING CLI 🌌`)); console.log(chalk.hex('#FF6B35').bold(` šŸš€ 200+ Professional Trading Tools | AI Multi-Agent Analysis šŸš€`)); console.log(chalk.hex('#8A2BE2').bold(` ⚔ Quantum Market Intelligence | Real-Time Trading ⚔`)); console.log(chalk.cyan.bold(` ✨ BEHEMOTH v2.2.4 - Production Ready ✨`)); console.log(chalk.gray.italic(` Built by fR3k@mcpintelligence.com.au`)); console.log(''); // Ensure setup is completed await ensureSetup(); const configManager = new MultiProviderConfigManager(); const defaultProvider = configManager.getDefaultProvider(); const defaultModel = configManager.getProviderModel(defaultProvider); try { // Create agent (API key will be checked on first message) const agent = await Agent.create(defaultModel, defaultProvider, temperature, system, debug); // Initialize BEHEMOTH MCP servers for crypto trading tools console.log(chalk.cyan('šŸ”Œ Initializing BEHEMOTH MCP servers...')); const mcpInitialized = await initializeBehemothMCP(); if (mcpInitialized) { console.log(chalk.green('āœ… BEHEMOTH MCP servers initialized successfully')); } else { console.log(chalk.yellow('āš ļø BEHEMOTH MCP initialization failed - trading tools may not be available')); } // CLI Configuration Notice console.log(chalk.magenta('āš™ļø CLI Mode: Streaming responses disabled for optimal terminal performance')); if (CLI_FEATURES.ENABLE_ASCII_CHARTS) { console.log(chalk.cyan('šŸ“Š Interactive ASCII charts available - try /chart --dashboard')); } console.log(''); render(React.createElement(App, { agent }), { stdin: process.stdin }); } catch (error) { console.log(chalk.red(`Error initializing agent: ${error}`)); throw error; // Allow caller to handle exit } } program .name('behemoth') .description('Behemoth Crypto Trading CLI - AI-Powered Cryptocurrency Analysis & Trading') .version('2.2.4') .option('-t, --temperature <temperature>', 'Temperature for generation', parseFloat, 1.0) .option('-s, --system <message>', 'Custom system message') .option('-d, --debug', 'Enable debug logging to debug-agent.log in current directory') .option('-p, --prompt <prompt>', 'Execute a single prompt and exit (useful for testing MCP tools)') .action(async (options) => { try { if (options.prompt) { await executePrompt( options.prompt, options.temperature, options.system || null, options.debug ); } else { await startChat( options.temperature, options.system || null, options.debug ); } } catch (error) { console.error(chalk.red(`Fatal error: ${error}`)); process.exitCode = 1; // Set exit code without forcing immediate exit } }); program.parse();