/** * Configuration Manager * Manages user-editable configuration in .cdp-tools/config.json */ /** * Port monitoring frequency configuration - interval per level in ms */ export interface PortMonitoringFreqMs { block: number; error: number; inform: number; } /** * Port monitoring configuration */ export interface PortMonitoringConfig { portMonitoringFreqMs: PortMonitoringFreqMs; } /** * Replay system configuration */ export interface ReplayConfig { /** Maximum nested conditional depth (default: 10) */ maxConditionalDepth: number; /** Maximum regex pattern length for url:matches conditions (default: 500) */ maxRegexLength: number; /** Show visual cursor during replay (default: true) */ showCursor: boolean; /** Export path for Playwright tests (default: ./tests/e2e) */ playwrightExportPath: string; /** Export path for Puppeteer tests (default: ./tests/puppeteer) */ puppeteerExportPath: string; /** Maximum delay between commands in ms when recording (default: 1000, 0 = no limit) */ maxDelayMs: number; } /** * DOM change detection configuration */ export interface ChangeDetectionConfig { /** Enable automatic change detection on actions (default: true) */ enabled: boolean; /** Max time to wait for mutations to settle in ms (default: 2000) */ settleTimeout: number; /** Time of no mutations to consider settled in ms (default: 300) */ quietPeriod: number; /** Longer timeout for page navigation in ms (default: 3000) */ navigationTimeout: number; } /** * Click validation configuration for replay sequences */ export interface ClickValidationConfig { /** Enable click validation in replay sequences (default: true) */ enabled: boolean; /** Validate navigation success if click caused URL change (default: true) */ validateNavigation: boolean; /** Require DOM mutations after click (default: false) */ requireDomChanges: boolean; /** Failure mode for DOM changes check: 'error' stops sequence, 'warn' logs and continues (default: 'warn') */ domChangesFailMode: 'error' | 'warn'; /** Check for new console errors after click (default: true) */ failOnConsoleErrors: boolean; /** Failure mode for console errors: 'error' stops sequence, 'warn' logs and continues (default: 'error') */ consoleErrorsFailMode: 'error' | 'warn'; /** Validate network requests triggered by click (default: false) */ validateNetworkPayload: boolean; /** Failure mode for network failures: 'error' stops sequence, 'warn' logs and continues (default: 'warn') */ networkFailMode: 'error' | 'warn'; /** Delay before validation checks in ms (default: 100) */ postClickDelayMs: number; } /** * Chrome configuration */ export interface ChromeConfig { /** Starting port for Chrome debugging - will find next available if in use (default: 9222) */ startingDebugPort: number; /** Inactivity timeout in minutes before closing connections and Chrome (default: 5, set to 0 to disable) */ inactivityTimeoutMinutes: number; /** Polling interval in minutes for inactivity checks (default: 2) */ inactivityPollingMinutes: number; /** * Where named persistent Chrome profiles (`launchChrome({ profile })`) live. * * Empty string (the default) means the global root `~/.cdp-tools/profiles`, * so a profile named "work-google" is shared by every project on this * machine. Set it in a project-local config (see `config({action:'useLocal'})`) * to give that project its own profile store. Relative paths resolve against * the process working directory; a leading `~/` is expanded. */ persistentProfileRoot: string; } /** * Debug configuration */ export interface DebugConfig { /** Enable debug logging to debug.log on startup (default: false) */ enabled: boolean; /** Enable history log file - records all commands in replay-compatible format (default: false) */ historyLogEnabled: boolean; } /** * List of tools that can be toggled via config * New tools added here will be auto-discovered and added to enabled list on startup */ export declare const TOGGLEABLE_TOOLS: readonly ["connection", "tab", "breakpoint", "execution", "inspection", "source", "console", "network", "page", "dom", "screenshot", "input", "content", "modal", "storage", "download", "request", "assert", "wait", "replay", "server", "issues", "dashboard"]; export type ToggleableToolName = typeof TOGGLEABLE_TOOLS[number]; /** * Tool dependencies - key depends on values * If a dependency is disabled, the dependent tool cannot function */ export declare const TOOL_DEPENDENCIES: Record; /** * Check for dependency conflicts in tools config * Returns array of conflict descriptions (grouped by disabled dependency), empty if no conflicts */ export declare function checkToolDependencyConflicts(enabled: string[], disabled: string[]): string[]; /** * Root configuration structure */ export interface ToolsConfig { enabled: string[]; disabled: string[]; } /** * Session lifetime configuration. * * Read by the supervisor process, not this ConfigManager - see * src/supervisor/idle-config.ts, which parses the same file directly because * the supervisor stays out of the server's module graph. Declared here so the * shape stays in one place and `config({ action: 'show' })` reports it. */ export interface SessionConfig { /** * Minutes without any traffic from the MCP client before the server is * suspended: it releases its connections, Chrome instances and managed dev * servers and exits, while the supervisor stays connected and spawns a * fresh server on the next request (default: 120, set to 0 to disable). */ idleSuspendMinutes: number; /** How often to check the MCP client is still alive, in seconds (default: 60) */ clientPollSeconds: number; } export interface CdpToolsConfig { version: number; configLocation: 'local' | 'global'; session: SessionConfig; chrome: ChromeConfig; portMonitoring: PortMonitoringConfig; replay: ReplayConfig; changeDetection: ChangeDetectionConfig; clickValidation: ClickValidationConfig; debug: DebugConfig; tools: ToolsConfig; } /** * Configuration Manager * Loads and saves configuration from .cdp-tools/config.json * Also tracks runtime port state */ export declare class ConfigManager { private config; private loaded; private loadedFromPath; private currentPort; private dependencyConflicts; private configWatchers; private reloadTimer; private static readonly RELOAD_DEBOUNCE_MS; constructor(); /** * Synchronously load config for early access during module initialization */ private loadSync; /** * Validate tool dependencies and store any conflicts */ private validateDependencies; /** * Check if there are dependency conflicts blocking tool access */ hasDependencyConflicts(): boolean; /** * Get the list of dependency conflicts */ getDependencyConflicts(): string[]; /** * Get preferred path for creating new config * Prefers working directory if .cdp-tools folder exists or can be created */ private getPreferredConfigPath; /** * Load configuration from disk * Checks local config first for configLocation preference. * If configLocation is 'global', uses global config. * Otherwise creates/uses local config (seeding from global if available). */ load(): Promise; /** * Re-read config.json from disk and apply it to the running process. * Unlike load(), this never writes back (no discover-and-persist) - it's * meant to pick up a manual edit live, not to run the one-time bootstrap, * and writing back here would re-trigger the file watcher that calls it. */ reload(): Promise<{ changed: boolean; path: string | null; }>; /** * debug.enabled/historyLogEnabled are otherwise only applied once at * server startup (see main() in index.ts) - mirror that here so a live * edit actually flips debug-logger.ts's module state. */ private applyLiveDebugSettings; /** * Watch the local and global config directories for edits and hot-reload * config.json into the running process. Watches the parent directory * (not the file itself) and ignores the event payload, debouncing to a * full reload() - same pattern as issue-tracker.ts's watcher, and safe * against atomic saves (write-temp + rename) losing the watch descriptor. * * Note: tools.enabled/tools.disabled cannot be hot-applied this way - the * MCP tool list is built once at server startup. Everything else this * class exposes (portMonitoring, replay, changeDetection, clickValidation, * debug) is read live from getConfig() and picks up a reload immediately. */ startWatching(): void; stopWatching(): void; /** * Coalesce a burst of filesystem events into one reload, firing a fixed * window after the FIRST event rather than the last. * * This used to clear and re-arm the timer on every event, which is the * textbook debounce - and the wrong shape here. startWatching() watches the * global ~/.cdp-tools directory as well as the project one, and that * directory is shared by every cdp-tools process on the machine (dashboard * locks, downloads, sequences). Under sustained unrelated writes there, the * timer was reset before it could ever fire, so an edit to config.json was * postponed indefinitely - live reload silently stopped working, and stayed * broken for as long as the other process kept writing. * * Firing from the first event bounds the latency at RELOAD_DEBOUNCE_MS no * matter how busy the directory is, while still collapsing the rapid * write/rename pairs an atomic save produces. */ private scheduleReload; /** * Deep merge two config objects */ private mergeConfig; /** * Save configuration to disk * Saves to the same location it was loaded from, or global if new */ save(): Promise; /** * Synchronously save configuration to disk * Used during loadSync() to persist config before async code runs */ private saveSync; /** * Get the full configuration */ getConfig(): CdpToolsConfig; /** * Get port monitoring configuration */ getPortMonitoringConfig(): PortMonitoringConfig; /** * Get the interval for a specific monitoring level */ getIntervalForLevel(level: 'block' | 'error' | 'inform'): number; /** * Get Chrome configuration */ getChromeConfig(): ChromeConfig; /** * Absolute directory that named persistent Chrome profiles live in * (`launchChrome({ profile })`, `config({action:'resetProfile'})`). * * Defaults to the global `~/.cdp-tools/profiles` so a named profile is shared * across projects. `chrome.persistentProfileRoot` in a project-local config * overrides it; relative values resolve against the working directory and a * leading `~/` is expanded. */ getPersistentProfileRoot(): string; /** * Get replay system configuration */ getReplayConfig(): ReplayConfig; /** * Get change detection configuration */ getChangeDetectionConfig(): ChangeDetectionConfig; /** * Get click validation configuration for replay sequences */ getClickValidationConfig(): ClickValidationConfig; /** * Get debug configuration */ getDebugConfig(): DebugConfig; /** * Get tools configuration */ getToolsConfig(): ToolsConfig; /** * Check if a tool is enabled * A tool is enabled if it's in the enabled list and not in the disabled list */ isToolEnabled(toolName: string): boolean; /** * Auto-discover and enable new tools * Any tool in TOGGLEABLE_TOOLS that isn't in disabled will be added to enabled * Also removes tools from enabled if they are in disabled * Returns true if config was modified */ discoverTools(): boolean; /** * Get list of available toggleable tools with their current state */ getToggleableTools(): Array<{ name: string; enabled: boolean; dependencies: string[]; }>; /** * Update port monitoring frequency configuration */ updatePortMonitoringFreqMs(updates: Partial): Promise; /** * Get the current port in use (runtime state) */ getCurrentPort(): number; /** * Set the current port (runtime state) */ setCurrentPort(port: number): void; /** * Get info about current config location and status */ getStatus(): { loadedFrom: string | null; isLocal: boolean; localPath: string; globalPath: string; localExists: boolean; globalExists: boolean; }; /** * Switch to using local config (creates if needed, optionally seeds from global) * * @param seedFromGlobal - seed new local config from global if it exists * @param projectPath - explicit project directory to treat as "local". * Needed when the MCP server's process.cwd() doesn't reflect the * project the user is currently working in (e.g. a shared long-lived * server process spawned from the home directory). */ useLocal(seedFromGlobal?: boolean, projectPath?: string): Promise<{ path: string; seeded: boolean; }>; /** * Switch to using global config * Writes a minimal local config with just configLocation: 'global' */ useGlobal(): Promise<{ path: string; }>; /** * Reset config to defaults */ reset(): Promise; /** * Create a backup of current config */ backup(): Promise<{ path: string; } | null>; /** * Clone global config to local */ cloneFromGlobal(): Promise<{ path: string; } | { error: string; }>; } export declare const configManager: ConfigManager; //# sourceMappingURL=config.d.ts.map