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