mya-cli
Version:
MYA - AI-Powered Stock & Options Analysis CLI Tool
1,193 lines โข 53.5 kB
JavaScript
#!/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