UNPKG

mya-cli

Version:

MYA - AI-Powered Stock & Options Analysis CLI Tool

1,193 lines โ€ข 53.5 kB
#!/usr/bin/env node /** * MYA CLI - HTTP Client for Production * Multi-user CLI with HTTP API calls */ import { Command } from 'commander'; import inquirer from 'inquirer'; import chalk from 'chalk'; import ora from 'ora'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; /** * Get CLI configuration */ function getCLIConfig() { // Determine the environment and corresponding API URL const nodeEnv = process.env.NODE_ENV || 'production'; const explicitApiUrl = process.env.MYA_API_URL; let apiUrl; if (explicitApiUrl) { // Use explicit URL if provided apiUrl = explicitApiUrl; } else { // Production environment (default) - use production worker apiUrl = 'https://mya-production.monibee-fudgekin.workers.dev'; } return { apiUrl, sessionFile: path.join(os.homedir(), '.mya-session.json'), }; } /** * Make API request */ async function makeApiRequest(url, options = {}) { const maxRetries = 3; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); const response = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeoutId); return response; } catch (error) { if (attempt === maxRetries) { throw new Error(`Request failed after ${maxRetries} attempts: ${error.message}`); } await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } } throw new Error('Request failed'); } /** * API helper function */ async function apiRequest(endpoint, options = {}) { const config = getCLIConfig(); const url = `${config.apiUrl}${endpoint}`; const response = await makeApiRequest(url, options); if (!response.ok) { let errorDetails = ''; try { const errorBody = await response.text(); errorDetails = errorBody ? ` - ${errorBody}` : ''; } catch { // Ignore error reading response body } throw new Error(`HTTP ${response.status}: ${response.statusText}${errorDetails}`); } return response.json(); } /** * Load user session from file */ function loadSession() { try { const config = getCLIConfig(); if (!fs.existsSync(config.sessionFile)) { return null; } const sessionData = JSON.parse(fs.readFileSync(config.sessionFile, 'utf8')); // Check if session is expired if (Date.now() > sessionData.expiresAt) { fs.unlinkSync(config.sessionFile); return null; } return sessionData; } catch (error) { console.error('Error loading session:', error); return null; } } /** * Save user session to file */ function saveSession(session) { try { const config = getCLIConfig(); session.lastActivity = Date.now(); fs.writeFileSync(config.sessionFile, JSON.stringify(session, null, 2)); } catch (error) { console.error('Error saving session:', error); } } /** * Clear user session */ function clearSession() { try { const config = getCLIConfig(); if (fs.existsSync(config.sessionFile)) { fs.unlinkSync(config.sessionFile); } } catch (error) { console.error('Error clearing session:', error); } } /** * Initialize services */ async function initializeServices() { try { await apiRequest('/initialize', { method: 'POST' }); } catch (error) { // Allow CLI to continue if service initialization fails // This is not critical for basic functionality if (error instanceof Error && error.message.includes('Unable to connect')) { throw error; // Re-throw connection errors } // Service initialization skipped (non-critical) } } /** * Process authentication */ async function processAuth(email) { return apiRequest('/auth', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email }), }); } /** * Verify OTP and create session */ async function verifyOtpAndCreateSession(email, otpCode, methodId) { return apiRequest('/verify-otp', { method: 'POST', body: JSON.stringify({ email, otpCode, methodId }), }); } /** * Validate session */ async function validateSession(_userId, _machineId, _sessionId) { try { // Load session to get JWT token const session = loadSession(); if (!session || !session.sessionJwt) { return false; } const result = await apiRequest('/validate-session', { headers: { 'Authorization': `Bearer ${session.sessionJwt}`, }, }); return result.valid; } catch { return false; } } /** * Submit analysis request */ async function submitAnalysisRequest(userId, machineId, analysisType, parameters) { // Load session to get JWT token const session = loadSession(); if (!session || !session.sessionJwt) { throw new Error('No valid session found'); } return apiRequest('/analyze', { method: 'POST', headers: { 'Authorization': `Bearer ${session.sessionJwt}`, }, body: JSON.stringify({ userId, machineId, analysisType, parameters }), }); } /** * Get analysis result */ async function getAnalysisResult(requestId, _userId) { // Load session to get JWT token const session = loadSession(); if (!session || !session.sessionJwt) { throw new Error('No valid session found'); } return apiRequest(`/analyze/${requestId}`, { headers: { 'Authorization': `Bearer ${session.sessionJwt}`, }, }); } /** * Get user status */ async function _getUserStatus(_userId, _machineId) { // Load session to get the JWT token const session = loadSession(); if (!session || !session.sessionJwt) { throw new Error('No valid session found'); } return apiRequest('/status', { headers: { 'Authorization': `Bearer ${session.sessionJwt}`, }, }); } /** * Logout user */ async function _logoutUser(_userId, _machineId) { await apiRequest('/auth/logout', { method: 'POST', body: JSON.stringify({ userId: _userId, machineId: _machineId }), }); } /** * Check if backend service is available */ async function checkServiceAvailability() { try { const config = getCLIConfig(); const response = await fetch(`${config.apiUrl}/health`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); return response.ok; } catch { return false; } } /** * Authenticate user */ async function authenticateUser() { const spinner = ora('Checking service availability...').start(); try { // Check if backend is reachable before proceeding const isServiceAvailable = await checkServiceAvailability(); if (!isServiceAvailable) { spinner.fail('Service not available'); console.error(chalk.red('โŒ Backend service is not reachable.')); console.log(chalk.yellow('๐Ÿ”ง Troubleshooting Steps:')); console.log(chalk.gray(' โ€ข Check your internet connection')); console.log(chalk.gray(' โ€ข Verify you can access other websites')); console.log(chalk.gray(' โ€ข The service may be temporarily down - try again in a few minutes')); console.log(chalk.gray(' โ€ข Check service status at: https://status.your-service.com')); return null; } spinner.text = 'Initializing authentication...'; await initializeServices(); spinner.succeed('Services initialized'); const { email } = await inquirer.prompt([ { type: 'input', name: 'email', message: 'Enter your email:', validate: (input) => { if (!input || !input.includes('@')) { return 'Please enter a valid email address'; } return true; }, }, ]); spinner.start('Sending authentication code...'); let authResult; try { authResult = await processAuth(email); if (!authResult.success) { spinner.fail('Authentication failed'); console.error(chalk.red('Authentication failed:'), authResult.error); console.error(chalk.red('Full response:'), JSON.stringify(authResult, null, 2)); return null; } spinner.succeed('Authentication code sent'); } catch (error) { spinner.fail('Authentication request failed'); console.error(chalk.red('Error details:'), error); console.error(chalk.red('Error message:'), error instanceof Error ? error.message : 'Unknown error'); // Try to get more details from the response if (error instanceof Error && error.message.includes('HTTP')) { console.error(chalk.red('This appears to be an HTTP error. Check the worker logs for more details.')); } return null; } const { otpCode } = await inquirer.prompt([ { type: 'input', name: 'otpCode', message: 'Enter the 6-digit code from your email:', validate: (input) => { if (!input || input.length !== 6 || !/^\d{6}$/.test(input)) { return 'Please enter a valid 6-digit code'; } return true; }, }, ]); const methodId = authResult.methodId || authResult.method_id; if (!methodId) { spinner.fail('Authentication failed'); console.error(chalk.red('Authentication failed: No method ID received')); return null; } spinner.start('Verifying code and creating session...'); const sessionResult = await verifyOtpAndCreateSession(email, otpCode, methodId); if (!sessionResult.success) { spinner.fail('Session creation failed'); if (sessionResult.error?.includes('otp_code_not_found') || sessionResult.error?.includes('incorrect')) { console.error(chalk.red('Invalid verification code. Please check your email and try again.')); console.log(chalk.yellow('Tip: Make sure to enter the 6-digit code exactly as received in your email.')); } else { console.error(chalk.red('Session creation failed:'), sessionResult.error); } return null; } spinner.succeed('Session created successfully'); const session = { userId: sessionResult.userId || '', machineId: sessionResult.machineId || '', sessionId: sessionResult.sessionToken || '', sessionJwt: sessionResult.sessionJwt || '', email, isActive: true, createdAt: Date.now(), lastActivity: Date.now(), expiresAt: Date.now() + (24 * 60 * 60 * 1000), // 24 hours }; saveSession(session); return session; } catch (error) { spinner.fail('Authentication error'); console.error(chalk.red('โŒ Authentication error:'), error); return null; } } /** * Ensure user is authenticated */ async function ensureAuthenticated() { let session = loadSession(); if (session) { const spinner = ora('Validating session...').start(); try { const isValid = await validateSession(session.userId, session.machineId, session.sessionId); if (isValid) { spinner.succeed('Session validated'); saveSession(session); // Update last activity return session; } else { spinner.warn('Session expired or invalid'); clearSession(); session = null; } } catch (error) { spinner.fail('Session validation error'); console.error(chalk.yellow('Session validation error:'), error); clearSession(); session = null; } } if (!session) { console.log(chalk.blue('Please authenticate to continue')); session = await authenticateUser(); } return session; } /** * Display analysis results */ async function displayAnalysisResults(requestId, session) { const spinner = ora('Retrieving analysis results...').start(); try { const result = await getAnalysisResult(requestId, session.userId); if (result.status === 'completed') { spinner.succeed('Analysis completed'); console.log(chalk.green('Analysis Results:')); // Check if result has data field or use the whole result const resultData = 'result' in result ? result.result : result; // Format the results nicely instead of raw JSON if (resultData && typeof resultData === 'object') { if (resultData.aiAnalysis) { console.log(chalk.white(resultData.aiAnalysis)); } else if (resultData.analysis) { console.log(chalk.white(resultData.analysis)); } else { // Fallback to formatted JSON, but filter out technical details const cleanResult = { ...resultData }; delete cleanResult.timestamp; delete cleanResult.requestId; delete cleanResult.dataPoints; delete cleanResult.marketContext; console.log(JSON.stringify(cleanResult, null, 2)); } // Show symbols analyzed if available if (resultData.symbols) { console.log(chalk.gray('\nSymbols analyzed:'), resultData.symbols.join(', ')); } // Show market status const marketStatus = getMarketStatusMessage(); console.log(chalk.blue('\nMarket Status:'), marketStatus); } else { console.log(JSON.stringify(resultData, null, 2)); } } else if (result.status === 'failed') { spinner.fail('Analysis failed'); console.log(chalk.red(`Analysis failed: ${result.error || 'Unknown error'}`)); } else if (result.status === 'processing') { spinner.warn('Analysis still processing'); console.log(chalk.yellow('Analysis still processing...')); } else { spinner.info('Analysis status retrieved'); console.log(chalk.gray('Analysis status:'), result.status); if ('message' in result && result.message) { console.log(chalk.gray('Message:'), result.message); } } } catch (error) { spinner.fail('Failed to retrieve results'); console.error(chalk.red('Failed to retrieve results:'), error); } } /** * Check if current time is during market hours (9:30 AM - 4:00 PM EST, Monday-Friday) * @returns {boolean} True if during market hours */ function isMarketHours() { const now = new Date(); const estTime = new Date(now.toLocaleString("en-US", { timeZone: "America/New_York" })); const day = estTime.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday const hour = estTime.getHours(); const minute = estTime.getMinutes(); const timeInMinutes = hour * 60 + minute; // Market is closed on weekends if (day === 0 || day === 6) { return false; } // Market hours: 9:30 AM - 4:00 PM EST (570 minutes - 960 minutes from midnight) const marketOpen = 9 * 60 + 30; // 9:30 AM const marketClose = 16 * 60; // 4:00 PM return timeInMinutes >= marketOpen && timeInMinutes <= marketClose; } /** * Get market status message for display * @returns {string} Market status message with timing */ function getMarketStatusMessage() { if (isMarketHours()) { return chalk.green('Market is OPEN (9:30 AM - 4:00 PM EST)'); } else { const now = new Date(); const estTime = new Date(now.toLocaleString("en-US", { timeZone: "America/New_York" })); const day = estTime.getDay(); if (day === 0 || day === 6) { return chalk.yellow('Market is CLOSED (Weekend) - Next open: Monday 9:30 AM EST'); } else { const hour = estTime.getHours(); if (hour < 9 || (hour === 9 && estTime.getMinutes() < 30)) { return chalk.yellow('Market is CLOSED (Pre-market) - Opens at 9:30 AM EST'); } else { return chalk.yellow('Market is CLOSED (After-hours) - Next open: 9:30 AM EST tomorrow'); } } } } /** * Pre-warm cache for analysis functions to optimize API usage */ async function preWarmCacheForAnalysis() { try { const { CacheManager } = await import('./utils/cache-manager.js'); // Pre-warm cache with common symbols for better API efficiency const symbols = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'META', 'NVDA']; await CacheManager.preWarmCache(symbols); // Show brief cache status const report = CacheManager.getCacheReport(); const hitRate = report.totalCacheStats.hitRate; if (hitRate > 50) { console.log(chalk.green(`Cache optimized (${hitRate}% hit rate)`)); } else { console.log(chalk.yellow(`Cache warmed for API efficiency`)); } } catch (error) { // Don't fail the analysis if cache warming fails console.log(chalk.gray('Cache warming skipped (non-critical)')); } } /** * Main CLI program */ async function main() { const program = new Command(); program .name('mya') .description('MYA - AI-Powered Stock & Options Analysis. Fully automated trading intelligence platform.') .version('1.0.0'); // Login command program .command('login') .description('Authenticate and create a new session') .option('-f, --force', 'Force re-authentication even if already logged in') .action(async (options) => { try { // Check if already authenticated (unless force flag is used) if (!options.force) { const existingSession = loadSession(); if (existingSession) { const spinner = ora('Checking existing session...').start(); try { const isValid = await validateSession(existingSession.userId, existingSession.machineId, existingSession.sessionId); if (isValid) { spinner.succeed('Already authenticated'); console.log(chalk.green('Already logged in as'), chalk.cyan(existingSession.email)); console.log(chalk.gray('Use "mya login --force" to re-authenticate')); console.log(getMarketStatusMessage()); return; } else { spinner.warn('Session expired, re-authenticating...'); } } catch (error) { spinner.warn('Session validation failed, re-authenticating...'); } } } const session = await authenticateUser(); if (session) { console.log(chalk.green('Successfully authenticated')); console.log(getMarketStatusMessage()); } } catch (error) { console.error(chalk.red('Authentication failed:'), error); } }); // Double capital command - Shows ONLY 200%+ return trades using analyze data program .command('double') .description('Find 200%+ return opportunities with specific entry prices and timeframes (CMT analysis)') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); // Pre-warm cache for optimal API efficiency await preWarmCacheForAnalysis(); const spinner = ora('Analyzing for 200%+ return opportunities...').start(); try { const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'double_cmt', { sessionId: session.sessionId, analysisType: 'cmt_double', cmtLevel: 'Level_III', minReturnTarget: 200, requireEntryPrice: true, requireTimeframe: true, includeOptions: true, includeStocks: true, symbols: [] }); if (analysisResult.success) { spinner.succeed('Double capital analysis completed'); console.log(chalk.green('200%+ Return Analysis Results:')); console.log(chalk.gray('Request ID:'), analysisResult.requestId); // Display the AI analysis results if (analysisResult.result) { console.log('\n' + chalk.blue('Market Analysis - 200%+ Opportunities:')); if (typeof analysisResult.result.aiAnalysis === 'string') { console.log(analysisResult.result.aiAnalysis); } else { console.log(JSON.stringify(analysisResult.result, null, 2)); } if (analysisResult.result.dataPoints) { console.log(chalk.gray(`\nAnalyzed ${analysisResult.result.dataPoints} potential 200%+ return opportunities`)); } } } else { spinner.fail('Double capital analysis failed'); console.error(chalk.red('Analysis request failed:'), analysisResult.error); } } catch (error) { spinner.fail('Double capital analysis error'); console.error(chalk.red('Analysis request error:'), error); console.log(chalk.yellow('๐Ÿ’ก Suggestions:')); console.log(chalk.gray(' โ€ข Try running "mya announcements" first to gather market data')); console.log(chalk.gray(' โ€ข Check your internet connection')); console.log(chalk.gray(' โ€ข Verify authentication with "mya status"')); } } catch (error) { console.error(chalk.red('Double capital command failed:'), error); console.log(chalk.blue('๐Ÿ’ก Try: "mya announcements" โ†’ "mya double" for better results')); } }); // Analyze command - Makes AI-powered BUY/SELL/HOLD recommendations using Auto RAG data program .command('analyze') .description('Stock analysis with BUY/SELL/HOLD recommendations and entry prices (CMT methodology)') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); // Pre-warm cache for optimal API efficiency await preWarmCacheForAnalysis(); const spinner = ora('Analyzing market data from announcements...').start(); try { const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'analyze_cmt', { sessionId: session.sessionId, analysisType: 'cmt_analysis', cmtLevel: 'Level_III', requireEntryPrice: true, requireTimeframe: true, symbols: [] }); if (analysisResult.success) { spinner.succeed('Market analysis completed'); console.log(chalk.green('Stock Analysis Results:')); console.log(chalk.gray('Request ID:'), analysisResult.requestId); // Display results immediately if (analysisResult.result) { console.log('\n' + chalk.blue('Market Analysis:')); if (typeof analysisResult.result.aiAnalysis === 'string') { console.log(analysisResult.result.aiAnalysis); } else { console.log(JSON.stringify(analysisResult.result, null, 2)); } if (analysisResult.result.dataPoints) { console.log(chalk.gray(`\nAnalyzed ${analysisResult.result.dataPoints} market data points`)); } } } else { spinner.fail('Market analysis failed'); console.error(chalk.red('Analysis request failed:'), analysisResult.error); } } catch (error) { spinner.fail('Market analysis error'); console.error(chalk.red('Analysis request error:'), error); } } catch (error) { console.error(chalk.red('Analyze command failed:'), error); } }); // Earnings command - Finds stocks with earnings this week and makes recommendations program .command('earnings') .description('CMT analysis of stocks with earnings THIS WEEK ONLY - provides buy prices and timeframes') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('โŒ Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); // Pre-warm cache for optimal API efficiency await preWarmCacheForAnalysis(); // Get current week dates const now = new Date(); const startOfWeek = new Date(now.setDate(now.getDate() - now.getDay())); const endOfWeek = new Date(now.setDate(now.getDate() - now.getDay() + 6)); console.log(chalk.blue(`๏ฟฝ Scanning for earnings: ${startOfWeek.toLocaleDateString()} - ${endOfWeek.toLocaleDateString()}`)); const spinner = ora('Finding stocks with earnings THIS WEEK only...').start(); try { const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'earnings_cmt', { sessionId: session.sessionId, analysisType: 'cmt_earnings', earningsWeekOnly: true, strictEarningsFilter: true, cmtLevel: 'Level_III', requireBuyPrice: true, requireTimeframe: true, weekStart: startOfWeek.toISOString(), weekEnd: endOfWeek.toISOString(), symbols: [] }); if (analysisResult.success) { spinner.succeed('Current week earnings analysis completed'); console.log(chalk.green('This Week\'s Earnings Analysis:')); console.log(chalk.gray('Request ID:'), analysisResult.requestId); // Display results immediately if (analysisResult.result) { console.log('\n' + chalk.blue('Earnings This Week:')); if (typeof analysisResult.result.aiAnalysis === 'string') { console.log(analysisResult.result.aiAnalysis); } else { console.log(JSON.stringify(analysisResult.result, null, 2)); } if (analysisResult.result.dataPoints) { console.log(chalk.gray(`\nFound ${analysisResult.result.dataPoints} stocks with earnings this week`)); } if (analysisResult.result.symbols && analysisResult.result.symbols.length > 0) { console.log(chalk.white('\nStocks with earnings this week:'), chalk.cyan(analysisResult.result.symbols.join(', '))); } else { console.log(chalk.yellow('\nNo major stocks found with earnings this week')); } } } else { spinner.fail('Earnings analysis failed'); console.error(chalk.red('Analysis request failed:'), analysisResult.error); } } catch (error) { spinner.fail('Earnings analysis error'); console.error(chalk.red('Analysis request error:'), error); } } catch (error) { console.error(chalk.red('Earnings command failed:'), error); } }); // Announcements command - Tracks important economic data (automated) program .command('announcements') .description('Market news review and fundamentals data collection for analysis functions') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('โŒ Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); // Pre-warm cache for optimal API efficiency await preWarmCacheForAnalysis(); const spinner = ora('Reviewing market news and fundamentals data...').start(); try { // Make HTTP request to worker endpoint instead of running locally const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'announcements_cmt', {}); if (analysisResult.success && analysisResult.result) { spinner.succeed('Market news review completed'); console.log(chalk.green('Market News Review:')); console.log(chalk.gray('Request ID:'), analysisResult.requestId || 'N/A'); const result = analysisResult.result; // Display AI analysis if (result.aiAnalysis) { console.log('\n' + chalk.blue('Market News Review:')); console.log(result.aiAnalysis); } // Display symbols if available if (result.symbols && result.symbols.length > 0) { console.log(chalk.white('\nStocks identified for analysis:'), chalk.cyan(result.symbols.join(', '))); } // Display timestamp if (result.timestamp) { console.log(chalk.gray(`\nGenerated: ${new Date(result.timestamp).toLocaleString()}`)); } } else { spinner.fail('Market news review failed'); console.error(chalk.red('Analysis request failed:'), analysisResult.error || 'Unknown error'); } } catch (error) { spinner.fail('Market news review error'); console.error(chalk.red('Analysis request error:'), error); } } catch (error) { console.error(chalk.red('Announcements command failed:'), error); } }); // Results command program .command('results [requestId]') .description('Get analysis results (optionally specify request ID)') .action(async (requestId) => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('โŒ Authentication required')); process.exit(1); } if (requestId) { // Get specific results by request ID const spinner = ora(`Retrieving results for ${requestId}...`).start(); try { await displayAnalysisResults(requestId, session); spinner.succeed('Results retrieved'); } catch (error) { spinner.fail('Failed to retrieve results'); console.log(chalk.red(`โŒ Could not retrieve results for request ID: ${requestId}`)); console.log(chalk.gray('Make sure the request ID is correct and the analysis has completed.')); } } else { // Get the most recent analysis results const spinner = ora('Retrieving most recent analysis results...').start(); try { const result = await apiRequest('/recent-results', { headers: { 'Authorization': `Bearer ${session.sessionJwt}`, }, }); if (result.success && result.requestId) { spinner.succeed('Recent results retrieved'); await displayAnalysisResults(result.requestId, session); } else { spinner.fail('No recent results found'); console.log(chalk.yellow('No recent analysis results found.')); console.log(chalk.blue('Run one of the analysis commands first:')); console.log(chalk.gray(' mya analyze')); console.log(chalk.gray(' mya double')); console.log(chalk.gray(' mya earnings')); console.log(chalk.gray(' mya announcements')); } } catch (error) { spinner.fail('Failed to retrieve recent results'); console.log(chalk.yellow('No recent analysis results found.')); console.log(chalk.blue('Run one of the analysis commands first:')); console.log(chalk.gray(' mya analyze')); console.log(chalk.gray(' mya double')); console.log(chalk.gray(' mya earnings')); console.log(chalk.gray(' mya announcements')); } } } catch (error) { console.error(chalk.red('โŒ Results command failed:'), error); } }); // Benchmark command - Track and evaluate recommendation performance program .command('benchmark') .description('Evaluate recommendation performance and accuracy against market results') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('โŒ Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); // Automatically analyze today's request IDs and benchmark against market performance const analysisDate = new Date().toISOString().split('T')[0]; // Today's date const spinner = ora(`Analyzing today's trading performance (${analysisDate})...`).start(); try { const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'daily_benchmark_analysis', { sessionId: session.sessionId, analysisType: 'daily_request_analysis', date: analysisDate, action: 'analyze_daily_requests', autoExtract: true, benchmarkAgainstMarket: true, prompt: `Automatically extract all recommendations from request IDs generated today (${analysisDate}) and benchmark their performance against actual market results. Show success rate, outperformance vs S&P 500, and key insights.` }); if (analysisResult.success) { spinner.succeed('Daily benchmark analysis complete'); console.log(chalk.cyan('\nTrading Performance Analysis')); console.log(chalk.cyan('=====================================')); if (analysisResult.analysis) { console.log(analysisResult.analysis); } if (analysisResult.requestId) { console.log(chalk.gray(`\nRequest ID: ${analysisResult.requestId}`)); console.log(chalk.gray(`Use 'mya results ${analysisResult.requestId}' for detailed analysis`)); } } else { spinner.fail('Benchmark analysis failed'); console.log(chalk.red(`Error: ${analysisResult.error || 'Unknown error occurred'}`)); } } catch (error) { spinner.fail('Daily benchmark analysis error'); console.log(chalk.red(`Analysis request error: ${error}`)); } } catch (error) { console.log(chalk.red('Benchmark command error:'), error); } }); // Status command program .command('status') .description('Show current authentication status and session information') .action(async () => { try { const session = loadSession(); if (!session) { console.log(chalk.red('Not authenticated')); console.log(chalk.blue('Run "mya login" to authenticate')); return; } const now = Date.now(); const timeLeft = session.expiresAt - now; const hoursLeft = Math.floor(timeLeft / (1000 * 60 * 60)); const minutesLeft = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60)); console.log(chalk.green('Authenticated')); console.log(chalk.white('Email:'), chalk.cyan(session.email)); console.log(chalk.white('User ID:'), chalk.gray(session.userId)); console.log(chalk.white('Machine ID:'), chalk.gray(session.machineId.substring(0, 8) + '...')); console.log(chalk.white('Session created:'), chalk.gray(new Date(session.createdAt).toLocaleString())); console.log(chalk.white('Last activity:'), chalk.gray(new Date(session.lastActivity).toLocaleString())); if (timeLeft > 0) { console.log(chalk.white('Session expires:'), chalk.yellow(`in ${hoursLeft}h ${minutesLeft}m`)); console.log(chalk.white('Expiry date:'), chalk.gray(new Date(session.expiresAt).toLocaleString())); } else { console.log(chalk.red('Session expired')); console.log(chalk.blue('Run "mya login" to re-authenticate')); } console.log('\n' + getMarketStatusMessage()); } catch (error) { console.error(chalk.red('Status check failed:'), error); } }); // Logout command program .command('logout') .description('Log out and clear session') .action(async () => { try { const session = loadSession(); if (!session) { console.log(chalk.yellow('Not currently logged in')); return; } const spinner = ora('Logging out...').start(); try { // Attempt to notify the server about logout await _logoutUser(session.userId, session.machineId); spinner.succeed('Logged out from server'); } catch (error) { // Continue with local logout even if server logout fails spinner.warn('Server logout failed, clearing local session'); } // Clear local session clearSession(); console.log(chalk.green('Session cleared successfully')); console.log(chalk.blue('Run "mya login" to authenticate again')); } catch (error) { console.error(chalk.red('Logout failed:'), error); } }); // Cache status command - Show API usage and cache performance program .command('cache') .description('Show cache status and API rate limiting information') .option('-c, --clear [provider]', 'Clear cache for specific provider (alphavantage|polygon|yahoo)') .option('-w, --warm', 'Pre-warm cache with common symbols') .action(async (options) => { try { // Import cache manager const { CacheManager } = await import('./utils/cache-manager.js'); if (options.clear) { const provider = options.clear === true ? 'all' : options.clear; if (provider === 'all') { const { dataCache } = await import('./utils/data-cache.js'); dataCache.clear(); console.log(chalk.green('๐Ÿงน Cleared all cache entries')); } else if (['alphavantage', 'polygon', 'yahoo'].includes(provider)) { const cleared = CacheManager.clearProviderCache(provider); console.log(chalk.green(`Cleared ${cleared} cache entries for ${provider}`)); } else { console.error(chalk.red('Invalid provider. Use: alphavantage, polygon, or yahoo')); return; } } if (options.warm) { console.log(chalk.blue('Pre-warming cache...')); await CacheManager.preWarmCache(); } // Show cache status console.log(chalk.blue('Cache & API Status Report')); console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); CacheManager.logCacheStats(); // Show optimization suggestions CacheManager.optimizeForApiLimits(); // Show next available API times const availability = CacheManager.getApiAvailability(); console.log('Next Available API Calls:'); for (const [provider, nextTime] of Object.entries(availability)) { const now = Date.now(); const timeUntil = nextTime.getTime() - now; const status = timeUntil <= 0 ? chalk.green('Available now') : chalk.yellow(`Available in ${Math.round(timeUntil / 1000)}s`); console.log(` ${provider}: ${status}`); } } catch (error) { console.error(chalk.red('Cache status failed:'), error); } }); // Help alias command program .command('help') .description('Show help information (same as --help)') .argument('[command]', 'Show help for specific command') .action(async (command) => { if (command) { // Show help for specific command program.outputHelp(); console.log(chalk.blue(`\nFor help with a specific command, use: mya ${command} --help`)); } else { // Show general help program.outputHelp(); } }); // Version command program .command('version') .description('Show version information') .action(() => { console.log(chalk.blue('MYA - AI-Powered Stock & Options Analysis')); console.log(chalk.white('Version:'), chalk.green('1.0.0')); console.log(chalk.white('Node.js:'), chalk.gray(process.version)); console.log(chalk.white('Platform:'), chalk.gray(process.platform)); console.log(chalk.gray('Built with โค๏ธ for traders and developers')); }); // Command aliases for faster typing - using direct function calls program .command('d') .description('Alias for "double" - Find 200%+ return opportunities') .action(async () => { console.log(chalk.gray('Running: mya double')); // Execute double command logic directly try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('Authentication required')); process.exit(1); } console.log(getMarketStatusMessage()); await preWarmCacheForAnalysis(); const spinner = ora('Analyzing for 200%+ return opportunities...').start(); const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'double_cmt', { sessionId: session.sessionId, analysisType: 'cmt_double', cmtLevel: 'Level_III', minReturnTarget: 200, requireEntryPrice: true, requireTimeframe: true, includeOptions: true, includeStocks: true, symbols: [] }); if (analysisResult.success) { spinner.succeed('Double capital analysis completed'); console.log(chalk.green('200%+ Return Analysis Results:')); if (analysisResult.result?.aiAnalysis) { console.log(analysisResult.result.aiAnalysis); } } else { spinner.fail('Double capital analysis failed'); console.error(chalk.red('Analysis request failed:'), analysisResult.error); } } catch (error) { console.error(chalk.red('Double capital command failed:'), error); } }); // Simple aliases that just show what they do program .command('a') .description('Alias for "analyze" - Stock analysis with recommendations') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya a" โ†’ "mya analyze"')); console.log(chalk.gray('Use: mya analyze')); }); program .command('e') .description('Alias for "earnings" - Earnings analysis for this week') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya e" โ†’ "mya earnings"')); console.log(chalk.gray('Use: mya earnings')); }); program .command('n') .description('Alias for "announcements" - Market news review') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya n" โ†’ "mya announcements"')); console.log(chalk.gray('Use: mya announcements')); }); program .command('b') .description('Alias for "benchmark" - Performance evaluation') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya b" โ†’ "mya benchmark"')); console.log(chalk.gray('Use: mya benchmark')); }); program .command('s') .description('Alias for "status" - Show authentication status') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya s" โ†’ "mya status"')); console.log(chalk.gray('Use: mya status')); }); program .command('r') .description('Alias for "results" - Get most recent analysis results') .action(() => { console.log(chalk.blue('๐Ÿ’ก Alias: "mya r" โ†’ "mya results"')); console.log(chalk.gray('Use: mya results')); }); // Quick/All command - runs multiple analysis commands in sequence program .command('quick') .alias('all') .description('Run comprehensive analysis (announcements โ†’ analyze โ†’ double โ†’ earnings)') .action(async () => { try { const session = await ensureAuthenticated(); if (!session) { console.log(chalk.red('โŒ Authentication required')); process.exit(1); } console.log(chalk.blue('๐Ÿš€ Starting comprehensive market analysis...')); console.log(getMarketStatusMessage()); console.log(''); // For now, show what the comprehensive analysis would include console.log(chalk.cyan('๐Ÿ“ฐ Step 1/4: Market News Review & Data Collection')); console.log(chalk.gray('โ•'.repeat(60))); console.log(chalk.yellow('Run: mya announcements')); console.log(''); console.log(chalk.cyan('๐Ÿ“Š Step 2/4: Stock Analysis with Recommendations')); console.log(chalk.gray('โ•'.repeat(60))); console.log(chalk.yellow('Run: mya analyze')); console.log(''); console.log(chalk.cya