cli.ts•8.29 kB
#!/usr/bin/env node
import { Command } from 'commander';
import inquirer from 'inquirer';
import chalk from 'chalk';
import { SSHService } from './services/ssh-service.js';
import { VPSInitializer } from './tools/vps-initializer.js';
import { NginxManager } from './tools/nginx-manager.js';
import { GitHubCICD } from './tools/github-cicd.js';
import { logger } from './utils/logger.js';
import { SSHConfig } from './types/index.js';
const program = new Command();
program.name('mcp-vps-init').description('VPS initialization and management CLI').version('1.0.0');
program
.command('interactive')
.description('Interactive VPS setup wizard')
.action(async () => {
try {
console.log(chalk.blue.bold('🚀 MCP VPS Initialize - Interactive Setup'));
console.log();
// SSH Configuration
const sshConfig = await inquirer.prompt([
{
type: 'input',
name: 'host',
message: 'VPS IP address or hostname:',
validate: input => input.length > 0 || 'Host is required',
},
{
type: 'input',
name: 'username',
message: 'SSH username:',
default: 'root',
},
{
type: 'list',
name: 'authMethod',
message: 'Authentication method:',
choices: ['password', 'private-key'],
},
{
type: 'password',
name: 'password',
message: 'SSH password:',
when: answers => answers.authMethod === 'password',
},
{
type: 'input',
name: 'privateKeyPath',
message: 'Private key path:',
when: answers => answers.authMethod === 'private-key',
validate: input => input.length > 0 || 'Private key path is required',
},
{
type: 'password',
name: 'passphrase',
message: 'Private key passphrase (optional):',
when: answers => answers.authMethod === 'private-key',
},
]);
// Services Configuration
const services = await inquirer.prompt([
{
type: 'checkbox',
name: 'selected',
message: 'Select services to install:',
choices: [
{ name: 'Node.js (LTS)', value: 'nodejs' },
{ name: 'PM2 Process Manager', value: 'pm2' },
{ name: 'Rust Toolchain', value: 'rust' },
{ name: 'Nginx Web Server', value: 'nginx' },
{ name: 'Redis Database', value: 'redis' },
],
},
]);
// Nginx Configuration
let nginxConfig = null;
if (services.selected.includes('nginx')) {
nginxConfig = await inquirer.prompt([
{
type: 'confirm',
name: 'configure',
message: 'Configure Nginx with domain and SSL?',
default: true,
},
{
type: 'input',
name: 'domain',
message: 'Domain name:',
when: answers => answers.configure,
validate: input => input.length > 0 || 'Domain is required',
},
{
type: 'number',
name: 'port',
message: 'Backend port to proxy to:',
default: 3000,
when: answers => answers.configure,
},
{
type: 'confirm',
name: 'ssl',
message: "Enable SSL with Let's Encrypt?",
default: true,
when: answers => answers.configure,
},
]);
}
// GitHub CI/CD Configuration
const githubConfig = await inquirer.prompt([
{
type: 'confirm',
name: 'setup',
message: 'Setup GitHub CI/CD?',
default: false,
},
{
type: 'input',
name: 'repoUrl',
message: 'GitHub repository URL:',
when: answers => answers.setup,
validate: input => input.length > 0 || 'Repository URL is required',
},
{
type: 'input',
name: 'deployPath',
message: 'Deployment path on server:',
default: '/opt/deployments/app',
when: answers => answers.setup,
},
]);
console.log();
console.log(chalk.yellow('🔄 Starting VPS setup...'));
// Execute setup
const sshService = new SSHService(sshConfig as SSHConfig);
const connected = await sshService.connect();
if (!connected) {
console.error(chalk.red('❌ Failed to connect to VPS'));
return;
}
console.log(chalk.green('✅ Connected to VPS'));
// Initialize VPS
const vpsInitializer = new VPSInitializer(sshService);
const servicesConfig = services.selected.reduce(
(acc: Record<string, boolean>, service: string) => {
acc[service] = true;
return acc;
},
{}
);
console.log(chalk.blue('📦 Installing services...'));
const initResults = await vpsInitializer.initializeVPS(servicesConfig);
initResults.forEach(result => {
const icon = result.success ? '✅' : '❌';
const color = result.success ? chalk.green : chalk.red;
console.log(`${icon} ${result.service}: ${color(result.message)}`);
});
// Setup Nginx
if (nginxConfig?.configure) {
console.log(chalk.blue('🌐 Configuring Nginx...'));
const nginxManager = new NginxManager(sshService);
const nginxResult = await nginxManager.setupNginx(nginxConfig);
const icon = nginxResult.success ? '✅' : '❌';
const color = nginxResult.success ? chalk.green : chalk.red;
console.log(`${icon} Nginx: ${color(nginxResult.message)}`);
}
// Setup GitHub CI/CD
if (githubConfig.setup) {
console.log(chalk.blue('🚀 Setting up GitHub CI/CD...'));
const githubCICD = new GitHubCICD(sshService);
const cicdResult = await githubCICD.setupCICD(githubConfig);
const icon = cicdResult.success ? '✅' : '❌';
const color = cicdResult.success ? chalk.green : chalk.red;
console.log(`${icon} GitHub CI/CD: ${color(cicdResult.message)}`);
if (cicdResult.success) {
const instructions = await githubCICD.getSetupInstructions(cicdResult);
console.log();
console.log(chalk.cyan(instructions));
}
}
await sshService.disconnect();
console.log();
console.log(chalk.green.bold('🎉 VPS setup completed!'));
} catch (error) {
console.error(
chalk.red('❌ Setup failed:'),
error instanceof Error ? error.message : 'Unknown error'
);
logger.error('CLI setup failed', { error });
process.exit(1);
}
});
program
.command('connect')
.description('Test SSH connection to VPS')
.requiredOption('-h, --host <host>', 'VPS hostname or IP')
.requiredOption('-u, --username <username>', 'SSH username')
.option('-p, --password <password>', 'SSH password')
.option('-k, --key <path>', 'Private key path')
.option('--passphrase <passphrase>', 'Private key passphrase')
.action(async options => {
try {
const config: SSHConfig = {
host: options.host,
username: options.username,
password: options.password,
privateKeyPath: options.key,
passphrase: options.passphrase,
};
const sshService = new SSHService(config);
const connected = await sshService.connect();
if (connected) {
console.log(chalk.green('✅ SSH connection successful'));
const result = await sshService.executeCommand('uname -a');
console.log(chalk.blue('System info:'), result.stdout);
await sshService.disconnect();
} else {
console.log(chalk.red('❌ SSH connection failed'));
process.exit(1);
}
} catch (error) {
console.error(
chalk.red('Connection error:'),
error instanceof Error ? error.message : 'Unknown error'
);
process.exit(1);
}
});
// Error handling
program.exitOverride();
try {
program.parse();
} catch (error) {
console.error(chalk.red('CLI error:'), error instanceof Error ? error.message : 'Unknown error');
process.exit(1);
}