UNPKG

@dorothywebb/any-browser-mcp

Version:

Any Browser MCP - Launch Chrome with your actual data in debug mode for comprehensive browser automation

255 lines 8.62 kB
/** * Configuration Manager for Any Browser MCP * * @fileoverview Handles loading, validation, and access to configuration settings. * Enforces safety policies to prevent auto-launching browsers during VS Code startup. * * @example * ```typescript * import { getConfigManager } from './ConfigManager.js'; * * const config = getConfigManager(); * const debugPort = config.getDebugPort(); * const browserConfig = config.getBrowserConfig(); * ``` * * @category Core Components */ import { readFileSync, existsSync } from 'fs'; import { join } from 'path'; import { ConfigValidator } from './ConfigValidator.js'; import { ErrorFactory } from '../types/errors.js'; // Handle both ES modules and CommonJS environments let currentFileName; let currentDirName; // Handle both ES modules and CommonJS/test environments // Check if we're in a test environment (Jest sets NODE_ENV to 'test') if (process.env.NODE_ENV === 'test') { // Test environment - use current working directory currentFileName = ''; currentDirName = process.cwd(); } else { // Production environment - fallback to process.cwd() to avoid TypeScript issues currentFileName = ''; currentDirName = process.cwd(); } /** * Singleton configuration manager that handles all configuration-related operations. * * @description This class provides centralized access to application configuration, * enforces safety policies, and ensures configuration validation. It implements * the singleton pattern to maintain consistency across the application. * * @example * ```typescript * const configManager = ConfigManager.getInstance(); * configManager.setVerbose(true); * * const browserConfig = configManager.getBrowserConfig(); * console.log(`Debug port: ${browserConfig.debugPort}`); * ``` */ export class ConfigManager { config; static instance; verbose = false; /** * Private constructor to enforce singleton pattern. * Loads and validates configuration, then enforces safety policies. */ constructor() { this.config = this.loadAndValidateConfig(); this.enforceNeverLaunchPolicy(); } /** * Gets the singleton instance of ConfigManager. * * @returns The singleton ConfigManager instance * * @example * ```typescript * const config = ConfigManager.getInstance(); * ``` */ static getInstance() { if (!ConfigManager.instance) { ConfigManager.instance = new ConfigManager(); } return ConfigManager.instance; } loadAndValidateConfig() { try { const configPath = join(currentDirName, '../../config.json'); let rawConfig = {}; if (existsSync(configPath)) { const configData = readFileSync(configPath, 'utf-8'); rawConfig = JSON.parse(configData); if (this.verbose) { console.log('📋 Loading configuration from:', configPath); } } else { if (this.verbose) { console.log('📋 No config file found, using defaults'); } } // Validate and merge with defaults const validatedConfig = ConfigValidator.mergeWithDefaults(rawConfig); if (this.verbose) { console.log('✅ Configuration validated successfully'); } return validatedConfig; } catch (error) { if (error instanceof Error && error.message.includes('Configuration validation failed')) { // Re-throw validation errors throw error; } // Handle other errors (file read, JSON parse, etc.) const configError = ErrorFactory.createConfigError(`Failed to load configuration: ${error.message}`, { operation: 'loadConfig', details: { error: error.message } }); console.error('❌ Configuration loading failed, using defaults:', configError.getFormattedMessage()); // Return default configuration as fallback return ConfigValidator.getDefaultConfig(); } } getDefaultConfig() { // Use the validator's default configuration return ConfigValidator.getDefaultConfig(); } /** * Enforce safe browser policy - allow separate instance launching but maintain safety */ enforceNeverLaunchPolicy() { // Ensure safe behavior while allowing separate instance launching this.config.server.initializeOnStartup = false; this.config.server.lazyInitialization = true; this.config.server.connectOnFirstUse = true; this.config.features.preventAutoStart = true; this.config.features.respectUserBrowser = true; this.config.browser.confirmDestructiveActions = true; // Only allow launching if it's a separate instance if (this.config.browser.allowLaunch && !this.config.browser.useSeparateInstance) { this.config.browser.allowLaunch = false; this.config.browser.useExistingOnly = true; } } /** * Gets the browser configuration section. * * @returns Browser configuration including debug port, timeouts, and launch settings * * @example * ```typescript * const browserConfig = config.getBrowserConfig(); * console.log(`Debug port: ${browserConfig.debugPort}`); * console.log(`Auto launch: ${browserConfig.autoLaunch}`); * ``` */ getBrowserConfig() { return this.config.browser; } /** * Gets the server configuration section. * * @returns Server configuration including strategy and connection settings * * @example * ```typescript * const serverConfig = config.getServerConfig(); * console.log(`Strategy: ${serverConfig.strategy}`); * console.log(`Lazy init: ${serverConfig.lazyInitialization}`); * ``` */ getServerConfig() { return this.config.server; } /** * Gets the safety configuration section. * * @returns Safety configuration including validation and timeout settings * * @example * ```typescript * const safetyConfig = config.getSafetyConfig(); * console.log(`Validate connection: ${safetyConfig.validateConnection}`); * ``` */ getSafetyConfig() { return this.config.safety; } getFullConfig() { return this.config; } shouldNeverLaunch() { return this.config.safety.neverLaunchBrowser; } shouldUseExistingOnly() { return this.config.browser.useExistingOnly; } shouldInitializeOnStartup() { return this.config.server.initializeOnStartup; } isLazyInitializationEnabled() { return this.config.server.lazyInitialization; } getDebugEndpoint() { return this.config.browser.endpoint || `http://localhost:${this.config.browser.debugPort}`; } getDebugPort() { return this.config.browser.debugPort; } isVerbose() { return this.config.server.verbose; } setVerbose(verbose) { this.config.server.verbose = verbose; this.verbose = verbose; } /** * Validate current configuration */ validateCurrentConfig() { try { ConfigValidator.validateOrThrow(this.config); if (this.verbose) { console.log('✅ Current configuration is valid'); } } catch (error) { console.error('❌ Configuration validation failed:', error); throw error; } } /** * Update configuration with validation */ updateConfig(newConfig) { const mergedConfig = { ...this.config, ...newConfig }; const validatedConfig = ConfigValidator.validateOrThrow(mergedConfig); this.config = validatedConfig; this.enforceNeverLaunchPolicy(); if (this.verbose) { console.log('✅ Configuration updated and validated'); } } /** * Get configuration validation report */ getValidationReport() { const result = ConfigValidator.validate(this.config); return { isValid: result.success, errors: result.errors || [], warnings: result.warnings || [], timestamp: new Date().toISOString() }; } } export function getConfigManager() { return ConfigManager.getInstance(); } //# sourceMappingURL=ConfigManager.js.map