UNPKG

lsp-gateway

Version:

Local LSP gateway for development - HTTP JSON-RPC and MCP server for multiple languages

352 lines (301 loc) 8.66 kB
const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); const PlatformInfo = require('./platform'); const Installer = require('./installer'); /** * LSP Gateway NPM Package * Main entry point for programmatic access to LSP Gateway */ class LSPGateway { constructor(options = {}) { this.platform = new PlatformInfo(); this.options = { port: options.port || 8080, config: options.config || path.join(__dirname, '..', 'config.yaml'), ...options }; this.process = null; this.isRunning = false; } /** * Get the path to the binary * @returns {string} Binary path */ getBinaryPath() { return this.platform.getBinaryPath(); } /** * Check if the binary exists and is executable * @returns {Promise<boolean>} True if binary is ready */ async isBinaryReady() { const binaryPath = this.getBinaryPath(); if (!fs.existsSync(binaryPath)) { return false; } // Test if binary works return new Promise((resolve) => { const child = spawn(binaryPath, ['version'], { stdio: 'pipe', timeout: 5000 }); child.on('close', (code) => { resolve(code === 0); }); child.on('error', () => { resolve(false); }); }); } /** * Install the binary if it's not available * @returns {Promise<void>} */ async ensureBinary() { if (!(await this.isBinaryReady())) { console.log('🔧 Binary not found, installing...'); const installer = new Installer(); await installer.install(); } } /** * Start the LSP Gateway server * @param {Object} options - Server options * @returns {Promise<void>} */ async start(options = {}) { if (this.isRunning) { throw new Error('LSP Gateway is already running'); } await this.ensureBinary(); const binaryPath = this.getBinaryPath(); const args = ['server']; // Add configuration file if (options.config || this.options.config) { args.push('--config', options.config || this.options.config); } // Add port if (options.port || this.options.port) { args.push('--port', (options.port || this.options.port).toString()); } console.log(`🚀 Starting LSP Gateway on port ${options.port || this.options.port}...`); return new Promise((resolve, reject) => { this.process = spawn(binaryPath, args, { stdio: 'inherit' }); this.process.on('spawn', () => { this.isRunning = true; console.log('✅ LSP Gateway started successfully'); resolve(); }); this.process.on('error', (error) => { this.isRunning = false; reject(new Error(`Failed to start LSP Gateway: ${error.message}`)); }); this.process.on('close', (code) => { this.isRunning = false; if (code !== 0) { console.log(`⚠️ LSP Gateway exited with code ${code}`); } }); }); } /** * Stop the LSP Gateway server * @returns {Promise<void>} */ async stop() { if (!this.isRunning || !this.process) { return; } return new Promise((resolve) => { this.process.on('close', () => { this.isRunning = false; this.process = null; console.log('🛑 LSP Gateway stopped'); resolve(); }); // Send SIGTERM this.process.kill('SIGTERM'); // Force kill after 5 seconds setTimeout(() => { if (this.process) { this.process.kill('SIGKILL'); } }, 5000); }); } /** * Get the status of the LSP Gateway * @returns {Object} Status information */ getStatus() { return { isRunning: this.isRunning, port: this.options.port, config: this.options.config, platform: this.platform.getPlatformName(), binaryPath: this.getBinaryPath() }; } /** * Get platform information * @returns {Object} Platform information */ getPlatformInfo() { return { platform: this.platform.getCurrentPlatformString(), name: this.platform.getPlatformName(), supported: this.platform.isSupported(), binaryPath: this.getBinaryPath() }; } /** * Get version information * @returns {Object} Version information */ getVersionInfo() { const packageJsonPath = path.join(__dirname, '..', 'package.json'); let version = 'unknown'; try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); version = packageJson.version || 'unknown'; } catch (error) { console.warn('Failed to read version from package.json:', error.message); } return { version, packagePath: packageJsonPath }; } /** * Get binary version by executing the version command * @returns {Promise<string>} Binary version */ async getBinaryVersion() { const binaryPath = this.getBinaryPath(); if (!fs.existsSync(binaryPath)) { throw new Error('Binary not found'); } return new Promise((resolve, reject) => { const child = spawn(binaryPath, ['version'], { stdio: 'pipe', timeout: 10000 }); let output = ''; child.stdout.on('data', (data) => { output += data.toString(); }); child.on('close', (code) => { if (code === 0) { resolve(output.trim()); } else { reject(new Error('Version command failed')); } }); child.on('error', (error) => { reject(new Error(`Failed to get binary version: ${error.message}`)); }); }); } /** * Create a default configuration file * @param {string} filePath - Path where to create the config file * @returns {Promise<void>} */ async createDefaultConfig(filePath) { const defaultConfig = `port: 8080 servers: - name: "go-lsp" languages: ["go"] command: "gopls" args: [] transport: "stdio" - name: "python-lsp" languages: ["python"] command: "jedi-language-server" args: [] transport: "stdio" - name: "typescript-lsp" languages: ["typescript", "javascript"] command: "typescript-language-server" args: ["--stdio"] transport: "stdio" - name: "java-lsp" languages: ["java"] command: "jdtls" args: [] transport: "stdio" `; return new Promise((resolve, reject) => { fs.writeFile(filePath, defaultConfig, 'utf8', (err) => { if (err) { reject(new Error(`Failed to create config file: ${err.message}`)); } else { console.log(`✅ Default configuration created at: ${filePath}`); resolve(); } }); }); } } // Export the main class and utilities module.exports = { LSPGateway, PlatformInfo, Installer, // Convenience methods async createGateway(options) { return new LSPGateway(options); }, async getPlatformInfo() { const platform = new PlatformInfo(); return platform.getPlatformInfo(); }, async installBinary() { const installer = new Installer(); return installer.install(); }, async getVersion() { const gateway = new LSPGateway(); return gateway.getVersionInfo(); }, async getBinaryVersion() { const gateway = new LSPGateway(); return gateway.getBinaryVersion(); } }; // CLI entry point for npm scripts if (require.main === module) { const command = process.argv[2]; async function handleCLI() { const { Installer } = require('./index.js'); switch (command) { case 'install-binary': const installer = new Installer(); await installer.install(); console.log('✅ LSP Gateway binary installed successfully'); break; case 'version': const gateway = new (require('./index.js').LSPGateway)(); const versionInfo = gateway.getVersionInfo(); console.log(`lsp-gateway npm wrapper ${versionInfo.version}`); try { const binaryVersion = await gateway.getBinaryVersion(); console.log(`Binary: ${binaryVersion}`); } catch (error) { console.log('Binary: not available or not installed'); } break; default: console.error(`Unknown command: ${command}`); console.error('Available commands: install-binary, version'); process.exit(1); } } handleCLI().catch(err => { console.error('❌ Installation failed:', err.message); process.exit(1); }); }