UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

659 lines 27.5 kB
/** * Configuration Management System * Infrastructure as Code (IAC) support for Hive AI * * Enables terminal developers to manage Hive AI configuration through: * - YAML/JSON configuration files * - Environment variable substitution * - Validation and drift detection * - Import/export capabilities * - Multi-environment support */ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { logger } from '../../utils/logging.js'; export class ConfigurationManager { configDir; currentEnvironment; constructor(environment = 'default') { this.configDir = path.join(os.homedir(), '.hive-ai', 'configs'); this.currentEnvironment = environment; this.ensureConfigDir(); } /** * Ensure configuration directory exists */ ensureConfigDir() { if (!fs.existsSync(this.configDir)) { fs.mkdirSync(this.configDir, { recursive: true }); } } /** * Parse configuration file with environment variable substitution */ async parseConfig(configPath) { try { if (!fs.existsSync(configPath)) { throw new Error(`Configuration file not found: ${configPath}`); } const content = fs.readFileSync(configPath, 'utf8'); const substituted = this.substituteEnvironmentVariables(content); let config; if (configPath.endsWith('.yaml') || configPath.endsWith('.yml')) { // Would need yaml parser here - for now we'll use JSON config = JSON.parse(substituted); } else { config = JSON.parse(substituted); } // Apply environment-specific overrides if (config.overrides && config.overrides[this.currentEnvironment]) { config = this.mergeConfigs(config, config.overrides[this.currentEnvironment]); } return config; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to parse configuration: ${errorMessage}`); } } /** * Substitute environment variables in configuration content */ substituteEnvironmentVariables(content) { return content.replace(/\$\{([^}]+)\}/g, (match, varExpression) => { // Handle default values: ${VAR_NAME:-default_value} const [varName, defaultValue] = varExpression.split(':-'); const value = process.env[varName.trim()]; if (value !== undefined) { return value; } else if (defaultValue !== undefined) { return defaultValue; } else { logger.warn(`Environment variable ${varName} not found and no default provided`); return match; // Keep original if not found } }); } /** * Merge two configuration objects */ mergeConfigs(base, override) { const result = { ...base }; for (const key in override) { if (override[key] !== null && typeof override[key] === 'object' && !Array.isArray(override[key])) { result[key] = this.mergeConfigs(result[key] || {}, override[key]); } else { result[key] = override[key]; } } return result; } /** * Validate configuration file */ async validateConfig(config) { const errors = []; const warnings = []; // Required fields validation if (!config.metadata?.name) { errors.push('metadata.name is required'); } if (!config.license?.key) { errors.push('license.key is required'); } if (!config.providers?.openrouter?.api_key) { errors.push('providers.openrouter.api_key is required'); } // Pipeline validation if (!config.pipelines || config.pipelines.length === 0) { warnings.push('No pipelines configured'); } else { config.pipelines.forEach((pipeline, index) => { if (!pipeline.name) { errors.push(`pipelines[${index}].name is required`); } if (!pipeline.stages?.generator) { errors.push(`pipelines[${index}].stages.generator is required`); } if (!pipeline.stages?.refiner) { errors.push(`pipelines[${index}].stages.refiner is required`); } if (!pipeline.stages?.validator) { errors.push(`pipelines[${index}].stages.validator is required`); } }); } // Backup location validation if (config.backup?.locations) { config.backup.locations.forEach((location, index) => { if (!location.name) { errors.push(`backup.locations[${index}].name is required`); } if (!location.path) { errors.push(`backup.locations[${index}].path is required`); } if (location.type === 's3' && !location.credentials) { errors.push(`backup.locations[${index}] requires credentials for S3 type`); } }); } // MCP configuration validation if (config.mcp) { if (!config.mcp.transport?.type) { warnings.push('mcp.transport.type not specified, defaulting to http+sse'); } if (config.mcp.transport?.type === 'http+sse') { const port = config.mcp.transport.port; if (port && (port < 1024 || port > 65535)) { errors.push('mcp.transport.port must be between 1024 and 65535'); } if (config.mcp.transport.port_range) { const { min, max } = config.mcp.transport.port_range; if (min >= max) { errors.push('mcp.transport.port_range.min must be less than max'); } if (min < 1024 || max > 65535) { errors.push('mcp.transport.port_range must be between 1024 and 65535'); } } } if (config.mcp.server?.max_sessions && config.mcp.server.max_sessions < 1) { errors.push('mcp.server.max_sessions must be at least 1'); } if (config.mcp.server?.session_ttl && config.mcp.server.session_ttl < 60) { warnings.push('mcp.server.session_ttl less than 60 seconds may cause frequent disconnections'); } if (config.mcp.ides?.configurations) { config.mcp.ides.configurations.forEach((ideConfig, index) => { if (!ideConfig.ide) { errors.push(`mcp.ides.configurations[${index}].ide is required`); } if (ideConfig.config_path && !ideConfig.config_path.includes('.')) { warnings.push(`mcp.ides.configurations[${index}].config_path should include file extension`); } }); } } // Environment variable validation const missingEnvVars = this.checkRequiredEnvironmentVariables(config); if (missingEnvVars.length > 0) { warnings.push(`Missing environment variables: ${missingEnvVars.join(', ')}`); } return { valid: errors.length === 0, errors, warnings }; } /** * Check for required environment variables */ checkRequiredEnvironmentVariables(config) { const missing = []; // Extract environment variable references from config const configStr = JSON.stringify(config); const envVarMatches = configStr.match(/\$\{([^}]+)\}/g) || []; envVarMatches.forEach(match => { const varName = match.slice(2, -1).split(':-')[0].trim(); if (!process.env[varName]) { missing.push(varName); } }); return [...new Set(missing)]; // Remove duplicates } /** * Export current configuration */ async exportConfig(outputPath) { try { // Get current configuration from unified database const { getConfig } = await import('../../storage/unified-database.js'); const config = { metadata: { name: 'exported-config', description: 'Exported Hive AI configuration', version: '1.0.0', environment: this.currentEnvironment, created: new Date().toISOString() }, license: { key: await getConfig('license_key') || '${HIVE_LICENSE_KEY}' }, providers: { openrouter: { api_key: '${OPENROUTER_API_KEY}' // Never export actual keys } }, pipelines: [], mcp: { transport: { type: await getConfig('mcp_transport_type') || 'http+sse', port: parseInt(await getConfig(`mcp_server_port_${this.currentEnvironment}`)) || 3000, auto_port_detection: (await getConfig('mcp_auto_port_detection')) === 'true' || true, port_range: JSON.parse(await getConfig('mcp_port_range') || '{"min": 3000, "max": 3100}') }, server: JSON.parse(await getConfig('mcp_server_config') || '{"timeout": 30000, "max_sessions": 100, "session_ttl": 3600, "streaming": {"enabled": true}}'), ides: JSON.parse(await getConfig('mcp_ide_settings') || '{"auto_detection": true, "supported_ides": ["claude-code", "vscode", "cursor", "windsurf"]}'), security: JSON.parse(await getConfig('mcp_security_config') || '{"session_security": {"secure_ids": true}}'), health: JSON.parse(await getConfig('mcp_health_config') || '{"endpoint_enabled": true, "detailed_metrics": false, "check_interval": 300}') } }; // Add more configuration extraction here based on unified database schema const configJson = JSON.stringify(config, null, 2); if (outputPath) { fs.writeFileSync(outputPath, configJson); logger.info(`Configuration exported to: ${outputPath}`); return outputPath; } else { const defaultPath = path.join(this.configDir, `hive-config-${this.currentEnvironment}-${Date.now()}.json`); fs.writeFileSync(defaultPath, configJson); logger.info(`Configuration exported to: ${defaultPath}`); return defaultPath; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to export configuration: ${errorMessage}`); } } /** * Apply configuration to system */ async applyConfig(config) { try { logger.info(`Applying configuration: ${config.metadata.name}`); // Validate before applying const validation = await this.validateConfig(config); if (!validation.valid) { throw new Error(`Configuration validation failed: ${validation.errors.join(', ')}`); } const { setConfig } = await import('../../storage/unified-database.js'); // Apply license configuration if (config.license.key) { await setConfig('license_key', config.license.key); } // Apply provider configuration if (config.providers.openrouter.api_key) { await setConfig('openrouter_api_key', config.providers.openrouter.api_key); } // Apply pipeline configurations if (config.pipelines) { await setConfig('pipeline_profiles', JSON.stringify(config.pipelines)); } // Apply intelligence settings if (config.intelligence) { await setConfig('intelligence_config', JSON.stringify(config.intelligence)); } // Apply backup configuration if (config.backup) { await setConfig('backup_config', JSON.stringify(config.backup)); } // Apply monitoring configuration if (config.monitoring) { await setConfig('monitoring_config', JSON.stringify(config.monitoring)); } // Apply MCP configuration if (config.mcp) { // Store MCP transport settings await setConfig('mcp_transport_type', config.mcp.transport?.type || 'http+sse'); if (config.mcp.transport?.port) { await setConfig(`mcp_server_port_${this.currentEnvironment}`, config.mcp.transport.port.toString()); } if (config.mcp.transport?.port_range) { await setConfig('mcp_port_range', JSON.stringify(config.mcp.transport.port_range)); } if (config.mcp.transport?.auto_port_detection !== undefined) { await setConfig('mcp_auto_port_detection', config.mcp.transport.auto_port_detection.toString()); } // Store MCP server settings if (config.mcp.server) { await setConfig('mcp_server_config', JSON.stringify(config.mcp.server)); } // Store IDE configurations if (config.mcp.ides) { await setConfig('mcp_ide_settings', JSON.stringify(config.mcp.ides)); // Store environment-specific IDE config await setConfig(`mcp_ide_config_${this.currentEnvironment}`, JSON.stringify({ transport: config.mcp.transport?.type || 'http+sse', port: config.mcp.transport?.port || 3000, environment: this.currentEnvironment, endpoints: { mcp: `http://localhost:${config.mcp.transport?.port || 3000}/mcp`, health: `http://localhost:${config.mcp.transport?.port || 3000}/health` }, version: '2025-03-26', lastUpdated: new Date().toISOString(), configuredIDEs: config.mcp.ides.configurations?.map(c => c.ide) || [] })); } // Store MCP security settings if (config.mcp.security) { await setConfig('mcp_security_config', JSON.stringify(config.mcp.security)); } // Store MCP health settings if (config.mcp.health) { await setConfig('mcp_health_config', JSON.stringify(config.mcp.health)); } } logger.info('✅ Configuration applied successfully'); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to apply configuration: ${errorMessage}`); } } /** * Compare configuration with current system state */ async diffConfig(config) { try { const currentConfig = await this.getCurrentConfig(); return this.computeDiff(currentConfig, config); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to compute configuration diff: ${errorMessage}`); } } /** * Get current system configuration */ async getCurrentConfig() { const { getConfig } = await import('../../storage/unified-database.js'); return { metadata: { name: 'current-config', description: 'Current system configuration', version: '1.0.0' }, license: { key: await getConfig('license_key') || '' }, providers: { openrouter: { api_key: await getConfig('openrouter_api_key') || '' } }, pipelines: JSON.parse(await getConfig('pipeline_profiles') || '[]'), mcp: { transport: { type: await getConfig('mcp_transport_type') || 'http+sse', port: parseInt(await getConfig(`mcp_server_port_${this.currentEnvironment}`)) || undefined, auto_port_detection: (await getConfig('mcp_auto_port_detection')) === 'true', port_range: JSON.parse(await getConfig('mcp_port_range') || 'null') }, server: JSON.parse(await getConfig('mcp_server_config') || 'null'), ides: JSON.parse(await getConfig('mcp_ide_settings') || 'null'), security: JSON.parse(await getConfig('mcp_security_config') || 'null'), health: JSON.parse(await getConfig('mcp_health_config') || 'null') } }; } /** * Compute differences between two configurations */ computeDiff(current, target) { const diff = { added: [], removed: [], changed: [] }; // This is a simplified diff - in production you'd want a more sophisticated algorithm const currentStr = JSON.stringify(current, null, 2); const targetStr = JSON.stringify(target, null, 2); if (currentStr !== targetStr) { diff.changed.push({ path: 'root', old_value: current, new_value: target }); } return diff; } /** * Generate configuration template */ generateTemplate(templateType = 'basic') { const templates = { basic: { metadata: { name: 'hive-ai-config', description: 'Basic Hive AI configuration', version: '1.0.0', environment: 'development' }, license: { key: '${HIVE_LICENSE_KEY}' }, providers: { openrouter: { api_key: '${OPENROUTER_API_KEY}' } }, pipelines: [ { name: 'Expert-Coding', description: 'Optimized for software development', enabled: true, stages: { generator: 'anthropic/claude-3.5-sonnet', refiner: 'openai/gpt-4-turbo', validator: 'google/gemini-pro' } } ], mcp: { transport: { type: 'http+sse', port: 3000, auto_port_detection: true, port_range: { min: 3000, max: 3100 } }, server: { timeout: 30000, max_sessions: 50, session_ttl: 3600, streaming: { enabled: true, buffer_size: 1024, flush_interval: 100 } }, ides: { auto_detection: true, supported_ides: ['claude-code', 'vscode', 'cursor', 'windsurf'], configurations: [ { ide: 'claude-code', enabled: true } ] }, security: { session_security: { secure_ids: true, ip_validation: false }, rate_limiting: { enabled: true, requests_per_minute: 60 } }, health: { endpoint_enabled: true, detailed_metrics: false, check_interval: 300 } } }, enterprise: { metadata: { name: 'hive-ai-enterprise-config', description: 'Enterprise Hive AI configuration with advanced MCP settings', version: '1.0.0', environment: 'production' }, license: { key: '${HIVE_ENTERPRISE_LICENSE_KEY}' }, providers: { openrouter: { api_key: '${OPENROUTER_API_KEY}', rate_limit: { requests_per_minute: 1000, tokens_per_minute: 50000 }, timeout: { connection: 10000, read: 60000 } } }, pipelines: [ { name: 'Enterprise-Consensus', description: 'High-performance enterprise consensus pipeline', enabled: true, stages: { generator: 'anthropic/claude-3.5-sonnet', refiner: 'openai/gpt-4-turbo', validator: 'google/gemini-pro', curator: 'anthropic/claude-3-haiku' }, settings: { max_tokens: 4000, temperature: 0.1, timeout: 120000 } } ], mcp: { transport: { type: 'http+sse', port: 3001, auto_port_detection: true, port_range: { min: 3001, max: 3050 } }, server: { timeout: 60000, max_sessions: 200, session_ttl: 7200, streaming: { enabled: true, buffer_size: 2048, flush_interval: 50 } }, ides: { auto_detection: true, supported_ides: ['claude-code', 'vscode', 'cursor', 'windsurf'], configurations: [ { ide: 'claude-code', enabled: true }, { ide: 'vscode', enabled: true } ] }, security: { cors_origins: ['https://app.company.com'], session_security: { secure_ids: true, ip_validation: true }, rate_limiting: { enabled: true, requests_per_minute: 120 } }, health: { endpoint_enabled: true, detailed_metrics: true, check_interval: 60 } }, intelligence: { mode: 'auto', cost_optimization: { enabled: true, budget_limit: 1000, cost_alerts: true }, template_maintenance: { auto_update: true, health_checks: true, check_interval: '24h' } }, backup: { locations: [ { name: 'primary-backup', path: '/var/backups/hive-ai', type: 'local', enabled: true, priority: 1 } ], schedule: { daily: { enabled: true, time: '02:00', retention: 30 }, weekly: { enabled: true, day: 'sunday', time: '01:00', retention: 12 } }, verification: { integrity_check: true, test_restore: true } }, monitoring: { health_checks: { enabled: true, interval: '5m', endpoints: ['http://localhost:3001/health'] }, performance: { enabled: true, metrics: ['response_time', 'throughput', 'error_rate'] }, alerts: { enabled: true, channels: [ { type: 'email', address: 'ops@company.com' } ], thresholds: { error_rate: 0.05, response_time: 30000, daily_cost: 100 } } }, security: { key_rotation: { enabled: true, interval: '30d' }, audit: { enabled: true, log_path: '/var/log/hive-ai/audit.log', retention: '1y' } } } }; return JSON.stringify(templates[templateType] || templates.basic, null, 2); } } export const configManager = new ConfigurationManager(); //# sourceMappingURL=config-management.js.map