UNPKG

task-engine-ai-core

Version:

Revolutionary AI-driven task management system with complete transformation trilogy: Frontend v0.1.0, Backend v0.2.0, CLI v0.3.0 - Enterprise-grade performance with 95% improvements

364 lines (314 loc) 10.9 kB
/** * Configuration Loader for Task Engine AI Core * * Handles loading, merging, and validating configuration files * for the task-engine-ai-core package. * * @version 0.3.0 * @author Task Engine AI Team */ import { readFileSync, existsSync } from 'fs'; import { join, resolve } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * Configuration Loader Class */ export class ConfigLoader { constructor(options = {}) { this.options = { environment: process.env.NODE_ENV || 'default', configDir: options.configDir || join(process.cwd(), 'config'), packageConfigDir: join(__dirname, '../../config'), validateSchema: options.validateSchema !== false, ...options }; this.config = null; this.schema = null; } /** * Load configuration based on environment */ async load() { try { // Load default configuration const defaultConfig = this.loadConfigFile('default.json'); // Load environment-specific configuration const envConfig = this.loadConfigFile(`${this.options.environment}.json`); // Load user configuration if exists const userConfig = this.loadConfigFile('user.json', false); // Load local configuration if exists const localConfig = this.loadConfigFile('local.json', false); // Merge configurations (later configs override earlier ones) this.config = this.mergeConfigs( defaultConfig, envConfig, userConfig, localConfig ); // Apply environment variables this.applyEnvironmentVariables(); // Validate configuration if schema validation is enabled if (this.options.validateSchema) { await this.validateConfiguration(); } // Set computed values this.setComputedValues(); return this.config; } catch (error) { throw new Error(`Failed to load configuration: ${error.message}`); } } /** * Load a specific configuration file */ loadConfigFile(filename, required = true) { const paths = [ join(this.options.configDir, filename), join(this.options.packageConfigDir, filename) ]; for (const configPath of paths) { if (existsSync(configPath)) { try { const content = readFileSync(configPath, 'utf8'); return JSON.parse(content); } catch (error) { throw new Error(`Failed to parse config file ${configPath}: ${error.message}`); } } } if (required) { throw new Error(`Required configuration file not found: ${filename}`); } return {}; } /** * Merge multiple configuration objects */ mergeConfigs(...configs) { return configs.reduce((merged, config) => { if (!config || typeof config !== 'object') { return merged; } return this.deepMerge(merged, config); }, {}); } /** * Deep merge two objects */ deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source.hasOwnProperty(key)) { if (this.isObject(source[key]) && this.isObject(target[key])) { result[key] = this.deepMerge(target[key], source[key]); } else { result[key] = source[key]; } } } return result; } /** * Check if value is an object */ isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); } /** * Apply environment variables to configuration */ applyEnvironmentVariables() { const envMappings = { 'TASK_ENGINE_PORT': 'architectures.backend.port', 'TASK_ENGINE_HOST': 'architectures.backend.host', 'TASK_ENGINE_DB_TYPE': 'architectures.backend.database.type', 'TASK_ENGINE_DB_HOST': 'architectures.backend.database.host', 'TASK_ENGINE_DB_PORT': 'architectures.backend.database.port', 'TASK_ENGINE_DB_NAME': 'architectures.backend.database.database', 'TASK_ENGINE_REDIS_HOST': 'architectures.backend.cache.host', 'TASK_ENGINE_REDIS_PORT': 'architectures.backend.cache.port', 'TASK_ENGINE_LOG_LEVEL': 'taskEngine.logging.level', 'TASK_ENGINE_DEBUG': 'taskEngine.debug', 'ANTHROPIC_API_KEY': 'ai.providers.anthropic.apiKey', 'OPENAI_API_KEY': 'ai.providers.openai.apiKey', 'MCP_PORT': 'mcp.server.port', 'MCP_HOST': 'mcp.server.host' }; for (const [envVar, configPath] of Object.entries(envMappings)) { const envValue = process.env[envVar]; if (envValue !== undefined) { this.setNestedValue(this.config, configPath, this.parseEnvValue(envValue)); } } } /** * Set nested value in object using dot notation */ setNestedValue(obj, path, value) { const keys = path.split('.'); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!(key in current) || typeof current[key] !== 'object') { current[key] = {}; } current = current[key]; } current[keys[keys.length - 1]] = value; } /** * Parse environment variable value to appropriate type */ parseEnvValue(value) { // Boolean values if (value.toLowerCase() === 'true') return true; if (value.toLowerCase() === 'false') return false; // Numeric values if (/^\d+$/.test(value)) return parseInt(value, 10); if (/^\d+\.\d+$/.test(value)) return parseFloat(value); // String values return value; } /** * Validate configuration against schema */ async validateConfiguration() { try { if (!this.schema) { this.schema = this.loadConfigFile('schema.json'); } // Basic validation - in a real implementation, you'd use a JSON schema validator this.validateRequired(); this.validateTypes(); this.validateRanges(); } catch (error) { throw new Error(`Configuration validation failed: ${error.message}`); } } /** * Validate required fields */ validateRequired() { const required = ['taskEngine', 'architectures']; for (const field of required) { if (!this.config[field]) { throw new Error(`Required configuration field missing: ${field}`); } } } /** * Validate data types */ validateTypes() { // Validate ports are numbers const ports = [ 'architectures.frontend.port', 'architectures.backend.port', 'mcp.server.port' ]; for (const portPath of ports) { const port = this.getNestedValue(this.config, portPath); if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) { throw new Error(`Invalid port number at ${portPath}: ${port}`); } } } /** * Validate value ranges */ validateRanges() { // Validate AI temperature const temperature = this.getNestedValue(this.config, 'ai.providers.anthropic.temperature'); if (temperature !== undefined && (temperature < 0 || temperature > 2)) { throw new Error(`AI temperature must be between 0 and 2: ${temperature}`); } } /** * Get nested value from object using dot notation */ getNestedValue(obj, path) { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : undefined; }, obj); } /** * Set computed values based on configuration */ setComputedValues() { // Set default database path for SQLite if (this.config.architectures?.backend?.database?.type === 'sqlite' && !this.config.architectures.backend.database.path) { this.config.architectures.backend.database.path = `data/${this.options.environment}-tasks.db`; } // Set default log file path if (this.config.taskEngine?.logging?.file && !this.config.taskEngine.logging.file.path) { this.config.taskEngine.logging.file.path = `logs/task-engine-${this.options.environment}.log`; } // Set computed limits based on environment if (this.options.environment === 'development') { this.config.limits = { ...this.config.limits, maxTasks: Math.min(this.config.limits?.maxTasks || 1000, 1000), maxConcurrentConnections: Math.min(this.config.limits?.maxConcurrentConnections || 100, 100) }; } } /** * Get current configuration */ getConfig() { if (!this.config) { throw new Error('Configuration not loaded. Call load() first.'); } return this.config; } /** * Get configuration value by path */ get(path, defaultValue = undefined) { const value = this.getNestedValue(this.getConfig(), path); return value !== undefined ? value : defaultValue; } /** * Check if configuration has a specific path */ has(path) { return this.getNestedValue(this.getConfig(), path) !== undefined; } /** * Reload configuration */ async reload() { this.config = null; return await this.load(); } } /** * Default configuration loader instance */ export const configLoader = new ConfigLoader(); /** * Load configuration with options */ export async function loadConfig(options = {}) { const loader = new ConfigLoader(options); return await loader.load(); } /** * Get configuration value */ export function getConfig(path, defaultValue) { return configLoader.get(path, defaultValue); } /** * Check if configuration has path */ export function hasConfig(path) { return configLoader.has(path); } export default ConfigLoader;