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
JavaScript
"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'));