@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
JavaScript
/**
* 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