mya-cli
Version:
MYA - AI-Powered Stock & Options Analysis CLI Tool
591 lines • 26.8 kB
JavaScript
/**
* Module: MYA CLI - HTTP Client
* Purpose: Main CLI entry point for MYA trading platform
* Dependencies: commander (CLI), chalk (colors), ora (spinners), modules in cli/ and shared/
* Used by: CLI users, bin/mya.cjs
*
* This is the main CLI orchestrator that handles all user commands.
* Business logic is split into modules in cli/ for easier maintenance:
* - cli/auth.ts - Authentication and session validation
* - cli/session.ts - Session persistence and config
* - cli/api-client.ts - HTTP API communication
* - cli/analysis.ts - Analysis request handling
* - cli/display.ts - Result formatting and display
* - cli/market.ts - Market utilities and timestamps
*/
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
// Import auth and session management
import { authenticateUser, ensureAuthenticated, validateSession } from './cli/auth.js';
import { loadSession, clearSession } from './cli/session.js';
import { apiRequest } from './cli/api-client.js';
// Import analysis and results
import { submitAnalysisRequest, getAnalysisResult, _logoutUser } from './cli/analysis.js';
// Import display utilities
import { displayAnalysisResults, displayFallbackNotice, printSystemStatus, startYahooCooldownPolling } from './cli/display.js';
// Import market utilities
import { getMarketStatusMessage, fetchSystemStatus, getHuggingFaceFreshness } from './cli/market.js';
/**
* 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...');
console.warn('Session validation error', error);
}
}
}
const session = await authenticateUser();
if (session) {
console.log(chalk.green('Successfully authenticated'));
console.log(getMarketStatusMessage());
}
}
catch (error) {
console.error(chalk.red('Authentication failed:'), error);
}
});
// Analyze command
program
.command('analyze')
.description('Stock analysis with BUY/SELL/HOLD recommendations (CMT methodology)')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
console.log(getMarketStatusMessage());
const sys = await fetchSystemStatus();
if (sys?.yahooCooldown?.active) {
const secs = Math.ceil((sys.yahooCooldown.remainingMs || 0) / 1000);
console.log(chalk.yellow(` Yahoo cooldown active (~${secs}s). We'll wait as needed during fetches.`));
}
await getHuggingFaceFreshness();
const spinner = ora('Analyzing market data from announcements...').start();
const stopCooldownTicker = startYahooCooldownPolling(spinner, 'Analyzing market data from announcements...');
try {
const analysisResult = await submitAnalysisRequest(session.userId, session.machineId, 'analyze_cmt', {
sessionId: session.sessionId,
analysisType: 'cmt_analysis',
cmtLevel: 'Level_III',
requireEntryPrice: true,
requireTimeframe: true
});
if (analysisResult.success) {
stopCooldownTicker();
spinner.succeed('Market analysis completed');
console.log(chalk.green('Stock Analysis Results:'));
console.log(chalk.gray('Request ID:'), analysisResult.requestId);
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`));
}
displayFallbackNotice(analysisResult.result);
}
}
else {
stopCooldownTicker();
spinner.fail('Market analysis failed');
console.error(chalk.red('Analysis request failed:'), analysisResult.error);
}
}
catch (error) {
stopCooldownTicker();
spinner.fail('Market analysis error');
console.error(chalk.red('Analysis request error:'), error);
}
}
catch (error) {
console.error(chalk.red('Analyze command failed:'), error);
}
});
// Double command
program
.command('double')
.description('Find 200%+ return opportunities (CMT analysis)')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
console.log(getMarketStatusMessage());
const sys = await fetchSystemStatus();
if (sys?.yahooCooldown?.active) {
const secs = Math.ceil((sys.yahooCooldown.remainingMs || 0) / 1000);
console.log(chalk.yellow(` Yahoo cooldown active (~${secs}s). We'll wait as needed during fetches.`));
}
await getHuggingFaceFreshness();
const spinner = ora('Analyzing for 200%+ return opportunities...').start();
const stopCooldownTicker = startYahooCooldownPolling(spinner, 'Analyzing for 200%+ return opportunities...');
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
});
if (analysisResult.success) {
stopCooldownTicker();
spinner.succeed('Double capital analysis completed');
console.log(chalk.green('200%+ Return Analysis Results:'));
console.log(chalk.gray('Request ID:'), analysisResult.requestId);
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`));
}
displayFallbackNotice(analysisResult.result);
}
}
else {
stopCooldownTicker();
spinner.fail('Double capital analysis failed');
console.error(chalk.red('Analysis request failed:'), analysisResult.error);
}
}
catch (error) {
stopCooldownTicker();
spinner.fail('Double capital analysis error');
console.error(chalk.red('Analysis request error:'), error);
}
}
catch (error) {
console.error(chalk.red('Double capital command failed:'), error);
}
});
// Earnings command
program
.command('earnings')
.description('CMT analysis of stocks with earnings THIS WEEK ONLY')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red(' Authentication required'));
process.exit(1);
}
console.log(getMarketStatusMessage());
const sys = await fetchSystemStatus();
if (sys?.yahooCooldown?.active) {
const secs = Math.ceil((sys.yahooCooldown.remainingMs || 0) / 1000);
console.log(chalk.yellow(` Yahoo cooldown active (~${secs}s). We'll wait as needed during fetches.`));
}
await getHuggingFaceFreshness();
const spinner = ora('Finding stocks with earnings THIS WEEK only...').start();
const stopCooldownTicker = startYahooCooldownPolling(spinner, 'Finding stocks with earnings THIS WEEK only...');
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
});
if (analysisResult.success) {
stopCooldownTicker();
spinner.succeed('Current week earnings analysis completed');
console.log(chalk.green('This Week\'s Earnings Analysis:'));
console.log(chalk.gray('Request ID:'), analysisResult.requestId);
if (analysisResult.result) {
console.log('\n' + chalk.blue('Earnings This Week:'));
const symbols = Array.isArray(analysisResult.result.symbols) ? analysisResult.result.symbols : [];
const count = typeof analysisResult.result.dataPoints === 'number' ? analysisResult.result.dataPoints : symbols.length;
if (count > 0 && symbols.length > 0) {
console.log(chalk.gray(`Found ${count} stocks with earnings this week`));
console.log(chalk.white('\nStocks with earnings this week:'), chalk.cyan(symbols.join(', ')));
}
else {
console.log(chalk.yellow('No stocks with earnings this week.'));
}
}
}
else {
stopCooldownTicker();
spinner.fail('Earnings analysis failed');
console.error(chalk.red('Analysis request failed:'), analysisResult.error);
}
}
catch (error) {
stopCooldownTicker();
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
program
.command('announcements')
.description('Market news review and fundamentals data collection')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red(' Authentication required'));
process.exit(1);
}
console.log(getMarketStatusMessage());
await getHuggingFaceFreshness();
const spinner = ora('Reviewing market news and fundamentals data...').start();
try {
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;
if (result.aiAnalysis) {
console.log('\n' + chalk.blue('Market News Review:'));
console.log(result.aiAnalysis);
}
if (result.symbols && result.symbols.length > 0) {
console.log(chalk.white('\nStocks identified for analysis:'), chalk.cyan(result.symbols.join(', ')));
}
if (result.timestamp) {
console.log(chalk.gray(`\nGenerated: ${new Date(result.timestamp).toLocaleString()}`));
}
displayFallbackNotice(analysisResult.result);
}
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) {
await displayAnalysisResults(requestId, session, getAnalysisResult);
}
else {
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, getAnalysisResult);
}
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'));
}
}
catch (error) {
spinner.fail('Failed to retrieve recent results');
console.log(chalk.yellow('No recent analysis results found.'));
console.warn('Recent results retrieval error', error);
}
}
}
catch (error) {
console.error(chalk.red(' Results command failed:'), 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());
const systemStatus = await fetchSystemStatus();
printSystemStatus(systemStatus);
}
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 {
await _logoutUser(session.userId, session.machineId);
spinner.succeed('Logged out from server');
}
catch (error) {
spinner.warn('Server logout failed, clearing local session');
console.warn('Server logout error', error);
}
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);
}
});
// Agent commands
program
.command('ingest')
.description('Run data ingestion pipeline for AI agent')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
const spinner = ora('Running data ingestion...').start();
try {
const result = await apiRequest('/agent/ingest', {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.sessionJwt}`,
},
body: JSON.stringify({}),
});
spinner.succeed('Data ingestion completed');
console.log(chalk.green('Ingestion Results:'));
console.log(result);
}
catch (error) {
spinner.fail('Data ingestion failed');
console.error(chalk.red('Ingestion error:'), error);
}
}
catch (error) {
console.error(chalk.red('Ingest command failed:'), error);
}
});
program
.command('predict')
.description('Run prediction pipeline for AI agent')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
const spinner = ora('Running predictions...').start();
try {
const result = await apiRequest('/agent/predict', {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.sessionJwt}`,
},
body: JSON.stringify({}),
});
spinner.succeed('Predictions completed');
console.log(chalk.green('Prediction Results:'));
console.log(result);
}
catch (error) {
spinner.fail('Predictions failed');
console.error(chalk.red('Prediction error:'), error);
}
}
catch (error) {
console.error(chalk.red('Predict command failed:'), error);
}
});
program
.command('benchmark')
.description('Run benchmark pipeline for AI agent')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
const spinner = ora('Running benchmark...').start();
try {
const result = await apiRequest('/agent/benchmark', {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.sessionJwt}`,
},
body: JSON.stringify({}),
});
spinner.succeed('Benchmark completed');
console.log(chalk.green('Benchmark Results:'));
console.log(result);
}
catch (error) {
spinner.fail('Benchmark failed');
console.error(chalk.red('Benchmark error:'), error);
}
}
catch (error) {
console.error(chalk.red('Benchmark command failed:'), error);
}
});
program
.command('agent-status')
.description('Get AI agent status and accuracy metrics')
.action(async () => {
try {
const session = await ensureAuthenticated();
if (!session) {
console.log(chalk.red('Authentication required'));
process.exit(1);
}
const spinner = ora('Fetching agent status...').start();
try {
const result = await apiRequest('/agent/status', {
headers: {
'Authorization': `Bearer ${session.sessionJwt}`,
},
});
spinner.succeed('Agent status retrieved');
console.log(chalk.green('Agent Status:'));
console.log(result);
}
catch (error) {
spinner.fail('Failed to get agent status');
console.error(chalk.red('Status error:'), error);
}
}
catch (error) {
console.error(chalk.red('Agent status command failed:'), error);
}
});
// 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));
});
// Parse command line arguments
program.parse();
}
// Enhanced error handler
function handleCliError(error) {
console.error(chalk.red(' CLI Error:'), error.message || error);
if (error.message?.includes('ENOTFOUND') || error.message?.includes('network')) {
console.log(chalk.yellow('[NETWORK] Issue Detected:'));
console.log(chalk.gray(' - Check your internet connection'));
console.log(chalk.gray(' - Verify you can access external websites'));
}
else if (error.message?.includes('Authentication') || error.message?.includes('401')) {
console.log(chalk.yellow('[AUTH] Issue:'));
console.log(chalk.gray(' - Run "mya login" to authenticate'));
console.log(chalk.gray(' - Check if your session has expired with "mya status"'));
}
else {
console.log(chalk.yellow('[INFO] Troubleshooting Tips:'));
console.log(chalk.gray(' - Check your authentication: "mya status"'));
console.log(chalk.gray(' - Verify internet connection'));
console.log(chalk.gray(' - Try "mya login" if authentication expired'));
}
}
// Run the CLI
main().catch(handleCliError);
//# sourceMappingURL=cli-http.js.map