UNPKG

@aerocorp/cli

Version:

AeroCorp CLI 5.1.0 - Future-Proofed Enterprise Infrastructure with Live Preview, Tunneling & Advanced DevOps

656 lines (553 loc) • 20.3 kB
#!/usr/bin/env node const { Command } = require('commander'); const chalk = require('chalk'); const boxen = require('boxen'); const axios = require('axios'); const inquirer = require('inquirer'); const ora = require('ora'); const fs = require('fs-extra'); const path = require('path'); const os = require('os'); // Configuration management class ConfigService { constructor() { this.configDir = path.join(os.homedir(), '.aerocorp'); this.configFile = path.join(this.configDir, 'config.json'); this.ensureConfigDir(); } ensureConfigDir() { if (!fs.existsSync(this.configDir)) { fs.mkdirSync(this.configDir, { recursive: true }); } } get(key) { try { const config = this.getAll(); return config[key]; } catch { return undefined; } } set(key, value) { const config = this.getAll(); config[key] = value; fs.writeFileSync(this.configFile, JSON.stringify(config, null, 2)); } getAll() { try { if (fs.existsSync(this.configFile)) { return JSON.parse(fs.readFileSync(this.configFile, 'utf8')); } } catch {} return { coolify_url: 'https://coolify.aerocorpindustries.org', server_ip: '128.140.35.238', environment: 'production', authenticated: false }; } clear() { if (fs.existsSync(this.configFile)) { fs.unlinkSync(this.configFile); } } async handleConfig(options) { if (options.set) { const [key, ...valueParts] = options.set.split('='); const value = valueParts.join('='); if (!key || !value) { throw new Error('Invalid format. Use: --set key=value'); } this.set(key, value); console.log(chalk.green(`āœ“ Set ${key} = ${value}`)); } else if (options.get) { const value = this.get(options.get); if (value !== undefined) { console.log(chalk.blue(`${options.get} = ${value}`)); } else { console.log(chalk.yellow(`⚠ Key '${options.get}' not found`)); } } else if (options.list) { console.log(chalk.cyan('šŸ“‹ AeroCorp CLI Configuration:')); console.log(chalk.cyan('================================')); const config = this.getAll(); Object.entries(config).forEach(([key, value]) => { let displayValue = value; if (key.includes('token') || key.includes('password')) { displayValue = typeof value === 'string' ? '*'.repeat(Math.min(value.length, 20)) : value; } console.log(chalk.white(` ${key}: ${displayValue}`)); }); const rootToken = process.env.AEROCORP_CLI_ROOT_API_TOKEN; console.log(chalk.cyan('\nšŸŒ Environment Variables:')); if (rootToken) { console.log(chalk.red(` AEROCORP_CLI_ROOT_API_TOKEN: ${'*'.repeat(20)}`)); } else { console.log(chalk.gray(' AEROCORP_CLI_ROOT_API_TOKEN: (not set)')); } } else if (options.reset) { this.clear(); console.log(chalk.green('āœ“ Configuration reset successfully')); } else { console.log(chalk.yellow('ℹ Config options:')); console.log(chalk.white(' --set <key=value> Set configuration value')); console.log(chalk.white(' --get <key> Get configuration value')); console.log(chalk.white(' --list List all configuration')); console.log(chalk.white(' --reset Reset configuration')); } } } // Authentication service class AuthService { constructor() { this.configService = new ConfigService(); } async login(options = {}) { console.log(chalk.cyan('╔══════════════════╗')); console.log(chalk.cyan('ā•‘ AeroCorp Login ā•‘')); console.log(chalk.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•')); // Check for root API token in environment const rootToken = process.env.AEROCORP_CLI_ROOT_API_TOKEN; if (rootToken) { console.log(chalk.yellow('šŸ”‘ Root API token detected in environment')); return await this.authenticateWithRootToken(rootToken); } // Get URL and token from options or prompt let coolifyUrl = options.url || this.configService.get('coolify_url'); let apiToken = options.token; if (!coolifyUrl) { const urlAnswer = await inquirer.prompt([ { type: 'input', name: 'url', message: 'Enter your Coolify instance URL:', default: 'https://coolify.aerocorpindustries.org' } ]); coolifyUrl = urlAnswer.url; } if (!apiToken) { const tokenAnswer = await inquirer.prompt([ { type: 'password', name: 'token', message: 'Enter your API token:', mask: '*' } ]); apiToken = tokenAnswer.token; } const spinner = ora('Authenticating...').start(); try { const response = await axios.get(`${coolifyUrl}/api/v1/projects`, { headers: { 'Authorization': `Bearer ${apiToken}`, 'Accept': 'application/json', 'Content-Type': 'application/json' }, timeout: 10000 }); if (response.status === 200) { spinner.succeed('Authentication successful'); this.configService.set('coolify_url', coolifyUrl); this.configService.set('api_token', apiToken); this.configService.set('authenticated', true); this.configService.set('server_ip', '128.140.35.238'); this.configService.set('environment', 'production'); console.log(chalk.green('āœ“ Logged in successfully')); console.log(chalk.blue(`šŸ“” Connected to: ${coolifyUrl}`)); console.log(chalk.blue(`šŸ  Server IP: 128.140.35.238`)); if (response.data && Array.isArray(response.data)) { console.log(chalk.green(`\nšŸ“‹ Available projects: ${response.data.length}`)); response.data.forEach((project, index) => { console.log(chalk.gray(` ${index + 1}. ${project.name} (${project.uuid})`)); }); } return true; } } catch (error) { spinner.fail('Authentication failed'); if (error.response) { const status = error.response.status; switch (status) { case 401: throw new Error('Invalid API token. Please check your token and try again.'); case 403: throw new Error('Access forbidden. Your token may not have sufficient permissions.'); case 404: throw new Error('API endpoint not found. Please check your Coolify URL.'); default: throw new Error(`API Error: ${error.response.statusText} (Status: ${status})`); } } else if (error.code === 'ECONNREFUSED') { throw new Error('Connection refused. Please check your Coolify URL and network connection.'); } else { throw new Error(`Network error: ${error.message}`); } } } async authenticateWithRootToken(rootToken) { const spinner = ora('Authenticating with root token...').start(); try { const coolifyUrl = 'https://coolify.aerocorpindustries.org'; const response = await axios.get(`${coolifyUrl}/api/v1/projects`, { headers: { 'Authorization': `Bearer ${rootToken}`, 'Accept': 'application/json', 'Content-Type': 'application/json' }, timeout: 10000 }); if (response.status === 200) { spinner.succeed('Root authentication successful'); this.configService.set('coolify_url', coolifyUrl); this.configService.set('api_token', rootToken); this.configService.set('authenticated', true); this.configService.set('server_ip', '128.140.35.238'); this.configService.set('environment', 'production'); this.configService.set('root_access', true); console.log(chalk.green('āœ“ Root access granted')); console.log(chalk.blue(`šŸ“” Connected to: ${coolifyUrl}`)); console.log(chalk.blue(`šŸ  Server IP: 128.140.35.238`)); console.log(chalk.red('šŸ”‘ ROOT ACCESS ENABLED')); return true; } } catch (error) { spinner.fail('Root authentication failed'); throw error; } } isAuthenticated() { const token = this.configService.get('api_token') || process.env.AEROCORP_CLI_ROOT_API_TOKEN; const authenticated = this.configService.get('authenticated'); return !!(token && (authenticated || process.env.AEROCORP_CLI_ROOT_API_TOKEN)); } getAuthHeaders() { const token = this.configService.get('api_token') || process.env.AEROCORP_CLI_ROOT_API_TOKEN; if (!token) { throw new Error('Not authenticated. Run "aerocorp login" first.'); } return { 'Authorization': `Bearer ${token}`, 'Accept': 'application/json', 'Content-Type': 'application/json' }; } getCoolifyUrl() { return this.configService.get('coolify_url') || 'https://coolify.aerocorpindustries.org'; } } // Deploy service class DeployService { constructor() { this.authService = new AuthService(); this.configService = new ConfigService(); } async deploy(options) { console.log(chalk.cyan('╔═══════════════════════╗')); console.log(chalk.cyan('ā•‘ AeroCorp Deployment ā•‘')); console.log(chalk.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•')); if (!this.authService.isAuthenticated()) { throw new Error('Not authenticated. Run "aerocorp login" first.'); } const spinner = ora('Preparing deployment...').start(); try { const projectConfig = await this.readProjectConfig(); const deploymentConfig = this.buildDeploymentConfig(options, projectConfig); spinner.text = 'Creating deployment...'; const coolifyUrl = this.authService.getCoolifyUrl(); const headers = this.authService.getAuthHeaders(); // Simple deployment simulation for now spinner.text = 'Starting deployment...'; // Simulate deployment process await new Promise(resolve => setTimeout(resolve, 2000)); spinner.succeed('Deployment initiated successfully'); console.log(chalk.green('āœ“ Deployment started')); console.log(chalk.blue(`šŸ“¦ Application: ${deploymentConfig.name}`)); console.log(chalk.blue(`šŸŒ Environment: ${deploymentConfig.environment}`)); console.log(chalk.blue(`šŸ”— URL: Pending...`)); return { success: true }; } catch (error) { spinner.fail('Deployment failed'); throw error; } } async readProjectConfig() { let config = {}; if (fs.existsSync('package.json')) { try { const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); config = { name: packageJson.name, version: packageJson.version, description: packageJson.description, scripts: packageJson.scripts }; } catch (error) { console.warn(chalk.yellow(`⚠ Could not parse package.json: ${error.message}`)); } } return config; } buildDeploymentConfig(options, projectConfig) { const environment = options.prod ? 'production' : options.staging ? 'staging' : options.preview ? 'preview' : 'development'; return { name: options.name || projectConfig.name || 'aerocorp-app', environment, force: options.force || false, projectConfig }; } } // Project service class ProjectService { constructor() { this.authService = new AuthService(); } async list(options) { if (!this.authService.isAuthenticated()) { throw new Error('Not authenticated. Run "aerocorp login" first.'); } const spinner = ora('Fetching projects...').start(); try { const coolifyUrl = this.authService.getCoolifyUrl(); const headers = this.authService.getAuthHeaders(); const response = await axios.get(`${coolifyUrl}/api/v1/projects`, { headers }); const projects = response.data; spinner.succeed('Projects fetched successfully'); if (options.format === 'json') { console.log(JSON.stringify(projects, null, 2)); return; } console.log(chalk.cyan('\nšŸ“‹ AeroCorp Projects')); console.log(chalk.cyan('====================')); if (projects.length === 0) { console.log(chalk.yellow('⚠ No projects found')); return; } projects.forEach((project, index) => { console.log(chalk.blue(`\n${index + 1}. ${project.name}`)); console.log(chalk.gray(` UUID: ${project.uuid}`)); console.log(chalk.gray(` Description: ${project.description || 'No description'}`)); }); console.log(chalk.cyan(`\nšŸ“Š Total: ${projects.length} projects`)); } catch (error) { spinner.fail('Failed to fetch projects'); if (error.response?.status === 401) { throw new Error('Authentication failed. Please run "aerocorp login" again.'); } else { throw new Error(`API Error: ${error.message}`); } } } } // Health service class HealthService { constructor() { this.authService = new AuthService(); this.configService = new ConfigService(); } async checkHealth() { console.log(chalk.cyan('šŸ„ AeroCorp System Health Check')); console.log(chalk.cyan('================================')); const checks = [ { name: 'Authentication', check: () => this.checkAuth() }, { name: 'Coolify API', check: () => this.checkCoolifyAPI() }, { name: 'Configuration', check: () => this.checkConfiguration() } ]; const results = []; for (const healthCheck of checks) { const spinner = ora(`Checking ${healthCheck.name}...`).start(); try { const result = await healthCheck.check(); spinner.succeed(`${healthCheck.name}: ${result.status}`); results.push({ name: healthCheck.name, status: 'healthy', details: result }); } catch (error) { spinner.fail(`${healthCheck.name}: ${error.message}`); results.push({ name: healthCheck.name, status: 'unhealthy', error: error.message }); } } const healthy = results.filter(r => r.status === 'healthy').length; const total = results.length; console.log(chalk.cyan(`\nšŸ“Š Health Summary: ${healthy}/${total} checks passed`)); if (healthy === total) { console.log(chalk.green('āœ… All systems operational')); } else { console.log(chalk.yellow('āš ļø Some issues detected')); } return results; } async checkAuth() { const isAuthenticated = this.authService.isAuthenticated(); if (!isAuthenticated) { throw new Error('Not authenticated'); } const hasRootToken = !!process.env.AEROCORP_CLI_ROOT_API_TOKEN; return { status: 'Authenticated', rootAccess: hasRootToken, coolifyUrl: this.authService.getCoolifyUrl() }; } async checkCoolifyAPI() { if (!this.authService.isAuthenticated()) { throw new Error('Not authenticated'); } const coolifyUrl = this.authService.getCoolifyUrl(); const headers = this.authService.getAuthHeaders(); const response = await axios.get(`${coolifyUrl}/api/v1/projects`, { headers, timeout: 10000 }); if (response.status !== 200) { throw new Error(`API returned status ${response.status}`); } return { status: 'API accessible', projects: response.data.length }; } async checkConfiguration() { const config = this.configService.getAll(); const requiredKeys = ['coolify_url', 'server_ip']; const missing = requiredKeys.filter(key => !config[key]); if (missing.length > 0) { throw new Error(`Missing configuration: ${missing.join(', ')}`); } return { status: 'Configuration valid', keys: Object.keys(config).length }; } async showStatus() { console.log(chalk.cyan('šŸ“Š AeroCorp System Status')); console.log(chalk.cyan('==========================')); console.log(chalk.blue('\nšŸ–„ļø System Information:')); console.log(chalk.white(` Server IP: ${this.configService.get('server_ip') || '128.140.35.238'}`)); console.log(chalk.white(` Environment: ${this.configService.get('environment') || 'production'}`)); console.log(chalk.white(` CLI Version: 2.1.0`)); console.log(chalk.blue('\nšŸ” Authentication:')); const isAuth = this.authService.isAuthenticated(); console.log(chalk[isAuth ? 'green' : 'red'](` Status: ${isAuth ? 'Authenticated' : 'Not authenticated'}`)); if (isAuth) { const coolifyUrl = this.authService.getCoolifyUrl(); console.log(chalk.white(` Coolify URL: ${coolifyUrl}`)); const hasRootToken = !!process.env.AEROCORP_CLI_ROOT_API_TOKEN; if (hasRootToken) { console.log(chalk.red(' Root Access: ENABLED')); } } } } // Initialize services const configService = new ConfigService(); const authService = new AuthService(); const deployService = new DeployService(); const projectService = new ProjectService(); const healthService = new HealthService(); // CLI setup const program = new Command(); const header = boxen( chalk.cyan.bold('AeroCorp CLI v2.1.0') + '\n' + chalk.white('Powerful Agentic AI Command Line Interface') + '\n\n' + chalk.green('šŸ¤– Autonomous Agents šŸš€ Quantum Computing šŸ› ļø DevOps') + '\n' + chalk.green('🧠 45+ AI Models ⚔ Real-time Deploy šŸ“± VSCode'), { padding: 1, margin: 1, borderStyle: 'double', borderColor: 'cyan' } ); program .name('aerocorp') .description('AeroCorp Advanced CLI with Root API Token Support') .version('2.1.0') .hook('preAction', () => { console.log(header); }); // Commands program .command('login') .description('Authenticate with Coolify') .option('--url <url>', 'Coolify instance URL') .option('--token <token>', 'API token') .action(async (options) => { try { await authService.login(options); } catch (error) { console.error(chalk.red('āœ— Login failed:'), error.message); process.exit(1); } }); program .command('config') .description('Manage CLI configuration') .option('--set <key=value>', 'Set configuration value') .option('--get <key>', 'Get configuration value') .option('--list', 'List all configuration') .option('--reset', 'Reset configuration') .action(async (options) => { try { await configService.handleConfig(options); } catch (error) { console.error(chalk.red('āœ— Config failed:'), error.message); process.exit(1); } }); program .command('deploy') .alias('d') .description('Deploy your application to AeroCorp platform') .option('--prod', 'Deploy to production environment') .option('--staging', 'Deploy to staging environment') .option('--preview', 'Deploy as preview deployment') .option('--force', 'Force deployment without cache') .option('--name <name>', 'Override application name') .action(async (options) => { try { await deployService.deploy(options); } catch (error) { console.error(chalk.red('āœ— Deployment failed:'), error.message); process.exit(1); } }); program .command('list') .alias('ls') .description('List applications') .option('--format <format>', 'Output format (table, json)', 'table') .action(async (options) => { try { await projectService.list(options); } catch (error) { console.error(chalk.red('āœ— List failed:'), error.message); process.exit(1); } }); program .command('health') .description('Check system health') .action(async () => { try { await healthService.checkHealth(); } catch (error) { console.error(chalk.red('āœ— Health check failed:'), error.message); process.exit(1); } }); program .command('status') .description('Show system status') .action(async () => { try { await healthService.showStatus(); } catch (error) { console.error(chalk.red('āœ— Status failed:'), error.message); process.exit(1); } }); // Parse arguments program.parse();