UNPKG

@light-merlin-dark/vssh

Version:

MCP-native SSH proxy for AI agents. CLI & MCP Server, plugin system, AI safety guards.

167 lines • 6.84 kB
"use strict"; 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.CONFIG_PATH = exports.LOGS_PATH = exports.DATA_PATH = exports.PROJECT_PATH = void 0; exports.saveConfig = saveConfig; exports.loadConfig = loadConfig; exports.setupInteractiveConfig = setupInteractiveConfig; const zod_1 = require("zod"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const readline = __importStar(require("readline")); const crypto = __importStar(require("crypto")); // Dynamic paths based on user's home directory exports.PROJECT_PATH = path.join(os.homedir(), '.vssh'); exports.DATA_PATH = path.join(exports.PROJECT_PATH, 'data'); exports.LOGS_PATH = path.join(exports.DATA_PATH, 'logs'); exports.CONFIG_PATH = path.join(exports.PROJECT_PATH, 'config.json'); const envSchema = zod_1.z.object({ VSSH_HOST: zod_1.z.string().optional(), VSSH_USER: zod_1.z.string().optional().default('root'), VSSH_KEY_PATH: zod_1.z.string().optional().default(`${process.env.HOME}/.ssh/id_rsa`) }); const configSchema = zod_1.z.object({ host: zod_1.z.string(), user: zod_1.z.string(), keyPath: zod_1.z.string(), localMode: zod_1.z.boolean().optional().default(false), encryptionKey: zod_1.z.string().optional(), plugins: zod_1.z.object({ enabled: zod_1.z.array(zod_1.z.string()).optional(), disabled: zod_1.z.array(zod_1.z.string()).optional(), config: zod_1.z.record(zod_1.z.any()).optional() }).optional() }); function saveConfig(config) { fs.mkdirSync(exports.PROJECT_PATH, { recursive: true }); fs.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2)); } function loadConfig() { // Ensure data directories exist fs.mkdirSync(exports.LOGS_PATH, { recursive: true }); // First try to load from saved config file if (fs.existsSync(exports.CONFIG_PATH)) { try { const savedConfig = JSON.parse(fs.readFileSync(exports.CONFIG_PATH, 'utf-8')); return configSchema.parse(savedConfig); } catch (error) { console.error('āš ļø Warning: Failed to parse saved config:', error); } } // Fall back to environment variables const envConfig = envSchema.safeParse({ VSSH_HOST: process.env.VSSH_HOST || process.env.SSH_HOST, VSSH_USER: process.env.VSSH_USER, VSSH_KEY_PATH: process.env.VSSH_KEY_PATH }); if (envConfig.success && envConfig.data.VSSH_HOST) { return { host: envConfig.data.VSSH_HOST, user: envConfig.data.VSSH_USER, keyPath: envConfig.data.VSSH_KEY_PATH, plugins: { enabled: ['file-transfer', 'docker', 'coolify'], // Default enabled plugins disabled: ['grafana'] // Grafana requires manual configuration } }; } // No config found return null; } async function setupInteractiveConfig() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const question = (prompt) => { return new Promise((resolve) => { rl.question(prompt, resolve); }); }; console.log('\nšŸ”§ First-time setup for vssh\n'); console.log('vssh is an AI-friendly SSH command proxy that helps AI assistants'); console.log('safely execute commands on remote servers with built-in safety guards.\n'); // Get SSH host const host = await question('SSH host (e.g., example.com or 192.168.1.100): '); if (!host) { console.error('\nāŒ Host is required'); process.exit(1); } // Get SSH user const user = await question(`SSH user [${process.env.USER || 'root'}]: `) || process.env.USER || 'root'; // Detect SSH keys const defaultKeyPath = path.join(os.homedir(), '.ssh', 'id_rsa'); const alternativeKeys = ['id_ed25519', 'id_ecdsa', 'id_dsa']; let suggestedKey = defaultKeyPath; if (!fs.existsSync(defaultKeyPath)) { for (const keyName of alternativeKeys) { const keyPath = path.join(os.homedir(), '.ssh', keyName); if (fs.existsSync(keyPath)) { suggestedKey = keyPath; break; } } } const keyPath = await question(`SSH private key path [${suggestedKey}]: `) || suggestedKey; if (!fs.existsSync(keyPath)) { console.error(`\nāŒ SSH key not found at: ${keyPath}`); console.error('Please ensure you have an SSH key set up.'); process.exit(1); } rl.close(); // Generate encryption key const encryptionKey = crypto.randomBytes(32).toString('base64'); const config = { host, user, keyPath, encryptionKey, plugins: { enabled: ['file-transfer', 'docker', 'coolify'], // Default enabled plugins disabled: ['grafana'] // Grafana requires manual configuration } }; // Save config fs.mkdirSync(exports.PROJECT_PATH, { recursive: true }); fs.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2)); console.log(`\nāœ… Configuration saved to: ${exports.CONFIG_PATH}`); console.log('šŸ” Encryption key generated for secure credential storage'); console.log('\nYou can now use vssh to execute commands on your remote server.'); console.log('Example: vssh ls -la\n'); return config; } //# sourceMappingURL=config.js.map