UNPKG

docker-pilot

Version:

A powerful, scalable Docker CLI library for managing containerized applications of any size

1,024 lines (1,023 loc) 86.2 kB
"use strict"; /** * Interactive Menu System for Docker Pilot * Provides a terminal-based menu interface similar to the legacy CLI */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.InteractiveMenu = void 0; const readline = __importStar(require("readline")); const path = __importStar(require("path")); const Logger_1 = require("../utils/Logger"); const i18n_1 = require("../utils/i18n"); class InteractiveMenu { constructor(dockerPilot) { this.isRunning = false; this.dockerPilot = dockerPilot; this.logger = new Logger_1.Logger(); this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); // Initialize i18n this.i18n = new i18n_1.I18n(); this.updateLanguage(); } /** * Start the interactive menu */ async start() { this.isRunning = true; // Check if language is configured, if not ask user to choose const config = this.dockerPilot.getConfig(); if (!config?.language) { await this.selectInitialLanguage(); } else { this.updateLanguage(); } // Check Docker status before starting if (!(await this.checkDockerStatus())) { await this.handleDockerNotRunning(); return; } // Auto-detect docker-compose files await this.autoDetectProject(); this.clearScreen(); this.showWelcome(); await this.showMainMenu(); } /** * Stop the interactive menu */ stop() { this.isRunning = false; if (this.rl) { try { this.rl.removeAllListeners(); this.rl.close(); } catch (error) { // Ignore errors when closing readline } } } /** * Select initial language if not configured */ async selectInitialLanguage() { this.clearScreen(); // Use default language for initial display this.i18n.setLanguage('en'); console.log(this.i18n.t('language.welcome')); console.log(''); console.log(this.i18n.t('language.choose_initial')); console.log(this.i18n.t('language.option_english')); console.log(this.i18n.t('language.option_portuguese')); console.log(''); let validChoice = false; let selectedLanguage = 'en'; while (!validChoice) { const choice = await this.askQuestion(this.i18n.t('language.enter_choice')); if (choice === '1') { selectedLanguage = 'en'; validChoice = true; } else if (choice === '2') { selectedLanguage = 'pt-br'; validChoice = true; } else { console.log(this.i18n.t('language.invalid_choice')); console.log(''); } } // Update language in i18n this.i18n.setLanguage(selectedLanguage); // Update language in DockerPilot and save to config await this.dockerPilot.updateLanguage(selectedLanguage); console.log(''); console.log(this.i18n.t('language.configured')); console.log(''); await this.sleep(1500); } /** * Check if Docker is running */ async checkDockerStatus() { try { const result = await this.dockerPilot.getCommandRunner().exec('docker info', { silent: true }); return result.success; } catch (error) { return false; } } /** * Handle Docker not running scenario */ async handleDockerNotRunning() { this.updateLanguage(); this.logger.error(this.i18n.t('docker.not_running')); this.logger.newLine(); this.logger.info(this.i18n.t('docker.options_available')); this.logger.info(this.i18n.t('docker.start_desktop')); this.logger.info(this.i18n.t('docker.start_linux')); this.logger.info(this.i18n.t('docker.start_macos')); this.logger.newLine(); this.logger.info(this.i18n.t('docker.restart_tip')); this.logger.newLine(); const answer = await this.askQuestion(this.i18n.t('docker.retry_or_exit')); const exitWords = ['sair', 'exit', 'quit', 'q']; if (exitWords.some(word => answer.toLowerCase() === word)) { this.logger.info(`👋 ${this.i18n.t('docker.pilot_finished')}`); process.exit(0); } else { this.clearScreen(); if (await this.checkDockerStatus()) { this.logger.success(this.i18n.t('docker.working_now')); this.logger.newLine(); await this.showMainMenu(); } else { await this.handleDockerNotRunning(); } } } /** * Show welcome message */ showWelcome() { this.updateLanguage(); const config = this.dockerPilot.getConfig(); const projectName = config?.projectName || 'Docker Project'; console.log(this.i18n.t('menu.welcome', { projectName: projectName, version: '2.0' })); // Show project info const workingDir = this.dockerPilot.getWorkingDirectory(); const services = config ? Object.keys(config.services) : []; const servicesText = services.length > 0 ? services.join(', ') : this.i18n.t('menu.no_services'); console.log(this.i18n.t('menu.directory', { path: workingDir })); console.log(this.i18n.t('menu.services', { services: servicesText })); console.log(''); } /** * Show main menu */ async showMainMenu() { if (!this.isRunning) return; try { const config = this.dockerPilot.getConfig(); if (!config) { this.logger.error('Configuração não encontrada!'); this.stop(); process.exit(1); } const menuOptions = this.buildMenuOptions(config); this.displayMenu(menuOptions); const choice = await this.askQuestion(this.i18n.t('menu.choose') + ' '); if (choice === '0') { const config = this.dockerPilot.getConfig(); const projectName = config?.projectName || 'Docker Project'; this.logger.info(`👋 ${this.i18n.t('menu.goodbye', { projectName })}`); this.stop(); process.exit(0); } const selectedOption = menuOptions.find(option => option.key === choice); if (selectedOption) { this.clearScreen(); console.log('='.repeat(60)); console.log(this.i18n.t('menu.executing', { command: selectedOption.label })); console.log('='.repeat(60)); console.log(''); try { await selectedOption.action(); await this.askToContinue(); } catch (error) { this.logger.error('Erro ao executar comando', error); await this.askToContinue(); } } else { this.logger.error(this.i18n.t('menu.invalid_choice')); await this.sleep(2000); this.clearScreen(); this.showWelcome(); await this.showMainMenu(); } } catch (error) { // Handle readline closure gracefully if (error instanceof Error && (error.message.includes('fechada') || error.message.includes('closed'))) { const config = this.dockerPilot.getConfig(); const projectName = config?.projectName || 'Docker Project'; console.log('\n👋 ' + this.i18n.t('menu.goodbye', { projectName })); this.stop(); process.exit(0); } else { this.logger.error('Erro no menu principal:', error); this.stop(); process.exit(1); } } } /** * Build menu options dynamically */ buildMenuOptions(config) { const options = []; let optionKey = 1; // Basic commands options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.setup'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { console.log(this.i18n.t('command.detecting_services')); // Ask user if they want to replace existing services const config = this.dockerPilot.getConfig(); const currentServices = config ? Object.keys(config.services) : []; let replaceExisting = false; if (currentServices.length > 0) { console.log(''); console.log(this.i18n.t('command.current_services', { services: currentServices.join(', ') })); const replaceAnswer = await this.askQuestion(this.i18n.t('command.replace_services_question')); replaceExisting = ['s', 'sim', 'y', 'yes'].some(ans => replaceAnswer.toLowerCase() === ans); } else { // If no current services, always replace (sync) replaceExisting = true; } await this.dockerPilot.detectServices(replaceExisting); console.log(this.i18n.t('command.detection_complete')); // Show updated service status after detection await this.displayServiceStatus(); await this.sleep(2000); this.clearScreen(); this.showWelcome(); await this.showMainMenu(); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.start_all'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { await this.dockerPilot.executeCommand('up', []); await this.displayServiceStatus(); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.stop_all'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { try { // Execute DownCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { DownCommand } = await Promise.resolve().then(() => __importStar(require('../commands/DownCommand'))); const downCommand = new DownCommand(context); this.logger.info('🛑 Stopping all services...'); console.log('\n' + '='.repeat(50)); const result = await downCommand.execute([], {}); console.log('='.repeat(50)); if (result.success) { this.logger.success('✅ All services stopped successfully'); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(`❌ Failed to stop services: ${result.error}`); } } catch (error) { this.logger.error('Failed to stop services', error); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.restart_all'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { try { // Execute RestartCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { RestartCommand } = await Promise.resolve().then(() => __importStar(require('../commands/RestartCommand'))); const restartCommand = new RestartCommand(context); const result = await restartCommand.execute([], {}); if (result.success) { console.log(''); this.logger.success('✅ All services restarted successfully'); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } // Show updated status after restart await this.displayServiceStatus(); } else { this.logger.error(result.error || 'Failed to restart services'); } } catch (error) { this.logger.error(`Restart command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: 'Build todos os serviços', category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { // Ask user for build options this.logger.info('🔨 Opções de build:'); console.log('1. Build normal'); console.log('2. Build sem cache (--no-cache)'); console.log('3. Build com pull (--pull)'); console.log('4. Build sem cache e com pull'); console.log(''); const choice = await this.askQuestion(this.i18n.t('menu.choose') + ' '); switch (choice) { case '1': await this.dockerPilot.executeCommand('build', []); break; case '2': await this.dockerPilot.executeCommand('build', ['--no-cache']); break; case '3': await this.dockerPilot.executeCommand('build', ['--pull']); break; case '4': await this.dockerPilot.executeCommand('build', ['--pull', '--no-cache']); break; default: this.logger.warn(this.i18n.t('error.invalid_choice')); return; } await this.displayServiceStatus(); await this.askToContinue(); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.rebuild_all'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { await this.dockerPilot.executeCommand('build', ['--no-cache']); await this.dockerPilot.executeCommand('up', []); await this.displayServiceStatus(); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.logs_all'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { try { // Ask user if they want to follow logs or see recent logs this.logger.info('📋 ' + this.i18n.t('command.logs_options')); console.log('1. ' + this.i18n.t('command.logs_follow_all')); console.log('2. ' + this.i18n.t('command.logs_recent_all')); console.log('3. ' + this.i18n.t('command.logs_tail_custom')); console.log(''); const choice = await this.askQuestion(this.i18n.t('menu.choose') + ' '); // Execute LogsCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { LogsCommand } = await Promise.resolve().then(() => __importStar(require('../commands/LogsCommand'))); const logsCommand = new LogsCommand(context); let logsArgs = []; switch (choice) { case '1': this.logger.info('🔄 ' + this.i18n.t('command.following_logs_all')); this.logger.info('💡 ' + this.i18n.t('command.stop_logs_tip')); this.logger.newLine(); logsArgs.push('--follow'); break; case '2': logsArgs.push('--tail', '50'); break; case '3': const tailCount = await this.askQuestion(this.i18n.t('command.logs_tail_count')); const count = parseInt(tailCount) || 50; logsArgs.push('--tail', count.toString()); break; default: this.logger.warn(this.i18n.t('error.invalid_choice')); return; } const result = await logsCommand.execute(logsArgs, {}); if (result.success) { if (result.output && result.output.trim() !== '') { console.log(''); console.log(result.output); } console.log(''); this.logger.success('✅ Logs retrieved successfully'); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(result.error || 'Failed to retrieve logs'); } } catch (error) { this.logger.error(`Logs command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.status'), category: '🚀 ' + this.i18n.t('command.basic'), action: async () => { try { const context = this.createCommandContext(); if (!context) { this.logger.error('Configuration not available in context'); return; } const { StatusCommand } = await Promise.resolve().then(() => __importStar(require('../commands/StatusCommand'))); const statusCommand = new StatusCommand(context); const result = await statusCommand.execute([], {}); if (result.success) { // Show the status output if available if (result.output && result.output.trim() !== '[]') { console.log(''); console.log(result.output); } console.log(''); this.logger.success(this.i18n.t('command.status_complete')); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(result.error || this.i18n.t('command.status_failed')); } } catch (error) { this.logger.error(`Status command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }); // Advanced commands options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.shell'), category: '🛠️ ' + this.i18n.t('command.advanced'), action: async () => { try { // Get current config first const currentConfig = this.dockerPilot.getConfig(); if (!currentConfig) { this.logger.error('Configuration not available'); return; } const services = Object.keys(currentConfig.services); if (services.length === 0) { this.logger.warn(this.i18n.t('command.no_services_configured')); return; } this.logger.info(this.i18n.t('command.available_services')); services.forEach((service, index) => { this.logger.info(`${index + 1}. ${service}`); }); const choice = await this.askQuestion(this.i18n.t('command.choose_service')); const serviceIndex = parseInt(choice) - 1; const serviceName = services[serviceIndex] || services[0]; if (!serviceName) { this.logger.error(this.i18n.t('command.no_valid_service')); return; } // Execute ShellCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { ShellCommand } = await Promise.resolve().then(() => __importStar(require('../commands/ShellCommand'))); const shellCommand = new ShellCommand(context); const result = await shellCommand.execute([serviceName], {}); if (result.success) { console.log(''); this.logger.success(`✅ Shell session completed for ${serviceName}`); if (result.executionTime) { this.logger.info(`⏱️ Session duration: ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(result.error || `Failed to open shell for ${serviceName}`); } } catch (error) { this.logger.error(`Shell command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: 'Executar comando customizado', category: '🛠️ ' + this.i18n.t('command.advanced'), action: async () => { try { // Get current config first const currentConfig = this.dockerPilot.getConfig(); if (!currentConfig) { this.logger.error('Configuration not available'); return; } const services = Object.keys(currentConfig.services); if (services.length === 0) { this.logger.warn(this.i18n.t('command.no_services_configured')); return; } this.logger.info(this.i18n.t('command.available_services')); services.forEach((service, index) => { this.logger.info(`${index + 1}. ${service}`); }); const serviceChoice = await this.askQuestion(this.i18n.t('command.choose_service')); const serviceIndex = parseInt(serviceChoice) - 1; const serviceName = services[serviceIndex] || services[0]; if (!serviceName) { this.logger.error(this.i18n.t('command.no_valid_service')); return; } const command = await this.askQuestion('Digite o comando para executar: '); if (!command.trim()) { this.logger.error('Command is required'); return; } console.log(''); this.logger.info(`🚀 Executando comando no serviço ${serviceName}: ${command}`); // Execute ExecCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { ExecCommand } = await Promise.resolve().then(() => __importStar(require('../commands/ExecCommand'))); const execCommand = new ExecCommand(context); const result = await execCommand.execute([serviceName, ...command.split(' ')], {}); if (result.success) { console.log(''); this.logger.success(`✅ Command executed successfully in ${serviceName}`); if (result.executionTime) { this.logger.info(`⏱️ Execution time: ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(result.error || `Failed to execute command in ${serviceName}`); } } catch (error) { this.logger.error(`Exec command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.health'), category: '🛠️ ' + this.i18n.t('command.advanced'), action: async () => { const serviceManager = this.dockerPilot.getServiceManager(); if (!serviceManager) { this.logger.error(this.i18n.t('command.service_manager_unavailable')); return; } const services = await serviceManager.getServiceStatus(); this.logger.info(this.i18n.t('command.health_status')); this.logger.newLine(); for (const service of services) { const statusIcon = service.state === 'running' ? '✅' : service.state === 'stopped' ? '❌' : '⚠️'; const health = service.health || 'N/A'; this.logger.info(`${statusIcon} ${service.name}: ${service.state} (Health: ${health})`); } this.logger.newLine(); this.logger.success(this.i18n.t('command.health_check_complete')); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.monitor'), category: '🛠️ ' + this.i18n.t('command.advanced'), action: async () => { this.logger.info(this.i18n.t('command.monitoring_start')); this.logger.info(this.i18n.t('command.monitoring_tip')); this.logger.newLine(); const monitorProcess = setInterval(async () => { try { this.clearScreen(); this.logger.info(this.i18n.t('command.docker_monitor', { time: new Date().toLocaleString() })); this.logger.separator('=', 50); this.logger.newLine(); await this.displayServiceStatus(); this.logger.info(this.i18n.t('command.press_ctrl_c')); } catch (error) { clearInterval(monitorProcess); } }, 5000); // Listen for Ctrl+C process.on('SIGINT', () => { clearInterval(monitorProcess); this.logger.newLine(); this.logger.info(this.i18n.t('command.monitoring_stopped')); }); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.update'), category: '🛠️ ' + this.i18n.t('command.advanced'), action: async () => { this.logger.loading(this.i18n.t('command.updating_images')); await this.dockerPilot.executeCommand('pull', []); await this.dockerPilot.executeCommand('build', ['--pull']); await this.dockerPilot.executeCommand('up', []); this.logger.success(this.i18n.t('command.update_complete')); } }); // Maintenance commands options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.clean'), category: '⚙️ ' + this.i18n.t('command.maintenance'), action: async () => { await this.dockerPilot.executeCommand('clean', []); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.deep_clean'), category: '⚙️ ' + this.i18n.t('command.maintenance'), action: async () => { const confirm = await this.askQuestion(this.i18n.t('command.deep_clean_warning')); if (confirm.toLowerCase() === 's' || confirm.toLowerCase() === 'sim' || confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') { await this.dockerPilot.executeCommand('down', ['--volumes']); await this.dockerPilot.executeCommand('clean', ['--all']); this.logger.success(this.i18n.t('command.deep_clean_complete')); } else { this.logger.info(this.i18n.t('command.operation_cancelled')); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.show_config'), category: '⚙️ ' + this.i18n.t('command.maintenance'), action: async () => { try { // Execute ConfigCommand directly const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { ConfigCommand } = await Promise.resolve().then(() => __importStar(require('../commands/ConfigCommand'))); const configCommand = new ConfigCommand(context); const result = await configCommand.execute(['show'], {}); if (result.success) { console.log(''); this.logger.success('✅ Configuration displayed successfully'); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } } else { this.logger.error(result.error || 'Failed to show configuration'); } } catch (error) { this.logger.error(`Config command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.advanced_settings'), category: '⚙️ ' + this.i18n.t('command.maintenance'), action: async () => { await this.showAdvancedSettings(); } }); // Compose file management commands options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.compose_list'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['list', '--variants']); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.compose_find'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { const searchDir = await this.askQuestion(this.i18n.t('command.compose_search_dir_prompt')); const dirToSearch = searchDir.trim() || process.cwd(); const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['find', dirToSearch]); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.compose_analyze'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { // First, let user select from available compose files const fileUtils = this.dockerPilot.getFileUtils(); const foundFiles = await fileUtils.findDockerComposeFilesWithInfo(undefined, { maxDepth: 6, includeVariants: true, includeEmptyFiles: false }); if (foundFiles.length === 0) { this.logger.warn(this.i18n.t('compose.no_files_found')); return; } if (foundFiles.length === 1) { // Only one file, analyze it directly const firstFile = foundFiles[0]; if (firstFile) { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['analyze', firstFile.path]); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } } else { // Multiple files, let user choose console.log(this.i18n.t('compose.available_files')); foundFiles.slice(0, 10).forEach((file, index) => { const envText = file.environment ? ` (${file.environment})` : ''; console.log(`${index + 1}. ${file.relativePath}${envText}`); }); const choice = await this.askQuestion(this.i18n.t('compose.select_file_to_analyze')); const selectedIndex = parseInt(choice) - 1; if (selectedIndex >= 0 && selectedIndex < foundFiles.length) { const selectedFile = foundFiles[selectedIndex]; if (selectedFile) { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['analyze', selectedFile.path]); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } } else { this.logger.error(this.i18n.t('error.invalid_choice')); } } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.compose_validate'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { // Similar to analyze, but for validation const fileUtils = this.dockerPilot.getFileUtils(); const foundFiles = await fileUtils.findDockerComposeFilesWithInfo(undefined, { maxDepth: 6, includeVariants: true, includeEmptyFiles: false }); if (foundFiles.length === 0) { this.logger.warn(this.i18n.t('compose.no_files_found')); return; } if (foundFiles.length === 1) { const firstFile = foundFiles[0]; if (firstFile) { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['validate', firstFile.path]); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } } else { console.log(this.i18n.t('compose.available_files')); foundFiles.slice(0, 10).forEach((file, index) => { const envText = file.environment ? ` (${file.environment})` : ''; console.log(`${index + 1}. ${file.relativePath}${envText}`); }); const choice = await this.askQuestion(this.i18n.t('compose.select_file_to_validate')); const selectedIndex = parseInt(choice) - 1; if (selectedIndex >= 0 && selectedIndex < foundFiles.length) { const selectedFile = foundFiles[selectedIndex]; if (selectedFile) { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['validate', selectedFile.path]); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } } else { this.logger.error(this.i18n.t('error.invalid_choice')); } } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.compose_services'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { const context = this.createCommandContext(); const composeCommand = new (await Promise.resolve().then(() => __importStar(require('../commands/ComposeCommand')))).ComposeCommand(context); const result = await composeCommand.execute(['services']); if (result.success) { console.log(result.output); } else { this.logger.error(result.error || 'Erro desconhecido'); } } }); // Compose file management commands options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.compose_show_primary'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { const primaryFile = this.dockerPilot.getPrimaryComposeFile(); if (primaryFile) { console.log(this.i18n.t('compose.current_primary', { file: path.relative(process.cwd(), primaryFile) })); } else { console.log(this.i18n.t('compose.no_primary_file')); } } }, { key: (optionKey++).toString(), label: this.i18n.t('command.compose_change_primary'), category: '📄 ' + this.i18n.t('command.compose_management'), action: async () => { await this.selectAndSetPrimaryComposeFile(); } }); // Service-specific commands Object.keys(config.services).forEach(serviceName => { const serviceDisplayName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1); options.push({ key: (optionKey++).toString(), label: this.i18n.t('command.start_service', { service: serviceName }), category: `🔧 ${serviceDisplayName}`, action: async () => { await this.dockerPilot.executeCommand('up', [serviceName]); await this.displayServiceStatus(serviceName); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.restart_service', { service: serviceName }), category: `🔧 ${serviceDisplayName}`, action: async () => { try { // Execute RestartCommand directly for specific service const currentConfig = this.dockerPilot.getConfig(); if (!currentConfig) { this.logger.error('Configuration not available'); return; } const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { RestartCommand } = await Promise.resolve().then(() => __importStar(require('../commands/RestartCommand'))); const restartCommand = new RestartCommand(context); const result = await restartCommand.execute([serviceName], {}); if (result.success) { console.log(''); this.logger.success(`✅ Service "${serviceName}" restarted successfully`); if (result.executionTime) { this.logger.info(`⏱️ Completed in ${result.executionTime.toFixed(2)}ms`); } // Show updated status after restart await this.displayServiceStatus(serviceName); } else { this.logger.error(result.error || `Failed to restart service "${serviceName}"`); } } catch (error) { this.logger.error(`Restart command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } }, { key: (optionKey++).toString(), label: `Build ${serviceName}`, category: `🔧 ${serviceDisplayName}`, action: async () => { // Ask user for build options for specific service this.logger.info(`🔨 Build options para ${serviceName}:`); console.log('1. Build normal'); console.log('2. Build sem cache (--no-cache)'); console.log('3. Build com pull (--pull)'); console.log('4. Build sem cache e com pull'); console.log(''); const choice = await this.askQuestion(this.i18n.t('menu.choose') + ' '); switch (choice) { case '1': await this.dockerPilot.executeCommand('build', [serviceName]); break; case '2': await this.dockerPilot.executeCommand('build', [serviceName, '--no-cache']); break; case '3': await this.dockerPilot.executeCommand('build', [serviceName, '--pull']); break; case '4': await this.dockerPilot.executeCommand('build', [serviceName, '--pull', '--no-cache']); break; default: this.logger.warn(this.i18n.t('error.invalid_choice')); return; } await this.displayServiceStatus(serviceName); await this.askToContinue(); } }, { key: (optionKey++).toString(), label: this.i18n.t('command.logs_service', { service: serviceName }), category: `🔧 ${serviceDisplayName}`, action: async () => { try { // Ask user for log viewing options this.logger.info(`📋 Logs do ${serviceName}:`); console.log('1. Seguir logs em tempo real'); console.log('2. Ver últimas 50 linhas'); console.log('3. Ver últimas N linhas'); console.log('4. Logs desde um tempo específico'); console.log(''); const choice = await this.askQuestion('Escolha uma opção: '); // Execute LogsCommand directly const config = this.dockerPilot.getConfig(); if (!config) { this.logger.error('Configuration not available'); return; } const context = this.createCommandContext(); if (!context) { this.logger.error('Failed to create command context'); return; } const { LogsCommand } = await Promise.resolve().then(() => __importStar(require('../commands/LogsCommand'))); const logsCommand = new LogsCommand(context); let logsArgs = [serviceName]; switch (choice) { case '1': this.logger.info(`🔄 Seguindo logs do ${serviceName}...`); this.logger.info('💡 ' + this.i18n.t('command.press_ctrl_c'));