ssh-bridge-ai
Version:
AI-Powered SSH Tool with Bulletproof Connections & Enterprise Sandbox Security + Cursor-like Confirmation - Enable AI assistants to securely SSH into your servers with persistent sessions, keepalive, automatic recovery, sandbox command testing, and user c
1,316 lines (1,127 loc) ⢠120 kB
JavaScript
#!/usr/bin/env node
const { program, Command } = require('commander');
const chalk = require('chalk');
const ora = require('ora');
const inquirer = require('inquirer');
const path = require('path');
const { APIClient } = require('./api');
const { Config } = require('./config');
const { AutoUpdater } = require('./updater');
const { IntegrityChecker } = require('./security/integrity-checker');
const { FeatureGate } = require('./security/feature-gate');
// Initialize these lazily to avoid requiring environment variables for basic commands
let api = null;
let config = null;
let updater = null;
let integrityChecker = null;
let featureGate = null;
// Version selector function
async function selectVersion() {
// Skip version selection for help, version, and basic commands
const args = process.argv.slice(2);
const isBasicCommand = args.length === 0 ||
args.includes('-V') ||
args.includes('--version') ||
args.includes('-h') ||
args.includes('--help') ||
args.includes('--help-all') ||
args.includes('help-simple') ||
args.includes('list-all') ||
args.includes('install-help') ||
args.includes('setup') ||
args.includes('interactive') ||
args.includes('change-mode') ||
args.includes('quick-start') ||
args.includes('examples') ||
args.includes('troubleshoot') ||
args.includes('sandbox') ||
args.includes('security') ||
args.includes('license') ||
args.includes('keygen') ||
args.includes('keys') ||
args.includes('copy-key') ||
args.includes('exec') ||
args.includes('run') ||
args.includes('connect') ||
args.includes('session') ||
args.includes('copy') ||
args.includes('register') ||
args.includes('verify') ||
args.includes('update') ||
args.includes('config') ||
args.includes('servers') ||
args.includes('usage');
if (isBasicCommand) {
return; // Skip version selection
}
// Check if user has already made a choice
const userConfig = getConfig();
const preferredMode = userConfig.getPreferredMode();
if (preferredMode && !process.argv.includes('--change-mode')) {
return; // User already has a preference
}
console.log(chalk.cyan('\nš Welcome to SSHBridge! Choose your experience:\n'));
const { mode } = await inquirer.prompt([
{
type: 'list',
name: 'mode',
message: 'How would you like to use SSHBridge?',
choices: [
{
name: 'šÆ Interactive Mode (Beginner Friendly)',
value: 'interactive',
description: 'Step-by-step guidance, menus, and smart defaults'
},
{
name: 'ā” Direct Commands (Power User)',
value: 'direct',
description: 'Traditional CLI commands with enhanced security'
}
],
default: preferredMode || 'interactive'
}
]);
// Save user preference permanently
userConfig.setPreferredMode(mode);
if (mode === 'interactive') {
console.log(chalk.green('\nā
Interactive mode enabled!'));
console.log(chalk.blue('š” Use "sshbridge setup" to get started'));
console.log(chalk.blue('š” Use "sshbridge --change-mode" to change later\n'));
// Offer to start setup wizard
const { startSetup } = await inquirer.prompt([
{
type: 'confirm',
name: 'startSetup',
message: 'Would you like to start the setup wizard now?',
default: true
}
]);
if (startSetup) {
await startSetupWizard();
process.exit(0);
}
} else if (mode === 'direct') {
console.log(chalk.green('\nā
Direct command mode enabled!'));
console.log(chalk.blue('š” Use commands like: sshbridge exec user@server "ls -la"'));
console.log(chalk.blue('š” Use "sshbridge --help" to see all commands'));
console.log(chalk.blue('š” Use "sshbridge --change-mode" to change later\n'));
}
}
// Interactive setup wizard
async function startSetupWizard() {
console.log(chalk.cyan('\nšÆ SSHBridge Setup Wizard\n'));
console.log(chalk.gray('This wizard will help you get started with SSHBridge step by step.\n'));
const { action } = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
{
name: 'š Generate SSH Key',
value: 'keygen',
description: 'Create a new SSH key pair for secure connections'
},
{
name: 'š Create Configuration',
value: 'config',
description: 'Set up server configurations for easy access'
},
{
name: 'š Register Account',
value: 'register',
description: 'Create account for advanced features and AI assistance'
},
{
name: 'š Quick Start',
value: 'quick',
description: 'Complete setup with recommended defaults'
},
{
name: 'ā Skip Setup',
value: 'skip',
description: 'Exit setup and use SSHBridge directly'
}
]
}
]);
switch (action) {
case 'keygen':
await setupSSHKey();
break;
case 'config':
await setupConfiguration();
break;
case 'register':
await setupRegistration();
break;
case 'quick':
await quickStartSetup();
break;
case 'skip':
console.log(chalk.blue('\nš” Setup skipped. You can run "sshbridge setup" anytime.\n'));
break;
}
}
// SSH Key setup
async function setupSSHKey() {
console.log(chalk.cyan('\nš SSH Key Setup\n'));
const { email } = await inquirer.prompt([
{
type: 'input',
name: 'email',
message: 'Enter your email address:',
validate: (input) => input.includes('@') || 'Please enter a valid email'
}
]);
// Check user's tier status
console.log(chalk.blue('š” Email is used for unlocking pro tier plan features'));
console.log(chalk.gray(' Free tier: 5 servers, 50 commands/month'));
console.log(chalk.gray(' Pro tier: unlimited servers and commands'));
// Show current tier status
try {
const { APIClient } = require('./api');
const api = new APIClient();
const userStatus = await api.getUserStatus(email);
if (userStatus.tier === 'pro') {
console.log(chalk.green(`ā
You are currently on the Pro tier!`));
console.log(chalk.green(` Unlimited servers and commands available`));
} else {
console.log(chalk.yellow(`š You are currently on the Free tier`));
console.log(chalk.yellow(` ${userStatus.serversUsed || 0}/5 servers, ${userStatus.commandsUsed || 0}/50 commands used this month`));
console.log(chalk.blue(` Upgrade to Pro for unlimited usage`));
}
} catch (error) {
console.log(chalk.gray(`š Checking tier status... (Free tier by default)`));
}
const { keyType } = await inquirer.prompt([
{
type: 'list',
name: 'keyType',
message: 'Choose key type:',
choices: [
{ name: 'ED25519 (Recommended)', value: 'ed25519', description: 'Most secure, fastest' },
{ name: 'RSA 4096', value: 'rsa', description: 'Widely compatible' },
{ name: 'ECDSA', value: 'ecdsa', description: 'Good balance of security and speed' }
],
default: 'ed25519'
}
]);
const { usePassphrase } = await inquirer.prompt([
{
type: 'confirm',
name: 'usePassphrase',
message: 'Would you like to encrypt your key with a passphrase?',
default: true
}
]);
// Check if key already exists before asking about force overwrite
const { SSHKeyManager } = require('./ssh-key-manager');
const keyManager = new SSHKeyManager();
const defaultKeyPath = path.join(keyManager.sshDir, `id_${keyType}`);
let forceOverwrite = false;
if (require('fs').existsSync(defaultKeyPath)) {
const { overwrite } = await inquirer.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `A key already exists at ${defaultKeyPath}. Would you like to overwrite it?`,
default: false
}
]);
forceOverwrite = overwrite;
}
console.log(chalk.blue('\nš§ Generating your SSH key...'));
try {
const result = await keyManager.generateKey({
type: keyType,
email: email,
passphrase: usePassphrase ? undefined : '', // Will prompt interactively
force: forceOverwrite
});
console.log(chalk.green('\nā
SSH key generated successfully!'));
console.log(chalk.blue('š Private key:'), result.privateKeyPath);
console.log(chalk.blue('š Public key:'), result.publicKeyPath);
// Offer to copy key to a server
const { copyToServer } = await inquirer.prompt([
{
type: 'confirm',
name: 'copyToServer',
message: 'Would you like to copy this key to a server now?',
default: true
}
]);
if (copyToServer) {
await setupKeyCopy(result.publicKeyPath);
}
} catch (error) {
console.error(chalk.red(`ā Key generation failed: ${error.message}`));
}
}
// Key copy setup
async function setupKeyCopy(keyPath) {
console.log(chalk.cyan('\nš¤ Copy Key to Server\n'));
const { server } = await inquirer.prompt([
{
type: 'input',
name: 'server',
message: 'Enter server (user@hostname):',
validate: (input) => input.includes('@') || 'Please enter format: user@hostname'
}
]);
const { authMethod } = await inquirer.prompt([
{
type: 'list',
name: 'authMethod',
message: 'How will you authenticate to copy the key?',
choices: [
{ name: 'Password', value: 'password', description: 'Enter server password' },
{ name: 'Existing SSH Key', value: 'key', description: 'Use another SSH key' }
]
}
]);
console.log(chalk.blue('\nš§ Copying key to server...'));
try {
const { SSHKeyManager } = require('./ssh-key-manager');
const keyManager = new SSHKeyManager();
const options = {};
if (authMethod === 'password') {
const { password } = await inquirer.prompt([
{
type: 'password',
name: 'password',
message: 'Enter server password:',
mask: '*'
}
]);
options.password = password;
} else {
const { existingKey } = await inquirer.prompt([
{
type: 'input',
name: 'existingKey',
message: 'Path to existing SSH key:',
default: '~/.ssh/id_rsa'
}
]);
options.key = existingKey;
}
await keyManager.copyKeyToServer(server, options);
console.log(chalk.green('\nā
Key copied successfully!'));
console.log(chalk.blue('š” You can now connect without passwords'));
} catch (error) {
console.error(chalk.red(`ā Key copy failed: ${error.message}`));
}
}
// Configuration setup
async function setupConfiguration() {
console.log(chalk.cyan('\nš Configuration Setup\n'));
const { configType } = await inquirer.prompt([
{
type: 'list',
name: 'configType',
message: 'Choose configuration format:',
choices: [
{ name: 'JSON (Recommended)', value: 'json', description: 'Easy to read and edit' },
{ name: 'YAML', value: 'yaml', description: 'Human-readable format' },
{ name: 'TOML', value: 'toml', description: 'Simple configuration format' }
],
default: 'json'
}
]);
console.log(chalk.blue('\nš§ Creating configuration file...'));
try {
const { SSHCredentialsManager } = require('./credentials');
const credManager = new SSHCredentialsManager();
const examples = credManager.generateExampleConfigs();
const fileName = `sshbridge.${configType}`;
const fs = require('fs-extra');
if (await fs.pathExists(fileName)) {
const { overwrite } = await inquirer.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `${fileName} already exists. Overwrite?`,
default: false
}
]);
if (!overwrite) return;
}
await fs.writeFile(fileName, examples[fileName]);
console.log(chalk.green(`\nā
Configuration file created: ${fileName}`));
console.log(chalk.blue('š” Edit this file with your server details'));
console.log(chalk.blue('š” Then use: sshbridge run server-name "command"'));
// Show preview
console.log(chalk.gray('\nš Preview:'));
console.log(examples[fileName]);
} catch (error) {
console.error(chalk.red(`ā Configuration creation failed: ${error.message}`));
}
}
// Registration setup
async function setupRegistration() {
console.log(chalk.cyan('\nš Account Registration\n'));
const { email } = await inquirer.prompt([
{
type: 'input',
name: 'email',
message: 'Enter your email address:',
validate: (input) => input.includes('@') || 'Please enter a valid email'
}
]);
console.log(chalk.blue('\nš§ Creating your account...'));
try {
const result = await getAPI().register(email);
if (result.needsVerification) {
console.log(chalk.yellow('\nš§ Account created! Email verification required.'));
if (result.verification_code) {
console.log(chalk.cyan('š Verification code:'), result.verification_code);
}
const { code } = await inquirer.prompt([
{
type: 'input',
name: 'code',
message: 'Enter your 6-digit verification code:',
validate: (input) => input.length === 6 && /^\d+$/.test(input) || 'Please enter a 6-digit code'
}
]);
const verifyResult = await getAPI().verifyEmail(email, code);
console.log(chalk.green('\nā
Email verified successfully!'));
console.log(chalk.blue('šÆ Free tier: 50 commands/month'));
} else {
console.log(chalk.green('\nā
Account created successfully!'));
console.log(chalk.blue('šÆ Free tier: 50 commands/month'));
}
} catch (error) {
console.error(chalk.red(`ā Registration failed: ${error.message}`));
}
}
// Quick start setup
async function quickStartSetup() {
console.log(chalk.cyan('\nš Quick Start Setup\n'));
console.log(chalk.blue('This will set up everything with recommended defaults.\n'));
try {
// 1. Generate SSH key
console.log(chalk.blue('1ļøā£ Generating SSH key...'));
const { SSHKeyManager } = require('./ssh-key-manager');
const keyManager = new SSHKeyManager();
const keyResult = await keyManager.generateKey({
type: 'ed25519',
email: 'your@email.com', // Will prompt interactively
passphrase: undefined, // Will prompt interactively
force: false
});
console.log(chalk.green('ā
SSH key generated'));
// 2. Create configuration
console.log(chalk.blue('\n2ļøā£ Creating configuration...'));
const { SSHCredentialsManager } = require('./credentials');
const credManager = new SSHCredentialsManager();
const examples = credManager.generateExampleConfigs();
const fs = require('fs-extra');
await fs.writeFile('sshbridge.json', examples['sshbridge.json']);
console.log(chalk.green('ā
Configuration created'));
// 3. Register account
console.log(chalk.blue('\n3ļøā£ Setting up account...'));
console.log(chalk.yellow('š” You can register later with: sshbridge register'));
console.log(chalk.green('\nš Quick start setup complete!'));
console.log(chalk.blue('š” Next steps:'));
console.log(chalk.white(' 1. Edit sshbridge.json with your server details'));
console.log(chalk.white(' 2. Use: sshbridge run server-name "command"'));
console.log(chalk.white(' 3. Or: sshbridge exec user@server "command" --direct'));
} catch (error) {
console.error(chalk.red(`ā Quick start failed: ${error.message}`));
}
}
// Environment variable validation
function validateEnvironment() {
const issues = [];
// Check for optional but recommended variables
if (!process.env.SSHBRIDGE_LOG_LEVEL) {
// Log level not set - using defaults
}
// Don't warn about missing API URL since we have a working fallback
// if (!process.env.SSHBRIDGE_API_URL) {
// console.log(chalk.blue('š” Tip: Set SSHBRIDGE_API_URL for custom backend'));
// }
if (issues.length > 0) {
console.log(chalk.yellow('ā ļø Environment Configuration:'));
issues.forEach(issue => console.log(chalk.yellow(` ${issue}`)));
console.log(chalk.blue('š” For full features, set recommended environment variables\n'));
}
}
// Lazy initialization functions
function getAPI() {
if (!api) {
// API will use constants file fallback URL if environment variable not set
api = new APIClient();
}
return api;
}
function getConfig() {
if (!config) {
config = new Config();
}
return config;
}
function getUpdater() {
if (!updater) {
updater = new AutoUpdater();
}
return updater;
}
function getIntegrityChecker() {
if (!integrityChecker) {
integrityChecker = new IntegrityChecker();
}
return integrityChecker;
}
function getFeatureGate() {
if (!featureGate) {
featureGate = new FeatureGate();
}
return featureGate;
}
program
.name('sshbridge')
.description('AI-Powered SSH Tool - Simple & Secure')
.version(require('../package.json').version)
.option('--change-mode', 'Change your preferred mode (interactive/direct)')
.option('--help-all', 'Show all available commands (advanced users)')
// Override the helpInformation method to show only essential commands
program.helpInformation = function() {
return chalk.cyan('\nSSHBridge\n') +
chalk.gray('Simple, secure SSH connections\n\n') +
chalk.white('š Quick Start:\n') +
' setup Set up your first connection\n' +
' connect Connect to a server\n' +
' run Execute commands\n\n' +
chalk.white('š Security:\n') +
' keygen Create secure keys\n' +
' copy-key Deploy keys to servers\n\n' +
chalk.white('š¤ Account:\n') +
' register Create account\n' +
' update Stay updated\n\n' +
chalk.gray('All commands: ') + 'sshbridge help-all\n' +
chalk.gray('Command help: ') + 'sshbridge <command> --help\n\n' +
chalk.yellow('š” Tip: Always start commands with "sshbridge" (e.g., sshbridge setup)');
};
// Override the default help command to show only essential commands
program
.command('help')
.description('Show help for commands')
.argument('[command]', 'Command to show help for')
.action((command) => {
if (command) {
// Show help for specific command
const cmd = program.commands.find(c => c.name() === command);
if (cmd) {
cmd.help();
} else {
console.log(chalk.red(`ā Unknown command: ${command}`));
console.log(chalk.blue('š” Use "sshbridge --help" to see available commands'));
}
} else {
// Show simplified help with only essential commands
console.log(chalk.cyan('\nSSHBridge\n'));
console.log(chalk.gray('Simple, secure SSH connections\n\n'));
console.log(chalk.white('š Quick Start:\n'));
console.log(' setup Set up your first connection');
console.log(' connect Connect to a server');
console.log(' run Execute commands\n');
console.log(chalk.white('š Security:\n'));
console.log(' keygen Create secure keys');
console.log(' copy-key Deploy keys to servers\n');
console.log(chalk.white('š¤ Account:\n'));
console.log(' register Create account');
console.log(' update Stay updated\n');
console.log(chalk.gray('All commands: sshbridge help-all'));
console.log(chalk.yellow('\nš” Tip: Always start commands with "sshbridge" (e.g., sshbridge setup)'));
}
});
// Add a custom help-all command that shows all commands
program
.command('help-all')
.description('Show all available commands (advanced users)')
.action(() => {
console.log(chalk.cyan('\nSSHBridge - All Commands\n'));
console.log(chalk.gray('For beginners, use: sshbridge help-simple\n'));
console.log(chalk.white('Connection & Execution:\n'));
const connectionCommands = [
{ name: 'setup', desc: 'Interactive setup wizard' },
{ name: 'connect', desc: 'Connect to a server' },
{ name: 'exec', desc: 'Execute single command' },
{ name: 'run', desc: 'Run command on configured host' },
{ name: 'session', desc: 'Interactive session' },
{ name: 'copy', desc: 'Transfer files' }
];
connectionCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(15))} ${cmd.desc}`);
});
console.log(chalk.white('\nSecurity & Keys:\n'));
const securityCommands = [
{ name: 'keygen', desc: 'Generate SSH keys' },
{ name: 'copy-key', desc: 'Deploy keys to servers' },
{ name: 'keys', desc: 'Manage your keys' },
{ name: 'security', desc: 'Security status' }
];
securityCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(15))} ${cmd.desc}`);
});
console.log(chalk.white('\nConfiguration:\n'));
const configCommands = [
{ name: 'config', desc: 'Manage configurations' },
{ name: 'servers', desc: 'Manage server list' },
{ name: 'change-mode', desc: 'Switch between modes' }
];
configCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(15))} ${cmd.desc}`);
});
console.log(chalk.white('\nAccount & Updates:\n'));
const accountCommands = [
{ name: 'register', desc: 'Create account' },
{ name: 'verify', desc: 'Verify email' },
{ name: 'update', desc: 'Check for updates' },
{ name: 'usage', desc: 'View usage stats' }
];
accountCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(15))} ${cmd.desc}`);
});
console.log(chalk.white('\nHelp & Troubleshooting:\n'));
const helpCommands = [
{ name: 'help-simple', desc: 'Beginner help' },
{ name: 'examples', desc: 'Usage examples' },
{ name: 'troubleshoot', desc: 'Common issues' },
{ name: 'install-help', desc: 'Installation help' }
];
helpCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(15))} ${cmd.desc}`);
});
console.log(chalk.gray('\nFor simple help: sshbridge help-simple'));
});
// Custom help command for simplified view
program
.command('help-simple')
.alias('hs')
.description('Show simplified help (beginner friendly)')
.action(() => {
console.log(chalk.cyan('\nSSHBridge\n'));
console.log(chalk.gray('Simple, secure SSH connections\n\n'));
console.log(chalk.white('š Quick Start:\n'));
console.log(' sshbridge setup # First-time setup');
console.log(' sshbridge connect # Connect to a server');
console.log(' sshbridge run # Execute commands\n');
console.log(chalk.white('š Security:\n'));
console.log(' sshbridge keygen # Create secure keys');
console.log(' sshbridge copy-key # Deploy keys to servers\n');
console.log(chalk.white('š¤ Account:\n'));
console.log(' sshbridge register # Create account');
console.log(' sshbridge update # Stay updated\n');
console.log(chalk.gray('\nNeed more? sshbridge help-all'));
console.log(chalk.yellow('\nš” Tip: Always start commands with "sshbridge" (e.g., sshbridge setup)'));
});
// Command to list all available commands
program
.command('list-all')
.alias('la')
.description('Show all available commands (advanced users)')
.action(() => {
console.log(chalk.cyan('\nš SSHBridge - All Commands (Advanced Users)\n'));
console.log(chalk.yellow('ā ļø This shows all 30+ commands. For beginners, use: sshbridge help-simple\n'));
const allCommands = [
{ name: 'setup', desc: 'Interactive setup wizard for new users' },
{ name: 'interactive|i', desc: 'Enter interactive mode for guided SSH operations' },
{ name: 'change-mode', desc: 'Change your preferred SSHBridge experience mode' },
{ name: 'quick-start|qs', desc: 'Quick start guide for new users' },
{ name: 'examples|ex', desc: 'Show usage examples' },
{ name: 'register', desc: 'Register for a free account' },
{ name: 'verify', desc: 'Verify your email address with SSHBridge' },
{ name: 'exec', desc: 'Execute SSH command on remote server' },
{ name: 'copy', desc: 'Copy files to/from remote server' },
{ name: 'usage', desc: 'Check your current usage' },
{ name: 'servers', desc: 'Manage your servers' },
{ name: 'update|u', desc: 'Check for and install updates' },
{ name: 'config', desc: 'Manage SSH configuration files' },
{ name: 'keygen', desc: 'Generate SSH key pairs' },
{ name: 'copy-key', desc: 'Copy SSH public key to remote server' },
{ name: 'keys', desc: 'Manage SSH keys' },
{ name: 'run', desc: 'Execute command on configured host (uses config file)' },
{ name: 'session', desc: 'Start an interactive SSH session' },
{ name: 'license|lic', desc: 'Manage SSHBridge license and features' },
{ name: 'security|sec', desc: 'Show security and integrity status' },
{ name: 'install-help', desc: 'Show installation troubleshooting' },
{ name: 'sandbox', desc: 'Show sandbox status and statistics' },
{ name: 'troubleshoot|ts', desc: 'Troubleshooting guide for common issues' },
{ name: 'rollback|rb', desc: 'Rollback to previous version' },
{ name: 'upgrade', desc: 'Quick upgrade (same as update)' },
{ name: 'clear-passwords', desc: 'Clear cached SSH passwords' },
{ name: 'help-simple|hs', desc: 'Show simplified help (beginner friendly)' },
{ name: 'list-all|la', desc: 'Show all available commands (advanced users)' }
];
allCommands.forEach(cmd => {
console.log(` ${chalk.blue(cmd.name.padEnd(20))} ${cmd.desc}`);
});
console.log(chalk.gray('\nš” Simple help: sshbridge help-simple'));
console.log(chalk.gray('š” Command help: sshbridge <command> --help'));
});
// Installation help command
// License management command
program
.command('license')
.alias('lic')
.description('Manage SSHBridge license and features')
.option('-s, --status', 'Show license status')
.option('-f, --features', 'Show available features')
.option('-u, --upgrade', 'Show upgrade information')
.action(async (options) => {
const featureGate = getFeatureGate();
if (options.status) {
const status = featureGate.getLicenseStatus();
console.log(chalk.cyan('\nš SSHBridge License Status\n'));
console.log(chalk.yellow('š License Information:'));
console.log(` Tier: ${chalk.green(status.tier)}`);
console.log(` Status: ${chalk.green(status.status)}`);
if (status.customerId) {
console.log(` Customer ID: ${chalk.green(status.customerId)}`);
console.log(` Issued: ${chalk.green(new Date(status.issuedAt).toLocaleDateString())}`);
if (status.expiresAt) {
console.log(` Expires: ${chalk.green(new Date(status.expiresAt).toLocaleDateString())}`);
}
}
console.log(chalk.yellow('\nš Usage Quota:'));
console.log(` Commands Used: ${chalk.blue(status.quota.commands)}`);
console.log(` Commands Limit: ${chalk.blue(status.quota.maxCommands)}`);
if (status.quota.resetDate) {
console.log(` Resets: ${chalk.blue(new Date(status.quota.resetDate).toLocaleDateString())}`);
}
console.log(chalk.yellow('\nš Available Features:'));
status.features.forEach(feature => {
const statusIcon = feature.enabled ? 'ā
' : 'ā';
console.log(` ${statusIcon} ${chalk.green(feature.name)}: ${feature.description}`);
});
} else if (options.features) {
const features = featureGate.getAvailableFeatures();
console.log(chalk.cyan('\nš Available Features\n'));
features.forEach(feature => {
console.log(`ā
${chalk.green(feature.name)}: ${feature.description}`);
});
} else if (options.upgrade) {
const upgradeInfo = featureGate.getUpgradeInfo();
if (!upgradeInfo) {
console.log(chalk.green('\nš You already have the highest tier!'));
return;
}
console.log(chalk.cyan('\nš Upgrade Information\n'));
console.log(chalk.yellow(`Current Tier: ${chalk.blue(upgradeInfo.currentTier)}`));
console.log(chalk.yellow(`Upgrade To: ${chalk.green(upgradeInfo.nextTier)}`));
const pricing = upgradeInfo.pricing;
console.log(chalk.yellow('\nš° Pricing:'));
console.log(` Monthly: $${chalk.green(pricing.monthly)}`);
console.log(` Yearly: $${chalk.green(pricing.yearly)} (Save 17%)`);
console.log(chalk.yellow('\nš New Features:'));
upgradeInfo.features.forEach(feature => {
console.log(` ā
${chalk.green(feature.name)}: ${feature.description}`);
});
} else {
// Show help for license command
console.log(chalk.cyan('\nš SSHBridge License Management\n'));
console.log(chalk.yellow('Available Options:'));
console.log(' --status, -s Show license status and features');
console.log(' --features, -f Show available features');
console.log(' --upgrade, -u Show upgrade information');
console.log('\nExamples:');
console.log(' sshbridge license --status');
console.log(' sshbridge license --features');
console.log(' sshbridge license --upgrade');
}
});
// Security status command
program
.command('security')
.alias('sec')
.description('Show security and integrity status')
.action(() => {
const integrityChecker = getIntegrityChecker();
const featureGate = getFeatureGate();
console.log(chalk.cyan('\nš SSHBridge Security Status\n'));
// Integrity checker status
const integrityStatus = integrityChecker.getSecurityStatus();
console.log(chalk.yellow('š”ļø Integrity Protection:'));
console.log(` Status: ${chalk.green('Active')}`);
console.log(` Checks Performed: ${chalk.blue(integrityStatus.integrityChecks)}`);
console.log(` Last Check: ${chalk.blue(new Date(integrityStatus.lastCheck).toLocaleTimeString())}`);
console.log(` Checksums Verified: ${chalk.green(integrityStatus.checksumsVerified ? 'Yes' : 'No')}`);
// Feature gate status
const licenseStatus = featureGate.getLicenseStatus();
console.log(chalk.yellow('\nš License Security:'));
console.log(` Tier: ${chalk.green(licenseStatus.tier)}`);
console.log(` Status: ${chalk.green(licenseStatus.status)}`);
if (licenseStatus.tier === 'free') {
console.log(chalk.blue('\nš” Upgrade to Pro or Enterprise for advanced security features'));
}
console.log(chalk.yellow('\nšØ Security Features:'));
console.log(' ā
Code integrity verification');
console.log(' ā
Runtime tampering detection');
console.log(' ā
Performance monitoring');
if (licenseStatus.tier !== 'free') {
console.log(' ā
Advanced security features');
console.log(' ā
Enterprise integrations');
console.log(' ā
Audit logging');
}
});
// Installation help command
program
.command('install-help')
.description('Show installation troubleshooting')
.action(() => {
console.log(chalk.cyan('\nš§ SSHBridge Installation Help\n'));
console.log(chalk.yellow('š¦ Installation Command:'));
console.log(chalk.green('sudo npm install -g ssh-bridge-ai\n'));
console.log(chalk.yellow('ā Why sudo?'));
console.log('⢠Global npm packages need admin permissions');
console.log('⢠This installs to /usr/local/lib/node_modules/');
console.log('⢠Same requirement as other CLI tools\n');
console.log(chalk.yellow('šØ Permission Error (EACCES)?'));
console.log('⢠Run: ' + chalk.green('sudo npm install -g ssh-bridge-ai'));
console.log('⢠Enter your macOS/Linux password when prompted');
console.log('⢠This is normal and safe for npm global installs\n');
console.log(chalk.yellow('šŖ Windows Users:'));
console.log('⢠Run Command Prompt as Administrator');
console.log('⢠Then: ' + chalk.green('npm install -g ssh-bridge-ai'));
console.log('⢠No sudo needed on Windows\n');
console.log(chalk.yellow('ā
After Installation:'));
console.log('⢠Run: ' + chalk.green('sshbridge register --email your@email.com'));
console.log('⢠Get 50 free commands per month');
console.log('⢠Start automating with AI!\n');
console.log(chalk.gray('š” Still having issues? Create an issue:'));
console.log(chalk.blue('https://github.com/A12-AA/sshbridge/issues'));
});
// Setup wizard command
program
.command('setup')
.description('Interactive setup wizard for new users')
.action(async () => {
await startSetupWizard();
});
// Interactive mode command
program
.command('interactive')
.alias('i')
.description('Enter interactive mode for guided SSH operations')
.action(async () => {
console.log(chalk.cyan('\nšÆ SSHBridge Interactive Mode\n'));
console.log(chalk.blue('Welcome to guided SSH operations!\n'));
const { action } = await inquirer.prompt([
{
type: 'list',
name: 'action',
message: 'What would you like to do?',
choices: [
{
name: 'š Manage SSH Keys',
value: 'keys',
description: 'Generate, copy, and manage SSH keys'
},
{
name: 'š„ļø Connect to Server',
value: 'connect',
description: 'Connect to a server with guided setup'
},
{
name: 'š Manage Configurations',
value: 'config',
description: 'Create and edit server configurations'
},
{
name: 'š Account Management',
value: 'account',
description: 'Register, verify, and manage your account'
},
{
name: 'ā Exit Interactive Mode',
value: 'exit',
description: 'Return to command line'
}
]
}
]);
switch (action) {
case 'keys':
await interactiveKeyManagement();
break;
case 'connect':
await interactiveConnection();
break;
case 'config':
await interactiveConfigManagement();
break;
case 'account':
await interactiveAccountManagement();
break;
case 'exit':
console.log(chalk.blue('\nš Exiting interactive mode. Use "sshbridge interactive" to return.\n'));
break;
}
});
// Change mode command
program
.command('change-mode')
.description('Change your preferred SSHBridge experience mode')
.action(async () => {
console.log(chalk.cyan('\nš Change SSHBridge Experience Mode\n'));
const userConfig = getConfig();
const currentMode = userConfig.getPreferredMode() || 'interactive';
console.log(chalk.blue(`Current mode: ${currentMode === 'interactive' ? 'šÆ Interactive' : 'ā” Direct Commands'}\n`));
const { mode } = await inquirer.prompt([
{
type: 'list',
name: 'mode',
message: 'How would you like to use SSHBridge?',
choices: [
{
name: 'šÆ Interactive Mode (Beginner Friendly)',
value: 'interactive',
description: 'Step-by-step guidance, menus, and smart defaults'
},
{
name: 'ā” Direct Commands (Power User)',
value: 'direct',
description: 'Traditional CLI commands with enhanced security'
}
],
default: currentMode
}
]);
userConfig.setPreferredMode(mode);
if (mode === 'interactive') {
console.log(chalk.green('\nā
Interactive mode enabled!'));
console.log(chalk.blue('š” Use "sshbridge interactive" to enter guided mode'));
console.log(chalk.blue('š” Use "sshbridge setup" for initial setup'));
} else {
console.log(chalk.green('\nā
Direct command mode enabled!'));
console.log(chalk.blue('š” Use commands like: sshbridge exec user@server "ls -la"'));
console.log(chalk.blue('š” Use "sshbridge --help" to see all commands'));
}
console.log(chalk.blue('\nš” Your preference has been saved and will be used automatically'));
});
// Quick start command
program
.command('quick-start')
.alias('qs')
.description('Quick start guide for new users')
.action(() => {
console.log(chalk.cyan('\nš SSHBridge Quick Start Guide\n'));
console.log(chalk.blue('š” For interactive setup, use: sshbridge setup\n'));
console.log(chalk.yellow('1ļøā£ SSH Key Setup (Recommended):'));
console.log(chalk.white(' sshbridge keygen --type ed25519 --email "your@email.com"'));
console.log(chalk.gray(' š” Will prompt for secure passphrase with verification'));
console.log(chalk.white(' sshbridge copy-key user@server'));
console.log(chalk.white(' sshbridge keys list'));
console.log(chalk.gray(' š” Keys provide secure, passwordless access\n'));
console.log(chalk.yellow('2ļøā£ Direct SSH (No Registration Required):'));
console.log(chalk.white(' sshbridge exec user@hostname "ls -la" --direct'));
console.log(chalk.white(' sshbridge exec user@hostname "whoami" --ask-pass'));
console.log(chalk.gray(' š” Use --direct for immediate SSH access\n'));
console.log(chalk.yellow('3ļøā£ Create Configuration File:'));
console.log(chalk.white(' sshbridge config --create json'));
console.log(chalk.white(' # Edit sshbridge.json with your servers\n'));
console.log(chalk.yellow('4ļøā£ Use Configuration:'));
console.log(chalk.white(' sshbridge run server-name "command"'));
console.log(chalk.white(' sshbridge run server-name "command" --direct\n'));
console.log(chalk.yellow('5ļøā£ Register for AI Features (Optional):'));
console.log(chalk.white(' sshbridge register --email your@email.com'));
console.log(chalk.gray(' š” Get 50 free AI-powered commands per month\n'));
console.log(chalk.blue('š More help: sshbridge --help'));
console.log(chalk.blue('š§ Config help: sshbridge config --help'));
console.log(chalk.blue('š Key help: sshbridge keygen --help'));
});
// Examples command
program
.command('examples')
.alias('ex')
.description('Show usage examples')
.action(() => {
console.log(chalk.cyan('\nš Usage Examples (Simple & Professional)\n'));
console.log(chalk.yellow('š SSH Key Management:'));
console.log(chalk.white(' # Generate new key (interactive passphrase)'));
console.log(chalk.gray(' sshbridge keygen --type ed25519 --email "your@email.com"'));
console.log(chalk.white(' # Generate RSA key with custom bits'));
console.log(chalk.gray(' sshbridge keygen --type rsa --bits 4096 --force'));
console.log(chalk.white(' # Deploy key to server'));
console.log(chalk.gray(' sshbridge copy-key user@server'));
console.log(chalk.gray(' sshbridge copy-key server-name --key ~/.ssh/id_ed25519'));
console.log(chalk.white(' # List and manage keys'));
console.log(chalk.gray(' sshbridge keys list'));
console.log(chalk.gray(' sshbridge keys fingerprint ~/.ssh/id_ed25519\n'));
console.log(chalk.yellow('š Direct SSH Connections:'));
console.log(chalk.white(' # Basic command execution'));
console.log(chalk.gray(' sshbridge exec user@server "ls -la" --direct'));
console.log(chalk.gray(' sshbridge exec admin@prod "uptime" --key ~/.ssh/id_rsa'));
console.log(chalk.gray(' sshbridge exec user@host "df -h" --ask-pass'));
console.log(chalk.gray(' sshbridge exec user@host "ls -la" --key ~/.ssh/id_ed25519 --ask-key-pass\n'));
console.log(chalk.yellow('š Configuration-Based Usage:'));
console.log(chalk.white(' # Create config file'));
console.log(chalk.gray(' sshbridge config --create json'));
console.log(chalk.white(' # Use with config'));
console.log(chalk.gray(' sshbridge run prod-server "docker ps"'));
console.log(chalk.gray(' sshbridge run staging "git pull" --direct\n'));
console.log(chalk.yellow('š§ Configuration Management:'));
console.log(chalk.white(' # List configured hosts'));
console.log(chalk.gray(' sshbridge config --list sshbridge.json'));
console.log(chalk.white(' # Test config file'));
console.log(chalk.gray(' sshbridge config --test myconfig.yaml'));
console.log(chalk.white(' # Show config locations'));
console.log(chalk.gray(' sshbridge config --show-path\n'));
console.log(chalk.yellow('š Account Management:'));
console.log(chalk.white(' # Check usage'));
console.log(chalk.gray(' sshbridge usage'));
console.log(chalk.white(' # List servers'));
console.log(chalk.gray(' sshbridge servers list'));
console.log(chalk.white(' # Update tool'));
console.log(chalk.gray(' sshbridge update\n'));
console.log(chalk.blue('š” For more help: sshbridge <command> --help'));
});
// Sandbox status command
program
.command('sandbox')
.description('Show sandbox status and statistics')
.option('--stats', 'Show detailed statistics')
.option('--clear', 'Clear sandbox execution history')
.action(async (options) => {
try {
if (options.clear) {
// This would clear sandbox history if we had direct access
console.log(chalk.blue('šļø Sandbox history cleared'));
return;
}
console.log(chalk.cyan('\nš SSHBridge Sandbox Status\n'));
if (options.stats) {
console.log(chalk.yellow('š Sandbox Statistics:'));
console.log(chalk.white(' Status: Enabled (default)'));
console.log(chalk.white(' Mode: Docker containers (with local fallback)'));
console.log(chalk.white(' Image: ubuntu:22.04'));
console.log(chalk.white(' Timeout: 30 seconds'));
console.log(chalk.white(' Memory limit: 512MB per container'));
console.log(chalk.white(' Disk limit: 100MB per container\n'));
console.log(chalk.yellow('š”ļø Safety Features:'));
console.log(chalk.white(' ⢠All commands tested in sandbox first'));
console.log(chalk.white(' ⢠Dangerous patterns automatically blocked'));
console.log(chalk.white(' ⢠Command output size limits'));
console.log(chalk.white(' ⢠Automatic cleanup after execution\n'));
console.log(chalk.yellow('š§ Usage Examples:'));
console.log(chalk.white(' # Test command in sandbox only'));
console.log(chalk.gray(' sshbridge exec user@host "ls -la" --dry-run'));
console.log(chalk.white(' # Always require confirmation (default)'));
console.log(chalk.gray(' sshbridge exec user@host "command" --confirm'));
console.log(chalk.white(' # Skip confirmation (dangerous!)'));
console.log(chalk.gray(' sshbridge exec user@host "command" --auto-approve'));
console.log(chalk.white(' # Disable sandbox (not recommended)'));
console.log(chalk.gray(' sshbridge exec user@host "command" --no-sandbox'));
console.log(chalk.white(' # Use custom sandbox image'));
console.log(chalk.gray(' sshbridge exec user@host "command" --sandbox-image centos:8\n'));
} else {
console.log(chalk.green('ā
Sandbox mode is enabled by default'));
console.log(chalk.white('All commands are tested in isolated containers before execution'));
console.log(chalk.blue('š” Use --stats for detailed information'));
console.log(chalk.blue('š” Use --dry-run to test commands safely'));
}
} catch (error) {
console.error(chalk.red(`ā Error: ${error.message}`));
process.exit(1);
}
});
// Troubleshoot command
program
.command('troubleshoot')
.alias('ts')
.description('Troubleshooting guide for common issues')
.action(() => {
console.log(chalk.cyan('\nš§ SSHBridge Troubleshooting Guide\n'));
console.log(chalk.yellow('ā "Not authenticated" Error:'));
console.log(chalk.white(' Solution: Use --direct flag for direct SSH'));
console.log(chalk.gray(' sshbridge exec user@host "command" --direct'));
console.log(chalk.gray(' Or register: sshbridge register --email your@email.com\n'));
console.log(chalk.yellow('ā "Host not found in config" Error:'));
console.log(chalk.white(' Solution: Create or check your config file'));
console.log(chalk.gray(' sshbridge config --create json'));
console.log(chalk.gray(' sshbridge config --list sshbridge.json\n'));
console.log(chalk.yellow('ā "Authentication failed" Error:'));
console.log(chalk.white(' Solution: Check credentials or use --ask-pass'));
console.log(chalk.gray(' sshbridge exec user@host "command" --ask-pass'));
console.log(chalk.gray(' Or specify key: --key ~/.ssh/id_rsa\n'));
console.log(chalk.yellow('ā "Config file not found" Error:'));
console.log(chalk.white(' Solution: Check file location or create new one'));
console.log(chalk.gray(' sshbridge config --show-path'));
console.log(chalk.gray(' sshbridge config --create json\n'));
console.log(chalk.yellow('ā "Permission denied" Error:'));
console.log(chalk.white(' Solution: Check SSH key permissions'));
console.log(chalk.gray(' chmod 600 ~/.ssh/id_rsa'));
console.log(chalk.gray(' Or use password: --ask-pass\n'));
console.log(chalk.yellow('ā "SSH key not found" Error:'));
console.log(chalk.white(' Solution: Generate new SSH key'));
console.log(chalk.gray(' sshbridge keygen --type ed25519'));
console.log(chalk.gray(' sshbridge copy-key user@server\n'));
console.log(chalk.yellow('ā "Key copy failed" Error:'));
console.log(chalk.white(' Solution: Check server connectivity and permissions'));
console.log(chalk.gray(' sshbridge copy-key user@server --key ~/.ssh/id_ed25519'));
console.log(chalk.gray(' Or manually: ssh-copy-id -i ~/.ssh/id_ed25519 user@server\n'));
console.log(chalk.blue('š More help: sshbridge quick-start'));
console.log(chalk.blue('š§ Config help: sshbridge config --help'));
});
// Register command
program
.command('register')
.description('Register for a free account')
.option('-e, --email <email>', 'Your email address')
.action(async (options) => {
try {
const spinner = ora('Creating account...').start();
let email = options.email;
if (!email) {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'email',
message: 'Enter your email:',
validate: (input) => input.includes('@') || 'Please enter a valid email'
}
]);
email = answers.email;
}
const result = await getAPI().register(email);
spinner.stop();
if (result.needsVerification) {
console.log(chalk.yellow('š§ Account created! Email verification required.'));
if (result.note) console.log(chalk.gray('Note:'), result.note);
// Store the API key from registration (before verification)
if (result.api_key) {
getConfig().setApiKey(result.api_key);
getConfig().setEmail(email);
}
// For testing - show the code
if (result.verification_code) {
console.log(chalk.cyan('š Verification code:'), result.verification_code);
}
// Prompt for verification code
const verifyAnswers = await inquirer.prompt([
{
type: 'input',
name: 'code',
message: 'Enter your 6-digit verification code:',
validate: (input) => {
return input.length === 6 && /^\d+$/.test(input) || 'Please enter a 6-digit code';
}
}
]);
const verifySpinner = ora('Verifying email...').start();
try {
const verifyResult = await getAPI().verifyEmail(email, verifyAnswers.code);
verifySpinner.stop();
console.log(chalk.green('ā Email verified successfully!'));
console.log(chalk.blue(`⨠Welcome to SSHBridge v1.1.0!`));
console.log(chalk.gray(`š§ Email: ${email}`));
console.log(chalk.gray(`šÆ Free tier: 50 commands/month`));
console.log(chalk.gray(`š Homepage: https://dulcet-gecko-595d55.netlify.app`));
console.log(chalk.yellow(`\nš” Try: sshbridge exec user@server "ls -la"`));
} catch (verifyError) {
verifySpinner.stop();
console.error(chalk.red('ā Verification failed:'),