browser-devtools-mcp 0.2.24 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,7 +53,7 @@ Choose the platform by running the appropriate MCP server or CLI:
53
53
 
54
54
  - **Connection**: Connect to Node.js processes by PID, process name, port, WebSocket URL, or Docker container
55
55
  - **Non-Blocking Debugging**: Tracepoints, logpoints, exceptionpoints without pausing execution
56
- - **JavaScript Execution**: Run arbitrary JavaScript in the connected Node process via `run_js-in-node` (CDP Runtime.evaluate)—inspect `process.memoryUsage()`, call `require()` modules, read globals
56
+ - **JavaScript Execution**: Run arbitrary JavaScript in the connected Node process via `run_js-in-node` (CDP Runtime.evaluate)—use `return` to get output; async/await supported. Inspect `process.memoryUsage()`, call `require()` modules, read globals
57
57
  - **Source Map Support**: Resolves bundled/minified code to original source locations; `debug_resolve-source-location` translates stack traces and bundle locations to original source
58
58
  - **OpenTelemetry Integration**: When the Node process uses `@opentelemetry/api`, tracepoint/logpoint snapshots automatically include `traceContext` (traceId, spanId) for correlating backend traces with browser traces
59
59
  - **Docker Support**: Connect to Node.js processes running inside Docker containers (`containerId` / `containerName`)
@@ -77,9 +77,10 @@ Choose the platform by running the appropriate MCP server or CLI:
77
77
  - **Resize Window**: Resize the real browser window (OS-level) using Chrome DevTools Protocol
78
78
 
79
79
  ### Navigation Tools
80
- - **Go To**: Navigate to URLs with configurable wait strategies
81
- - **Go Back**: Navigate backward in history
82
- - **Go Forward**: Navigate forward in history
80
+ - **Go To**: Navigate to URLs with configurable wait strategies. By default (`includeSnapshot: true`) returns an ARIA snapshot with refs (`output`, `refs`); set `includeSnapshot: false` for url/status/ok only. Use `snapshotInteractiveOnly` and `snapshotCursorInteractive` to control which elements get refs (same as `a11y_take-aria-snapshot`).
81
+ - **Go Back**: Navigate backward in history. Same snapshot/refs behavior as Go To when `includeSnapshot` is true.
82
+ - **Go Forward**: Navigate forward in history. Same snapshot/refs behavior as Go To when `includeSnapshot` is true.
83
+ - **Reload**: Reload the current page. Same snapshot/refs behavior as Go To when `includeSnapshot` is true.
83
84
 
84
85
  ### Run Tools
85
86
  - **JS in Browser**: Execute JavaScript code inside the active browser page (page context with access to window, document, DOM, and Web APIs)
@@ -1122,6 +1123,8 @@ The server can be configured using environment variables. Configuration is divid
1122
1123
  | `SESSION_IDLE_SECONDS` | Idle session timeout (seconds) | `300` |
1123
1124
  | `SESSION_IDLE_CHECK_SECONDS` | Interval for checking idle sessions (seconds) | `30` |
1124
1125
  | `SESSION_CLOSE_ON_SOCKET_CLOSE` | Close session when socket closes | `false` |
1126
+ | `TOOL_OUTPUT_SCHEMA_DISABLE` | When true, omit tool output schema from MCP tool registration (can reduce token usage for some clients) | `false` |
1127
+ | `AVAILABLE_TOOL_DOMAINS` | Optional comma-separated list of tool domains to enable. When set, only tools from these domains are registered; unset means all tools. **Browser domains:** `a11y`, `content`, `debug`, `figma`, `interaction`, `navigation`, `o11y`, `react`, `run`, `stub`, `sync`. **Node domains:** `debug`, `run`. Example: `AVAILABLE_TOOL_DOMAINS=navigation,interaction,a11y` | (all tools) |
1125
1128
 
1126
1129
  ### Node Platform Configuration
1127
1130
 
@@ -1370,20 +1373,33 @@ The server can be configured using environment variables. Configuration is divid
1370
1373
  - `url` (string, required): URL to navigate to (must include scheme)
1371
1374
  - `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
1372
1375
  - `waitUntil` (enum, optional): When to consider navigation succeeded - "load", "domcontentloaded", "networkidle", or "commit" (default: "load")
1376
+ - `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after navigation and include `output` and `refs` in the response; when false, only url/status/ok are returned.
1377
+ - `snapshotInteractiveOnly` (boolean, optional): When includeSnapshot is true, if set, only interactive elements get refs (same as a11y_take-aria-snapshot).
1378
+ - `snapshotCursorInteractive` (boolean, optional): When includeSnapshot is true, if set, also include cursor-interactive elements in refs (same as a11y_take-aria-snapshot).
1373
1379
 
1374
1380
  **Returns:**
1375
1381
  - `url` (string): Final URL after navigation
1376
1382
  - `status` (number): HTTP status code
1377
1383
  - `statusText` (string): HTTP status text
1378
1384
  - `ok` (boolean): Whether navigation was successful (2xx status)
1385
+ - `output` (string, optional): When includeSnapshot is true, ARIA snapshot text (page URL, title, YAML tree).
1386
+ - `refs` (record, optional): When includeSnapshot is true, map of ref id (e1, e2, ...) to role/name/selector; use in interaction tools (e.g. click @e1).
1379
1387
  </details>
1380
1388
 
1381
1389
  <details>
1382
1390
  <summary><code>navigation_go-back</code> - Navigates backward in browser history.</summary>
1391
+
1392
+ **Parameters:** `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotInteractiveOnly`, `snapshotCursorInteractive` — same semantics as navigation_go-to for snapshot/refs.
1393
+
1394
+ **Returns:** `url`, `status`, `statusText`, `ok`; when includeSnapshot is true also `output` and `refs` (ARIA snapshot with refs).
1383
1395
  </details>
1384
1396
 
1385
1397
  <details>
1386
1398
  <summary><code>navigation_go-forward</code> - Navigates forward in browser history.</summary>
1399
+
1400
+ **Parameters:** `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotInteractiveOnly`, `snapshotCursorInteractive` — same semantics as navigation_go-to for snapshot/refs.
1401
+
1402
+ **Returns:** `url`, `status`, `statusText`, `ok`; when includeSnapshot is true also `output` and `refs` (ARIA snapshot with refs).
1387
1403
  </details>
1388
1404
 
1389
1405
  <details>
@@ -1392,12 +1408,17 @@ The server can be configured using environment variables. Configuration is divid
1392
1408
  **Parameters:**
1393
1409
  - `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
1394
1410
  - `waitUntil` (enum, optional): When to consider navigation succeeded - "load", "domcontentloaded", "networkidle", or "commit" (default: "load")
1411
+ - `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after reload and include `output` and `refs`; when false, only url/status/ok.
1412
+ - `snapshotInteractiveOnly` (boolean, optional): When includeSnapshot is true, control which elements get refs (same as a11y_take-aria-snapshot).
1413
+ - `snapshotCursorInteractive` (boolean, optional): When includeSnapshot is true, include cursor-interactive elements in refs (same as a11y_take-aria-snapshot).
1395
1414
 
1396
1415
  **Returns:**
1397
1416
  - `url` (string): Final URL after reload
1398
1417
  - `status` (number): HTTP status code
1399
1418
  - `statusText` (string): HTTP status text
1400
1419
  - `ok` (boolean): Whether reload was successful (2xx status)
1420
+ - `output` (string, optional): When includeSnapshot is true, ARIA snapshot text.
1421
+ - `refs` (record, optional): When includeSnapshot is true, map of ref id to role/name/selector for use in interaction tools.
1401
1422
  </details>
1402
1423
 
1403
1424
  ### Run Tools
@@ -1466,7 +1487,7 @@ The server can be configured using environment variables. Configuration is divid
1466
1487
  <summary><code>run_js-in-node</code> - Runs custom JavaScript INSIDE the connected Node.js process (Node platform only).</summary>
1467
1488
 
1468
1489
  **Parameters:**
1469
- - `script` (string, required): JavaScript code to execute in the Node process
1490
+ - `script` (string, required): JavaScript code to execute in the Node process. Use `return` to pass a value back (e.g. `return process.memoryUsage();`). Async/await is supported (e.g. `return await fetch(url).then(r => r.json());`).
1470
1491
  - `timeoutMs` (number, optional): Max evaluation time in milliseconds (default: 5000, max: 30000)
1471
1492
 
1472
1493
  **Returns:**
@@ -1474,19 +1495,21 @@ The server can be configured using environment variables. Configuration is divid
1474
1495
 
1475
1496
  **Notes:**
1476
1497
  - Requires `debug_connect` first—must be connected to a Node.js process
1498
+ - **You must use `return` to get output** (e.g. `return 2+2;`, `return process.memoryUsage();`). The script runs inside an async function.
1499
+ - **Async code supported:** use `await` and return a Promise; the tool waits for it and returns the resolved value.
1477
1500
  - Executes in the NODE PROCESS CONTEXT (real Node.js environment):
1478
1501
  - Has access to process, require, global, and all loaded modules
1479
1502
  - Can read/modify process state
1480
1503
  - Full Node.js APIs (fs, http, etc.)
1481
- - Execution blocks the Node event loop until the script completes
1482
- - Long-running scripts will block the process; use short expressions
1504
+ - Execution blocks the Node event loop until the script (and any returned Promise) completes
1505
+ - Long-running scripts will block the process; use short scripts
1483
1506
  - Return value must be serializable
1484
1507
 
1485
1508
  **Typical use cases:**
1486
- - Inspect process state: `process.memoryUsage()`, `process.uptime()`
1487
- - Call loaded modules: `require('os').loadavg()`
1509
+ - Inspect process state: `return process.memoryUsage();`, `return process.uptime();`
1510
+ - Call loaded modules: `return require('os').loadavg();`
1488
1511
  - Read globals or cached data
1489
- - One-off diagnostic commands
1512
+ - Async: `return await someAsyncCall();`
1490
1513
  </details>
1491
1514
 
1492
1515
  ### Observability (O11Y) Tools
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{platformInfo}from"../core-M3RUMGR5.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-ARVKOWOZ.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools,DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));throw _debug("Tool call error:",errorBody),new Error(errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
2
+ import{platformInfo}from"../core-LM2SLZDP.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-247YVH6X.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools,DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));throw _debug("Tool call error:",errorBody),new Error(errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
3
3
  `).map(line=>`${prefix}${line}`).join(`
4
4
  `);if(typeof output=="number"||typeof output=="boolean")return`${prefix}${output}`;if(Array.isArray(output))return output.length===0?`${prefix}[]`:output.map(item=>_formatOutput(item,indent)).join(`
5
5
  `);if(typeof output=="object"){let lines=[];for(let[key,value]of Object.entries(output))value!==void 0&&(typeof value=="object"&&value!==null&&!Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):lines.push(`${prefix}${key}: ${value}`));return lines.join(`
@@ -1,4 +1,4 @@
1
- import path from"node:path";function _envStr(name){let v=process.env[name];if(!v)return;let t=v.trim();return t||void 0}function _envInt(name){let v=_envStr(name);if(!v)return;let n=Number(v);if(Number.isFinite(n))return Math.floor(n)}function _envBool(name){let v=_envStr(name);if(v)return v==="true"}function _parseKeyValueFromEnv(envValue){let headers={};if(!envValue)return headers;let pairs=envValue.split(",");for(let pair of pairs){let trimmed=pair.trim();if(!trimmed)continue;let eqIndex=trimmed.indexOf("=");if(eqIndex===-1)continue;let key=trimmed.slice(0,eqIndex).trim(),value=trimmed.slice(eqIndex+1).trim();!key||!value||(headers[key]=value)}return headers}var PORT=_envInt("PORT")??3e3,_PLATFORM_RAW=_envStr("PLATFORM"),PLATFORM=_PLATFORM_RAW==="browser"||_PLATFORM_RAW==="node"?_PLATFORM_RAW:"browser",SESSION_IDLE_SECONDS=_envInt("SESSION_IDLE_SECONDS")??300,SESSION_IDLE_CHECK_SECONDS=_envInt("SESSION_IDLE_CHECK_SECONDS")??30,SESSION_CLOSE_ON_SOCKET_CLOSE=_envBool("SESSION_CLOSE_ON_SOCKET_CLOSE")??!1,BROWSER_HEADLESS_ENABLE=_envBool("BROWSER_HEADLESS_ENABLE")??!0,BROWSER_PERSISTENT_ENABLE=_envBool("BROWSER_PERSISTENT_ENABLE")??!1,BROWSER_PERSISTENT_USER_DATA_DIR=_envStr("BROWSER_PERSISTENT_USER_DATA_DIR")??path.join(process.cwd(),"browser-devtools-mcp"),BROWSER_USE_INSTALLED_ON_SYSTEM=_envBool("BROWSER_USE_INSTALLED_ON_SYSTEM")??!1,BROWSER_EXECUTABLE_PATH=_envStr("BROWSER_EXECUTABLE_PATH"),BROWSER_LOCALE=_envStr("BROWSER_LOCALE"),BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE=_envInt("BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE")??1e3,BROWSER_HTTP_REQUESTS_BUFFER_SIZE=_envInt("BROWSER_HTTP_REQUESTS_BUFFER_SIZE")??1e3,NODE_CONSOLE_MESSAGES_BUFFER_SIZE=_envInt("NODE_CONSOLE_MESSAGES_BUFFER_SIZE")??1e3,NODE_INSPECTOR_HOST=_envStr("NODE_INSPECTOR_HOST"),OTEL_ENABLE=_envBool("OTEL_ENABLE")??!1,OTEL_SERVICE_NAME=_envStr("OTEL_SERVICE_NAME")??"frontend",OTEL_SERVICE_VERSION=_envStr("OTEL_SERVICE_VERSION"),OTEL_ASSETS_DIR=_envStr("OTEL_ASSETS_DIR"),OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS=_envStr("OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS")?.split(",")??["click"],OTEL_EXPORTER_TYPE=_envStr("OTEL_EXPORTER_TYPE")??"none",OTEL_EXPORTER_HTTP_URL=_envStr("OTEL_EXPORTER_HTTP_URL"),OTEL_EXPORTER_HTTP_HEADERS=_parseKeyValueFromEnv(_envStr("OTEL_EXPORTER_HTTP_HEADERS")),AWS_REGION=_envStr("AWS_REGION"),AWS_PROFILE=_envStr("AWS_PROFILE"),AMAZON_BEDROCK_ENABLE=_envBool("AMAZON_BEDROCK_ENABLE")??!1,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID=_envStr("AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID"),AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID=_envStr("AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID"),AMAZON_BEDROCK_VISION_MODEL_ID=_envStr("AMAZON_BEDROCK_VISION_MODEL_ID"),FIGMA_ACCESS_TOKEN=_envStr("FIGMA_ACCESS_TOKEN")??"",FIGMA_API_BASE_URL=_envStr("FIGMA_API_BASE_URL")??"https://api.figma.com/v1",DAEMON_PORT=_envInt("DAEMON_PORT")??2020,DAEMON_SESSION_IDLE_SECONDS=_envInt("DAEMON_SESSION_IDLE_SECONDS")??300,DAEMON_SESSION_IDLE_CHECK_SECONDS=_envInt("DAEMON_SESSION_IDLE_CHECK_SECONDS")??30;var ConsoleMessageLevelName=(ConsoleMessageLevelName2=>(ConsoleMessageLevelName2.ALL="all",ConsoleMessageLevelName2.DEBUG="debug",ConsoleMessageLevelName2.INFO="info",ConsoleMessageLevelName2.WARNING="warning",ConsoleMessageLevelName2.ERROR="error",ConsoleMessageLevelName2))(ConsoleMessageLevelName||{});var ConsoleMessageLevel={all:{code:-1},debug:{code:0},info:{code:1},warning:{code:2},error:{code:3}};var BANNER_TEXT="[BROWSER-DEVTOOLS]",enabled=!0,debugEnabled=process.env.DEBUG_ENABLE==="true";function _timeAsString(){return`${new Date().toLocaleTimeString(void 0,{hour:"numeric",minute:"numeric",second:"numeric",hour12:!1,timeZoneName:"short"})}`}function _normalizeArgs(...args){return isDebugEnabled()?args:(args||[]).map(arg=>arg?arg instanceof Error||arg.name&&arg.message&&arg.stack?`${arg.name}: ${arg.message}`:arg:"")}function enable(){enabled=!0}function disable(){enabled=!1}function isDebugEnabled(){return debugEnabled}function debug(...args){enabled&&isDebugEnabled()&&console.debug(BANNER_TEXT,_timeAsString(),"|","DEBUG","-",..._normalizeArgs(...args))}function info(...args){enabled&&console.info(BANNER_TEXT,_timeAsString(),"|","INFO ","-",..._normalizeArgs(...args))}function warn(...args){enabled&&console.warn(BANNER_TEXT,_timeAsString(),"|","WARN ","-",..._normalizeArgs(...args))}function error(...args){enabled&&console.error(BANNER_TEXT,_timeAsString(),"|","ERROR","-",..._normalizeArgs(...args))}function _getCircularReplacer(){let seen=new WeakSet;return(key,value)=>{if(typeof value=="object"&&value!==null){if(seen.has(value))return;seen.add(value)}return value}}function toJson(obj){return JSON.stringify(obj,_getCircularReplacer())}var BASE64_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",BASE64_MAP=new Map;for(let i=0;i<BASE64_CHARS.length;i++)BASE64_MAP.set(BASE64_CHARS[i],i);function _decodeVLQ(encoded){let values=[],shift=0,value=0;for(let char of encoded){let digit=BASE64_MAP.get(char);if(digit===void 0)throw new Error(`Invalid base64 character: ${char}`);let hasContinuation=(digit&32)!==0;if(value+=(digit&31)<<shift,hasContinuation)shift+=5;else{let isNegative=(value&1)!==0;value=value>>1,isNegative&&(value=-value),values.push(value),value=0,shift=0}}return values}function _parseMappings(mappings,sources){let entries=[],lines=mappings.split(";"),generatedLine=0,sourceIndex=0,originalLine=0,originalColumn=0,nameIndex=0;for(let line of lines){let generatedColumn=0,segments=line.split(",");for(let segment of segments){if(!segment)continue;let values=_decodeVLQ(segment);if(values.length===0)continue;generatedColumn+=values[0];let entry={generatedLine,generatedColumn};values.length>=4&&(sourceIndex+=values[1],originalLine+=values[2],originalColumn+=values[3],entry.source=sources[sourceIndex],entry.originalLine=originalLine,entry.originalColumn=originalColumn,values.length>=5&&(nameIndex+=values[4])),entries.push(entry)}generatedLine++}return entries}var SourceMapResolver=class{scripts=new Map;scriptsByUrl=new Map;sourceToScripts=new Map;fetchCache=new Map;page;customFetcher;constructor(source){typeof source=="function"?this.customFetcher=source:this.page=source}registerScript(script){let resolved={scriptId:script.scriptId,url:script.url,sourceMapURL:script.sourceMapURL,originalSources:[]};if(this.scripts.set(script.scriptId,resolved),script.url){let existing=this.scriptsByUrl.get(script.url)||[];existing.push(resolved),this.scriptsByUrl.set(script.url,existing)}}getScriptUrl(scriptId){return this.scripts.get(scriptId)?.url}async loadSourceMap(scriptId){let script=this.scripts.get(scriptId);if(!script||!script.sourceMapURL)return!1;if(script.sourceMap)return!0;try{let sourceMap=await this._fetchSourceMap(script.sourceMapURL,script.url);if(!sourceMap)return!1;script.sourceMap=sourceMap,script.mappings=_parseMappings(sourceMap.mappings,sourceMap.sources),script.originalSources=sourceMap.sources.map(s=>this._resolveSourcePath(s,sourceMap.sourceRoot,script.url));for(let source of script.originalSources){let existing=this.sourceToScripts.get(source)||[];existing.push(script),this.sourceToScripts.set(source,existing)}return!0}catch(error2){return debug(`Failed to load source map for ${script.url}:`,error2),!1}}async loadAllSourceMaps(){let loaded=0;for(let scriptId of this.scripts.keys())await this.loadSourceMap(scriptId)&&loaded++;return loaded}originalToGenerated(originalSource,line,column=0){let normalizedSource=this._normalizeSourcePath(originalSource),scripts=this._findScriptsForSource(normalizedSource);if(scripts.length!==0)for(let script of scripts){if(!script.mappings)continue;let sourceIndex=script.originalSources.findIndex(s=>this._normalizeSourcePath(s)===normalizedSource||s.endsWith(normalizedSource)||normalizedSource.endsWith(s));if(sourceIndex===-1)continue;let bestMatch,bestDistance=1/0;for(let mapping of script.mappings)if(mapping.source===script.sourceMap?.sources[sourceIndex]&&mapping.originalLine!==void 0&&mapping.originalLine===line){let colDist=mapping.originalColumn!==void 0?Math.abs(mapping.originalColumn-column):0;colDist<bestDistance&&(bestDistance=colDist,bestMatch=mapping)}if(bestMatch)return{scriptId:script.scriptId,location:{line:bestMatch.generatedLine,column:bestMatch.generatedColumn}}}}async resolveLocationByUrl(url,line,column=1){let line0=Math.max(0,line-1),column0=Math.max(0,column-1),candidates=this.scriptsByUrl.get(url)||[];if(candidates.length===0)for(let script of this.scripts.values())(script.url===url||script.url.endsWith(url)||url.endsWith(script.url))&&candidates.push(script);for(let script of candidates){if(!await this.loadSourceMap(script.scriptId)||!script.mappings)continue;let resolved=this.generatedToOriginal(script.scriptId,line0,column0);if(resolved)return resolved}return null}generatedToOriginal(scriptId,line,column=0){let script=this.scripts.get(scriptId);if(!script?.mappings)return;let bestMatch,bestDistance=1/0;for(let mapping of script.mappings){if(mapping.generatedLine!==line||mapping.source===void 0)continue;let colDist=Math.abs(mapping.generatedColumn-column);colDist<bestDistance&&(bestDistance=colDist,bestMatch=mapping)}if(bestMatch&&bestMatch.source!==void 0&&bestMatch.originalLine!==void 0){let sourceIndex=script.sourceMap?.sources.indexOf(bestMatch.source);return{source:sourceIndex!==void 0&&sourceIndex>=0?script.originalSources[sourceIndex]:bestMatch.source,line:bestMatch.originalLine,column:bestMatch.originalColumn??0,name:bestMatch.name}}}getOriginalSources(){return Array.from(this.sourceToScripts.keys())}findScriptsForSource(sourcePattern){let normalized=this._normalizeSourcePath(sourcePattern);return this._findScriptsForSource(normalized)}hasSourceMaps(){for(let script of this.scripts.values())if(script.sourceMap)return!0;return!1}clear(){this.scripts.clear(),this.scriptsByUrl.clear(),this.sourceToScripts.clear(),this.fetchCache.clear()}_findScriptsForSource(normalizedSource){let exact=this.sourceToScripts.get(normalizedSource);if(exact&&exact.length>0)return exact;let results=[];for(let[source,scripts]of this.sourceToScripts.entries()){let normalizedKey=this._normalizeSourcePath(source);(normalizedKey.endsWith(normalizedSource)||normalizedSource.endsWith(normalizedKey))&&results.push(...scripts)}return results}_normalizeSourcePath(path3){let normalized=path3.replace(/^webpack:\/\/[^/]*\//,"").replace(/^file:\/\//,"").replace(/^\.*\//,"");for(normalized=normalized.replace(/\\/g,"/");normalized.startsWith("./");)normalized=normalized.slice(2);return normalized}_resolveSourcePath(source,sourceRoot,scriptUrl){return source.startsWith("webpack://")||source.startsWith("http://")||source.startsWith("https://")||sourceRoot&&(source=sourceRoot.replace(/\/$/,"")+"/"+source),source}async _fetchSourceMap(sourceMapURL,scriptUrl){if(sourceMapURL.startsWith("data:"))return this._parseDataUrl(sourceMapURL);let absoluteUrl=this._resolveUrl(sourceMapURL,scriptUrl);if(this.fetchCache.has(absoluteUrl))return this.fetchCache.get(absoluteUrl)||null;try{let sourceMap=null;if(this.customFetcher){let content=await this.customFetcher(absoluteUrl,scriptUrl);content&&(sourceMap=JSON.parse(content))}else this.page&&(sourceMap=await this._fetchViaBrowser(absoluteUrl));return this.fetchCache.set(absoluteUrl,sourceMap),sourceMap}catch(error2){return debug(`Failed to fetch source map from ${absoluteUrl}:`,error2),this.fetchCache.set(absoluteUrl,null),null}}_parseDataUrl(dataUrl){try{let match=dataUrl.match(/^data:[^,]*(?:;base64)?,(.*)$/);if(!match)return null;let isBase64=dataUrl.includes(";base64,"),data=match[1],json=isBase64?Buffer.from(data,"base64").toString("utf-8"):decodeURIComponent(data);return JSON.parse(json)}catch{return null}}_resolveUrl(url,baseUrl){if(url.startsWith("http://")||url.startsWith("https://"))return url;try{return new URL(url,baseUrl).href}catch{return baseUrl.replace(/[^/]*$/,"")+url}}async _fetchViaBrowser(url){if(!this.page)return null;try{let result=await this.page.evaluate(async fetchUrl=>{try{let response=await fetch(fetchUrl);return response.ok?{data:await response.text()}:{error:`HTTP ${response.status}`}}catch(err){return{error:err.message||"Fetch failed"}}},url);return"error"in result?(debug(`Browser fetch error for ${url}: ${result.error}`),null):JSON.parse(result.data)}catch(error2){return debug(`Failed to fetch ${url} via browser:`,error2),null}}};var V8API_DEFAULT_OPTIONS={maxDepth:3,maxProperties:50,maxArrayLength:100,maxStringLength:1e3},V8Api=class{cdp=null;enabled=!1;scripts=new Map;scriptsByUrl=new Map;eventHandlers={};options;cdpProvider;constructor(source,options){if(this.options={...V8API_DEFAULT_OPTIONS,...options},typeof source=="function")this.cdpProvider=source;else if(source&&typeof source.context=="function"){let page=source;this.cdpProvider=async()=>await page.context().newCDPSession(page)}else if(source&&typeof source.send=="function"){let session=source;this.cdp=session,this.cdpProvider=async()=>session}else throw new Error("V8Api: Invalid source. Expected Page, CDPSessionProvider, or ICDPSession.")}getOptions(){return{...this.options}}isEnabled(){return this.enabled}async getCdp(){return this.cdp||(this.cdp=await this.cdpProvider()),this.cdp}on(event,handler){this.eventHandlers[event]=handler}off(event){delete this.eventHandlers[event]}async enable(){if(this.enabled)return;let cdp=await this.getCdp();cdp.on("Debugger.scriptParsed",event=>{let parsed=event;if(this.scripts.set(parsed.scriptId,parsed),parsed.url){let existing=this.scriptsByUrl.get(parsed.url)||[];existing.push(parsed),this.scriptsByUrl.set(parsed.url,existing)}this.eventHandlers.scriptParsed&&this.eventHandlers.scriptParsed(parsed)}),cdp.on("Debugger.paused",event=>{let pausedEvent=event;this.eventHandlers.paused&&this.eventHandlers.paused(pausedEvent)}),cdp.on("Debugger.resumed",()=>{this.eventHandlers.resumed&&this.eventHandlers.resumed()}),await cdp.send("Debugger.enable",{maxScriptsCacheSize:1e8}),await cdp.send("Runtime.enable"),this.enabled=!0}async disable(){if(!(!this.enabled||!this.cdp)){try{await this.cdp.send("Debugger.disable"),await this.cdp.send("Runtime.disable")}catch{}this.scripts.clear(),this.scriptsByUrl.clear(),this.enabled=!1}}async detach(){if(await this.disable(),this.cdp){try{await this.cdp.detach()}catch{}this.cdp=null}}getScripts(){return Array.from(this.scripts.values())}getScript(scriptId){return this.scripts.get(scriptId)}findScriptsByUrl(urlPattern){let regex=new RegExp(urlPattern),results=[];for(let script of this.scripts.values())script.url&&regex.test(script.url)&&results.push(script);return results}async setBreakpointByUrl(options){let cdp=await this.getCdp(),params={lineNumber:options.lineNumber};options.urlRegex?params.urlRegex=options.urlRegex:options.url&&(params.url=options.url),options.columnNumber!==void 0&&(params.columnNumber=options.columnNumber),options.condition&&(params.condition=options.condition);let result=await cdp.send("Debugger.setBreakpointByUrl",params);return{breakpointId:result.breakpointId,locations:result.locations||[]}}async setBreakpoint(location,condition){let cdp=await this.getCdp(),params={location:{scriptId:location.scriptId,lineNumber:location.lineNumber,columnNumber:location.columnNumber}};condition&&(params.condition=condition);let result=await cdp.send("Debugger.setBreakpoint",params);return{breakpointId:result.breakpointId,actualLocation:result.actualLocation}}async removeBreakpoint(breakpointId){await(await this.getCdp()).send("Debugger.removeBreakpoint",{breakpointId})}async resume(){await(await this.getCdp()).send("Debugger.resume")}async stepOver(){await(await this.getCdp()).send("Debugger.stepOver")}async stepInto(){await(await this.getCdp()).send("Debugger.stepInto")}async stepOut(){await(await this.getCdp()).send("Debugger.stepOut")}async pause(){await(await this.getCdp()).send("Debugger.pause")}async setPauseOnExceptions(state){await(await this.getCdp()).send("Debugger.setPauseOnExceptions",{state})}async evaluateOnCallFrame(callFrameId,expression,options){let result=await(await this.getCdp()).send("Debugger.evaluateOnCallFrame",{callFrameId,expression,returnByValue:options?.returnByValue??!0,generatePreview:options?.generatePreview??!0,throwOnSideEffect:options?.throwOnSideEffect??!1});return{result:result.result,exceptionDetails:result.exceptionDetails}}async getProperties(objectId,options){let result=await(await this.getCdp()).send("Runtime.getProperties",{objectId,ownProperties:options?.ownProperties??!0,accessorPropertiesOnly:options?.accessorPropertiesOnly??!1,generatePreview:options?.generatePreview??!0});return{result:result.result||[],internalProperties:result.internalProperties,privateProperties:result.privateProperties}}async getScopeVariables(scope,options){if(!scope.object.objectId)return{};let opts={maxDepth:options?.maxDepth??this.options.maxDepth,maxProperties:options?.maxProperties??this.options.maxProperties,maxArrayLength:options?.maxArrayLength??this.options.maxArrayLength},{result}=await this.getProperties(scope.object.objectId,{ownProperties:!0,generatePreview:!0}),variables={},visited=new Set,propCount=0;for(let prop of result){if(propCount>=opts.maxProperties){variables.__truncated__=`${result.length-propCount} more properties`;break}prop.value&&(variables[prop.name]=await this.extractValueDeep(prop.value,opts.maxDepth,opts.maxProperties,opts.maxArrayLength,visited),propCount++)}return variables}extractValue(obj){return this._extractValueShallow(obj)}async extractValueDeep(obj,maxDepth,maxProperties,maxArrayLength,visited=new Set){let depth=maxDepth??this.options.maxDepth,props=maxProperties??this.options.maxProperties,arrLen=maxArrayLength??this.options.maxArrayLength;if(obj.value!==void 0)return typeof obj.value=="string"&&obj.value.length>this.options.maxStringLength?obj.value.slice(0,this.options.maxStringLength)+`... (${obj.value.length} chars)`:obj.value;if(obj.unserializableValue)return obj.unserializableValue==="NaN"?NaN:obj.unserializableValue==="Infinity"?1/0:obj.unserializableValue==="-Infinity"?-1/0:obj.unserializableValue==="-0"?-0:obj.unserializableValue.endsWith("n")?`BigInt(${obj.unserializableValue.slice(0,-1)})`:obj.unserializableValue;if(obj.subtype==="null")return null;if(obj.type!=="undefined"){if(obj.type==="function")return`[function${obj.description?`: ${obj.description.split(`
1
+ import path from"node:path";function _envStr(name){let v=process.env[name];if(!v)return;let t=v.trim();return t||void 0}function _envInt(name){let v=_envStr(name);if(!v)return;let n=Number(v);if(Number.isFinite(n))return Math.floor(n)}function _envBool(name){let v=_envStr(name);if(v)return v==="true"}function _parseKeyValueFromEnv(envValue){let headers={};if(!envValue)return headers;let pairs=envValue.split(",");for(let pair of pairs){let trimmed=pair.trim();if(!trimmed)continue;let eqIndex=trimmed.indexOf("=");if(eqIndex===-1)continue;let key=trimmed.slice(0,eqIndex).trim(),value=trimmed.slice(eqIndex+1).trim();!key||!value||(headers[key]=value)}return headers}var PORT=_envInt("PORT")??3e3,TOOL_OUTPUT_SCHEMA_DISABLE=_envBool("TOOL_OUTPUT_SCHEMA_DISABLE")??!1,AVAILABLE_TOOL_DOMAINS=(()=>{let v=_envStr("AVAILABLE_TOOL_DOMAINS");if(!v)return;let set=new Set(v.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean));return set.size>0?set:void 0})(),_PLATFORM_RAW=_envStr("PLATFORM"),PLATFORM=_PLATFORM_RAW==="browser"||_PLATFORM_RAW==="node"?_PLATFORM_RAW:"browser",SESSION_IDLE_SECONDS=_envInt("SESSION_IDLE_SECONDS")??300,SESSION_IDLE_CHECK_SECONDS=_envInt("SESSION_IDLE_CHECK_SECONDS")??30,SESSION_CLOSE_ON_SOCKET_CLOSE=_envBool("SESSION_CLOSE_ON_SOCKET_CLOSE")??!1,BROWSER_HEADLESS_ENABLE=_envBool("BROWSER_HEADLESS_ENABLE")??!0,BROWSER_PERSISTENT_ENABLE=_envBool("BROWSER_PERSISTENT_ENABLE")??!1,BROWSER_PERSISTENT_USER_DATA_DIR=_envStr("BROWSER_PERSISTENT_USER_DATA_DIR")??path.join(process.cwd(),"browser-devtools-mcp"),BROWSER_USE_INSTALLED_ON_SYSTEM=_envBool("BROWSER_USE_INSTALLED_ON_SYSTEM")??!1,BROWSER_EXECUTABLE_PATH=_envStr("BROWSER_EXECUTABLE_PATH"),BROWSER_LOCALE=_envStr("BROWSER_LOCALE"),BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE=_envInt("BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE")??1e3,BROWSER_HTTP_REQUESTS_BUFFER_SIZE=_envInt("BROWSER_HTTP_REQUESTS_BUFFER_SIZE")??1e3,NODE_CONSOLE_MESSAGES_BUFFER_SIZE=_envInt("NODE_CONSOLE_MESSAGES_BUFFER_SIZE")??1e3,NODE_INSPECTOR_HOST=_envStr("NODE_INSPECTOR_HOST"),OTEL_ENABLE=_envBool("OTEL_ENABLE")??!1,OTEL_SERVICE_NAME=_envStr("OTEL_SERVICE_NAME")??"frontend",OTEL_SERVICE_VERSION=_envStr("OTEL_SERVICE_VERSION"),OTEL_ASSETS_DIR=_envStr("OTEL_ASSETS_DIR"),OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS=_envStr("OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS")?.split(",")??["click"],OTEL_EXPORTER_TYPE=_envStr("OTEL_EXPORTER_TYPE")??"none",OTEL_EXPORTER_HTTP_URL=_envStr("OTEL_EXPORTER_HTTP_URL"),OTEL_EXPORTER_HTTP_HEADERS=_parseKeyValueFromEnv(_envStr("OTEL_EXPORTER_HTTP_HEADERS")),AWS_REGION=_envStr("AWS_REGION"),AWS_PROFILE=_envStr("AWS_PROFILE"),AMAZON_BEDROCK_ENABLE=_envBool("AMAZON_BEDROCK_ENABLE")??!1,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID=_envStr("AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID"),AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID=_envStr("AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID"),AMAZON_BEDROCK_VISION_MODEL_ID=_envStr("AMAZON_BEDROCK_VISION_MODEL_ID"),FIGMA_ACCESS_TOKEN=_envStr("FIGMA_ACCESS_TOKEN")??"",FIGMA_API_BASE_URL=_envStr("FIGMA_API_BASE_URL")??"https://api.figma.com/v1",DAEMON_PORT=_envInt("DAEMON_PORT")??2020,DAEMON_SESSION_IDLE_SECONDS=_envInt("DAEMON_SESSION_IDLE_SECONDS")??300,DAEMON_SESSION_IDLE_CHECK_SECONDS=_envInt("DAEMON_SESSION_IDLE_CHECK_SECONDS")??30;var ConsoleMessageLevelName=(ConsoleMessageLevelName2=>(ConsoleMessageLevelName2.ALL="all",ConsoleMessageLevelName2.DEBUG="debug",ConsoleMessageLevelName2.INFO="info",ConsoleMessageLevelName2.WARNING="warning",ConsoleMessageLevelName2.ERROR="error",ConsoleMessageLevelName2))(ConsoleMessageLevelName||{});var ConsoleMessageLevel={all:{code:-1},debug:{code:0},info:{code:1},warning:{code:2},error:{code:3}};var BANNER_TEXT="[BROWSER-DEVTOOLS]",enabled=!0,debugEnabled=process.env.DEBUG_ENABLE==="true";function _timeAsString(){return`${new Date().toLocaleTimeString(void 0,{hour:"numeric",minute:"numeric",second:"numeric",hour12:!1,timeZoneName:"short"})}`}function _normalizeArgs(...args){return isDebugEnabled()?args:(args||[]).map(arg=>arg?arg instanceof Error||arg.name&&arg.message&&arg.stack?`${arg.name}: ${arg.message}`:arg:"")}function enable(){enabled=!0}function disable(){enabled=!1}function isDebugEnabled(){return debugEnabled}function debug(...args){enabled&&isDebugEnabled()&&console.debug(BANNER_TEXT,_timeAsString(),"|","DEBUG","-",..._normalizeArgs(...args))}function info(...args){enabled&&console.info(BANNER_TEXT,_timeAsString(),"|","INFO ","-",..._normalizeArgs(...args))}function warn(...args){enabled&&console.warn(BANNER_TEXT,_timeAsString(),"|","WARN ","-",..._normalizeArgs(...args))}function error(...args){enabled&&console.error(BANNER_TEXT,_timeAsString(),"|","ERROR","-",..._normalizeArgs(...args))}function _getCircularReplacer(){let seen=new WeakSet;return(key,value)=>{if(typeof value=="object"&&value!==null){if(seen.has(value))return;seen.add(value)}return value}}function toJson(obj){return JSON.stringify(obj,_getCircularReplacer())}var BASE64_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",BASE64_MAP=new Map;for(let i=0;i<BASE64_CHARS.length;i++)BASE64_MAP.set(BASE64_CHARS[i],i);function _decodeVLQ(encoded){let values=[],shift=0,value=0;for(let char of encoded){let digit=BASE64_MAP.get(char);if(digit===void 0)throw new Error(`Invalid base64 character: ${char}`);let hasContinuation=(digit&32)!==0;if(value+=(digit&31)<<shift,hasContinuation)shift+=5;else{let isNegative=(value&1)!==0;value=value>>1,isNegative&&(value=-value),values.push(value),value=0,shift=0}}return values}function _parseMappings(mappings,sources){let entries=[],lines=mappings.split(";"),generatedLine=0,sourceIndex=0,originalLine=0,originalColumn=0,nameIndex=0;for(let line of lines){let generatedColumn=0,segments=line.split(",");for(let segment of segments){if(!segment)continue;let values=_decodeVLQ(segment);if(values.length===0)continue;generatedColumn+=values[0];let entry={generatedLine,generatedColumn};values.length>=4&&(sourceIndex+=values[1],originalLine+=values[2],originalColumn+=values[3],entry.source=sources[sourceIndex],entry.originalLine=originalLine,entry.originalColumn=originalColumn,values.length>=5&&(nameIndex+=values[4])),entries.push(entry)}generatedLine++}return entries}var SourceMapResolver=class{scripts=new Map;scriptsByUrl=new Map;sourceToScripts=new Map;fetchCache=new Map;page;customFetcher;constructor(source){typeof source=="function"?this.customFetcher=source:this.page=source}registerScript(script){let resolved={scriptId:script.scriptId,url:script.url,sourceMapURL:script.sourceMapURL,originalSources:[]};if(this.scripts.set(script.scriptId,resolved),script.url){let existing=this.scriptsByUrl.get(script.url)||[];existing.push(resolved),this.scriptsByUrl.set(script.url,existing)}}getScriptUrl(scriptId){return this.scripts.get(scriptId)?.url}async loadSourceMap(scriptId){let script=this.scripts.get(scriptId);if(!script||!script.sourceMapURL)return!1;if(script.sourceMap)return!0;try{let sourceMap=await this._fetchSourceMap(script.sourceMapURL,script.url);if(!sourceMap)return!1;script.sourceMap=sourceMap,script.mappings=_parseMappings(sourceMap.mappings,sourceMap.sources),script.originalSources=sourceMap.sources.map(s=>this._resolveSourcePath(s,sourceMap.sourceRoot,script.url));for(let source of script.originalSources){let existing=this.sourceToScripts.get(source)||[];existing.push(script),this.sourceToScripts.set(source,existing)}return!0}catch(error2){return debug(`Failed to load source map for ${script.url}:`,error2),!1}}async loadAllSourceMaps(){let loaded=0;for(let scriptId of this.scripts.keys())await this.loadSourceMap(scriptId)&&loaded++;return loaded}originalToGenerated(originalSource,line,column=0){let normalizedSource=this._normalizeSourcePath(originalSource),scripts=this._findScriptsForSource(normalizedSource);if(scripts.length!==0)for(let script of scripts){if(!script.mappings)continue;let sourceIndex=script.originalSources.findIndex(s=>this._normalizeSourcePath(s)===normalizedSource||s.endsWith(normalizedSource)||normalizedSource.endsWith(s));if(sourceIndex===-1)continue;let bestMatch,bestDistance=1/0;for(let mapping of script.mappings)if(mapping.source===script.sourceMap?.sources[sourceIndex]&&mapping.originalLine!==void 0&&mapping.originalLine===line){let colDist=mapping.originalColumn!==void 0?Math.abs(mapping.originalColumn-column):0;colDist<bestDistance&&(bestDistance=colDist,bestMatch=mapping)}if(bestMatch)return{scriptId:script.scriptId,location:{line:bestMatch.generatedLine,column:bestMatch.generatedColumn}}}}async resolveLocationByUrl(url,line,column=1){let line0=Math.max(0,line-1),column0=Math.max(0,column-1),candidates=this.scriptsByUrl.get(url)||[];if(candidates.length===0)for(let script of this.scripts.values())(script.url===url||script.url.endsWith(url)||url.endsWith(script.url))&&candidates.push(script);for(let script of candidates){if(!await this.loadSourceMap(script.scriptId)||!script.mappings)continue;let resolved=this.generatedToOriginal(script.scriptId,line0,column0);if(resolved)return resolved}return null}generatedToOriginal(scriptId,line,column=0){let script=this.scripts.get(scriptId);if(!script?.mappings)return;let bestMatch,bestDistance=1/0;for(let mapping of script.mappings){if(mapping.generatedLine!==line||mapping.source===void 0)continue;let colDist=Math.abs(mapping.generatedColumn-column);colDist<bestDistance&&(bestDistance=colDist,bestMatch=mapping)}if(bestMatch&&bestMatch.source!==void 0&&bestMatch.originalLine!==void 0){let sourceIndex=script.sourceMap?.sources.indexOf(bestMatch.source);return{source:sourceIndex!==void 0&&sourceIndex>=0?script.originalSources[sourceIndex]:bestMatch.source,line:bestMatch.originalLine,column:bestMatch.originalColumn??0,name:bestMatch.name}}}getOriginalSources(){return Array.from(this.sourceToScripts.keys())}findScriptsForSource(sourcePattern){let normalized=this._normalizeSourcePath(sourcePattern);return this._findScriptsForSource(normalized)}hasSourceMaps(){for(let script of this.scripts.values())if(script.sourceMap)return!0;return!1}clear(){this.scripts.clear(),this.scriptsByUrl.clear(),this.sourceToScripts.clear(),this.fetchCache.clear()}_findScriptsForSource(normalizedSource){let exact=this.sourceToScripts.get(normalizedSource);if(exact&&exact.length>0)return exact;let results=[];for(let[source,scripts]of this.sourceToScripts.entries()){let normalizedKey=this._normalizeSourcePath(source);(normalizedKey.endsWith(normalizedSource)||normalizedSource.endsWith(normalizedKey))&&results.push(...scripts)}return results}_normalizeSourcePath(path3){let normalized=path3.replace(/^webpack:\/\/[^/]*\//,"").replace(/^file:\/\//,"").replace(/^\.*\//,"");for(normalized=normalized.replace(/\\/g,"/");normalized.startsWith("./");)normalized=normalized.slice(2);return normalized}_resolveSourcePath(source,sourceRoot,scriptUrl){return source.startsWith("webpack://")||source.startsWith("http://")||source.startsWith("https://")||sourceRoot&&(source=sourceRoot.replace(/\/$/,"")+"/"+source),source}async _fetchSourceMap(sourceMapURL,scriptUrl){if(sourceMapURL.startsWith("data:"))return this._parseDataUrl(sourceMapURL);let absoluteUrl=this._resolveUrl(sourceMapURL,scriptUrl);if(this.fetchCache.has(absoluteUrl))return this.fetchCache.get(absoluteUrl)||null;try{let sourceMap=null;if(this.customFetcher){let content=await this.customFetcher(absoluteUrl,scriptUrl);content&&(sourceMap=JSON.parse(content))}else this.page&&(sourceMap=await this._fetchViaBrowser(absoluteUrl));return this.fetchCache.set(absoluteUrl,sourceMap),sourceMap}catch(error2){return debug(`Failed to fetch source map from ${absoluteUrl}:`,error2),this.fetchCache.set(absoluteUrl,null),null}}_parseDataUrl(dataUrl){try{let match=dataUrl.match(/^data:[^,]*(?:;base64)?,(.*)$/);if(!match)return null;let isBase64=dataUrl.includes(";base64,"),data=match[1],json=isBase64?Buffer.from(data,"base64").toString("utf-8"):decodeURIComponent(data);return JSON.parse(json)}catch{return null}}_resolveUrl(url,baseUrl){if(url.startsWith("http://")||url.startsWith("https://"))return url;try{return new URL(url,baseUrl).href}catch{return baseUrl.replace(/[^/]*$/,"")+url}}async _fetchViaBrowser(url){if(!this.page)return null;try{let result=await this.page.evaluate(async fetchUrl=>{try{let response=await fetch(fetchUrl);return response.ok?{data:await response.text()}:{error:`HTTP ${response.status}`}}catch(err){return{error:err.message||"Fetch failed"}}},url);return"error"in result?(debug(`Browser fetch error for ${url}: ${result.error}`),null):JSON.parse(result.data)}catch(error2){return debug(`Failed to fetch ${url} via browser:`,error2),null}}};var V8API_DEFAULT_OPTIONS={maxDepth:3,maxProperties:50,maxArrayLength:100,maxStringLength:1e3},V8Api=class{cdp=null;enabled=!1;scripts=new Map;scriptsByUrl=new Map;eventHandlers={};options;cdpProvider;constructor(source,options){if(this.options={...V8API_DEFAULT_OPTIONS,...options},typeof source=="function")this.cdpProvider=source;else if(source&&typeof source.context=="function"){let page=source;this.cdpProvider=async()=>await page.context().newCDPSession(page)}else if(source&&typeof source.send=="function"){let session=source;this.cdp=session,this.cdpProvider=async()=>session}else throw new Error("V8Api: Invalid source. Expected Page, CDPSessionProvider, or ICDPSession.")}getOptions(){return{...this.options}}isEnabled(){return this.enabled}async getCdp(){return this.cdp||(this.cdp=await this.cdpProvider()),this.cdp}on(event,handler){this.eventHandlers[event]=handler}off(event){delete this.eventHandlers[event]}async enable(){if(this.enabled)return;let cdp=await this.getCdp();cdp.on("Debugger.scriptParsed",event=>{let parsed=event;if(this.scripts.set(parsed.scriptId,parsed),parsed.url){let existing=this.scriptsByUrl.get(parsed.url)||[];existing.push(parsed),this.scriptsByUrl.set(parsed.url,existing)}this.eventHandlers.scriptParsed&&this.eventHandlers.scriptParsed(parsed)}),cdp.on("Debugger.paused",event=>{let pausedEvent=event;this.eventHandlers.paused&&this.eventHandlers.paused(pausedEvent)}),cdp.on("Debugger.resumed",()=>{this.eventHandlers.resumed&&this.eventHandlers.resumed()}),await cdp.send("Debugger.enable",{maxScriptsCacheSize:1e8}),await cdp.send("Runtime.enable"),this.enabled=!0}async disable(){if(!(!this.enabled||!this.cdp)){try{await this.cdp.send("Debugger.disable"),await this.cdp.send("Runtime.disable")}catch{}this.scripts.clear(),this.scriptsByUrl.clear(),this.enabled=!1}}async detach(){if(await this.disable(),this.cdp){try{await this.cdp.detach()}catch{}this.cdp=null}}getScripts(){return Array.from(this.scripts.values())}getScript(scriptId){return this.scripts.get(scriptId)}findScriptsByUrl(urlPattern){let regex=new RegExp(urlPattern),results=[];for(let script of this.scripts.values())script.url&&regex.test(script.url)&&results.push(script);return results}async setBreakpointByUrl(options){let cdp=await this.getCdp(),params={lineNumber:options.lineNumber};options.urlRegex?params.urlRegex=options.urlRegex:options.url&&(params.url=options.url),options.columnNumber!==void 0&&(params.columnNumber=options.columnNumber),options.condition&&(params.condition=options.condition);let result=await cdp.send("Debugger.setBreakpointByUrl",params);return{breakpointId:result.breakpointId,locations:result.locations||[]}}async setBreakpoint(location,condition){let cdp=await this.getCdp(),params={location:{scriptId:location.scriptId,lineNumber:location.lineNumber,columnNumber:location.columnNumber}};condition&&(params.condition=condition);let result=await cdp.send("Debugger.setBreakpoint",params);return{breakpointId:result.breakpointId,actualLocation:result.actualLocation}}async removeBreakpoint(breakpointId){await(await this.getCdp()).send("Debugger.removeBreakpoint",{breakpointId})}async resume(){await(await this.getCdp()).send("Debugger.resume")}async stepOver(){await(await this.getCdp()).send("Debugger.stepOver")}async stepInto(){await(await this.getCdp()).send("Debugger.stepInto")}async stepOut(){await(await this.getCdp()).send("Debugger.stepOut")}async pause(){await(await this.getCdp()).send("Debugger.pause")}async setPauseOnExceptions(state){await(await this.getCdp()).send("Debugger.setPauseOnExceptions",{state})}async evaluateOnCallFrame(callFrameId,expression,options){let result=await(await this.getCdp()).send("Debugger.evaluateOnCallFrame",{callFrameId,expression,returnByValue:options?.returnByValue??!0,generatePreview:options?.generatePreview??!0,throwOnSideEffect:options?.throwOnSideEffect??!1});return{result:result.result,exceptionDetails:result.exceptionDetails}}async getProperties(objectId,options){let result=await(await this.getCdp()).send("Runtime.getProperties",{objectId,ownProperties:options?.ownProperties??!0,accessorPropertiesOnly:options?.accessorPropertiesOnly??!1,generatePreview:options?.generatePreview??!0});return{result:result.result||[],internalProperties:result.internalProperties,privateProperties:result.privateProperties}}async getScopeVariables(scope,options){if(!scope.object.objectId)return{};let opts={maxDepth:options?.maxDepth??this.options.maxDepth,maxProperties:options?.maxProperties??this.options.maxProperties,maxArrayLength:options?.maxArrayLength??this.options.maxArrayLength},{result}=await this.getProperties(scope.object.objectId,{ownProperties:!0,generatePreview:!0}),variables={},visited=new Set,propCount=0;for(let prop of result){if(propCount>=opts.maxProperties){variables.__truncated__=`${result.length-propCount} more properties`;break}prop.value&&(variables[prop.name]=await this.extractValueDeep(prop.value,opts.maxDepth,opts.maxProperties,opts.maxArrayLength,visited),propCount++)}return variables}extractValue(obj){return this._extractValueShallow(obj)}async extractValueDeep(obj,maxDepth,maxProperties,maxArrayLength,visited=new Set){let depth=maxDepth??this.options.maxDepth,props=maxProperties??this.options.maxProperties,arrLen=maxArrayLength??this.options.maxArrayLength;if(obj.value!==void 0)return typeof obj.value=="string"&&obj.value.length>this.options.maxStringLength?obj.value.slice(0,this.options.maxStringLength)+`... (${obj.value.length} chars)`:obj.value;if(obj.unserializableValue)return obj.unserializableValue==="NaN"?NaN:obj.unserializableValue==="Infinity"?1/0:obj.unserializableValue==="-Infinity"?-1/0:obj.unserializableValue==="-0"?-0:obj.unserializableValue.endsWith("n")?`BigInt(${obj.unserializableValue.slice(0,-1)})`:obj.unserializableValue;if(obj.subtype==="null")return null;if(obj.type!=="undefined"){if(obj.type==="function")return`[function${obj.description?`: ${obj.description.split(`
2
2
  `)[0]}`:""}]`;if(!obj.objectId)return this._extractValueShallow(obj);if(depth<=0)return this._getShallowDescription(obj);if(visited.has(obj.objectId))return"[Circular]";visited.add(obj.objectId);try{return obj.subtype==="array"?await this._extractArray(obj.objectId,depth-1,props,arrLen,visited):obj.subtype==="map"?await this._extractMap(obj.objectId,depth-1,props,arrLen,visited):obj.subtype==="set"?await this._extractSet(obj.objectId,depth-1,arrLen,visited):obj.subtype==="date"?obj.description||"[Date]":obj.subtype==="regexp"?obj.description||"[RegExp]":obj.subtype==="error"?obj.description||"[Error]":obj.subtype==="promise"?`[Promise: ${obj.description||"pending"}]`:obj.subtype==="node"||obj.className?.includes("Element")||obj.className?.includes("Node")?`[DOM: ${obj.description||obj.className||"Node"}]`:obj.type==="object"?await this._extractObject(obj.objectId,depth-1,props,arrLen,visited,obj.className):this._extractValueShallow(obj)}catch{return this._getShallowDescription(obj)}}}async _extractArray(objectId,maxDepth,maxProperties,maxArrayLength,visited){let{result}=await this.getProperties(objectId,{ownProperties:!0,generatePreview:!0}),arr=[],length=0;for(let prop of result)if(prop.name==="length"&&prop.value?.value!==void 0){length=prop.value.value;break}for(let prop of result){let index=parseInt(prop.name,10);if(!(isNaN(index)||index<0)){if(arr.length>=maxArrayLength)break;prop.value&&(arr[index]=await this.extractValueDeep(prop.value,maxDepth,maxProperties,maxArrayLength,visited))}}return length>maxArrayLength&&(arr.__truncated__=`${length-maxArrayLength} more items`),arr}async _extractMap(objectId,maxDepth,maxProperties,maxArrayLength,visited){let{internalProperties}=await this.getProperties(objectId,{ownProperties:!0,generatePreview:!0}),mapObj={__type__:"Map"},entries=[],entriesProp=internalProperties?.find(p=>p.name==="[[Entries]]");if(entriesProp?.value?.objectId){let{result:entryResults}=await this.getProperties(entriesProp.value.objectId,{ownProperties:!0,generatePreview:!0}),count=0;for(let entry of entryResults){if(count>=maxArrayLength)break;let index=parseInt(entry.name,10);if(isNaN(index)||!entry.value?.objectId)continue;let{result:kvResult}=await this.getProperties(entry.value.objectId,{ownProperties:!0,generatePreview:!0}),keyProp=kvResult.find(p=>p.name==="key"),valueProp=kvResult.find(p=>p.name==="value");if(keyProp?.value&&valueProp?.value){let key=await this.extractValueDeep(keyProp.value,maxDepth,maxProperties,maxArrayLength,visited),value=await this.extractValueDeep(valueProp.value,maxDepth,maxProperties,maxArrayLength,visited);entries.push([key,value])}count++}}return mapObj.entries=entries,mapObj}async _extractSet(objectId,maxDepth,maxArrayLength,visited){let{internalProperties}=await this.getProperties(objectId,{ownProperties:!0,generatePreview:!0}),setObj={__type__:"Set"},values=[],entriesProp=internalProperties?.find(p=>p.name==="[[Entries]]");if(entriesProp?.value?.objectId){let{result:entryResults}=await this.getProperties(entriesProp.value.objectId,{ownProperties:!0,generatePreview:!0}),count=0;for(let entry of entryResults){if(count>=maxArrayLength)break;let index=parseInt(entry.name,10);isNaN(index)||(entry.value&&values.push(await this.extractValueDeep(entry.value,maxDepth,50,maxArrayLength,visited)),count++)}}return setObj.values=values,setObj}async _extractObject(objectId,maxDepth,maxProperties,maxArrayLength,visited,className){let{result}=await this.getProperties(objectId,{ownProperties:!0,generatePreview:!0}),obj={};className&&className!=="Object"&&(obj.__class__=className);let count=0;for(let prop of result){if(count>=maxProperties){obj.__truncated__=`${result.length-count} more properties`;break}prop.isOwn&&(prop.name.startsWith("__")&&prop.name.endsWith("__")||prop.value&&(obj[prop.name]=await this.extractValueDeep(prop.value,maxDepth,maxProperties,maxArrayLength,visited),count++))}return obj}_extractValueShallow(obj){if(obj.value!==void 0)return obj.value;if(obj.unserializableValue)return obj.unserializableValue==="NaN"?NaN:obj.unserializableValue==="Infinity"?1/0:obj.unserializableValue==="-Infinity"?-1/0:obj.unserializableValue==="-0"?-0:obj.unserializableValue.endsWith("n")?`BigInt(${obj.unserializableValue.slice(0,-1)})`:obj.unserializableValue;if(obj.subtype==="null")return null;if(obj.type!=="undefined")return this._getShallowDescription(obj)}_getShallowDescription(obj){if(obj.description){let desc=obj.description.split(`
3
3
  `)[0];return desc.length>100?`[${obj.type}${obj.subtype?`:${obj.subtype}`:""}] ${desc.slice(0,100)}...`:`[${obj.type}${obj.subtype?`:${obj.subtype}`:""}] ${desc}`}return`[${obj.type}${obj.subtype?`:${obj.subtype}`:""}]`}async getScriptSource(scriptId){return await(await this.getCdp()).send("Debugger.getScriptSource",{scriptId})}async setScriptSource(scriptId,scriptSource,dryRun){let result=await(await this.getCdp()).send("Debugger.setScriptSource",{scriptId,scriptSource,dryRun:dryRun??!1});return{callFrames:result.callFrames,stackChanged:result.stackChanged,asyncStackTrace:result.asyncStackTrace,exceptionDetails:result.exceptionDetails}}};import*as fs from"node:fs";import*as http from"node:http";import*as https from"node:https";import*as path2 from"node:path";var ProbeKind=(ProbeKind2=>(ProbeKind2.TRACEPOINT="tracepoint",ProbeKind2.LOGPOINT="logpoint",ProbeKind2))(ProbeKind||{}),DEFAULT_NODE_DEBUG_CONFIG={maxSnapshots:1e3,maxCallStackDepth:20,maxFramesWithScopes:5,maxAsyncStackSegments:10,maxFramesPerAsyncSegment:10},STORES=new Map;function _generateId(){let t=Date.now(),r=Math.floor(Math.random()*1e6);return`${t.toString(36)}-${r.toString(36)}`}function _locationKey(urlPattern,lineNumber,columnNumber){return`${urlPattern}:${lineNumber}:${columnNumber}`}function _cdpConsoleTypeToLevelName(type){switch(type){case"error":return"error";case"warning":case"warn":return"warning";case"info":return"info";case"debug":return"debug";default:return"info"}}function _cdpArgsToText(args){return!args||args.length===0?"":args.map(a=>a.type==="string"&&a.value!==void 0||a.value!==void 0?String(a.value):a.description?a.description:"[object]").join(" ")}function _evaluateHitCondition(hitCondition,hitCount){try{let condition=hitCondition.trim();return/^[=<>!%]/.test(condition)&&(condition=`hitCount ${condition}`),!!new Function("hitCount",`return (${condition});`)(hitCount)}catch{return!1}}async function _getOtelTraceContext(session){let expression=`(function(){
4
4
  try {
@@ -10,4 +10,6 @@ try {
10
10
  } catch (e) {
11
11
  return null;
12
12
  }
13
- })()`;try{let result=await session.send("Runtime.evaluate",{expression,returnByValue:!0,timeout:500});if(result.exceptionDetails)return null;let value=result.result?.value;if(value&&typeof value.traceId=="string"&&typeof value.spanId=="string")return{traceId:value.traceId,spanId:value.spanId}}catch{}return null}function _createNodeSourceMapFetcher(){return async(url,baseUrl)=>{let filePath=null;if(url.startsWith("file://"))filePath=url.replace("file://","");else if(url.startsWith("/")||url.match(/^[a-zA-Z]:\\/))filePath=url;else if(baseUrl.startsWith("file://")){let baseDir=path2.dirname(baseUrl.replace("file://",""));filePath=path2.resolve(baseDir,url)}else if(!url.startsWith("http://")&&!url.startsWith("https://")){let baseDir=path2.dirname(baseUrl.replace(/^file:\/\//,""));filePath=path2.resolve(baseDir,url)}if(filePath)try{return fs.readFileSync(filePath,"utf-8")}catch{}return url.startsWith("http://")||url.startsWith("https://")?new Promise(resolve2=>{let req=(url.startsWith("https://")?https:http).get(url,res=>{if(res.statusCode!==200){resolve2(null);return}let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>resolve2(data))});req.on("error",()=>resolve2(null)),req.setTimeout(5e3,()=>{req.destroy(),resolve2(null)})}):null}}async function _captureScopes(v8Api,callFrame,maxDepth=3){let scopeSnapshots=[];for(let scope of callFrame.scopeChain)if(!(scope.type==="global"||scope.type==="script"||scope.type==="with"||scope.type==="eval"||scope.type==="wasm-expression-stack")){if(scopeSnapshots.length>=maxDepth)break;try{let variables=await v8Api.getScopeVariables(scope),scopeVariables=[];for(let[name,value]of Object.entries(variables))scopeVariables.push({name,value,type:typeof value});scopeSnapshots.push({type:scope.type,name:scope.name,variables:scopeVariables})}catch{}}return scopeSnapshots}async function _callFrameToSnapshot(v8Api,frame,captureScopes,sourceMapResolver){let scopes=[];captureScopes&&(scopes=await _captureScopes(v8Api,frame));let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}return{functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,scriptId:frame.location.scriptId,scopes,originalLocation}}function _convertAsyncStackTrace(asyncTrace,sourceMapResolver,maxSegments,maxFramesPerSegment){if(!asyncTrace)return;let maxSegs=maxSegments??DEFAULT_NODE_DEBUG_CONFIG.maxAsyncStackSegments,maxFrames=maxFramesPerSegment??DEFAULT_NODE_DEBUG_CONFIG.maxFramesPerAsyncSegment,segments=[],currentTrace=asyncTrace,segmentCount=0;for(;currentTrace&&segmentCount<maxSegs;){let asyncFrames=[];for(let frame of currentTrace.callFrames.slice(0,maxFrames)){let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}asyncFrames.push({functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,originalLocation})}asyncFrames.length>0&&segments.push({description:currentTrace.description,callFrames:asyncFrames}),currentTrace=currentTrace.parent,segmentCount++}return segments.length===0?void 0:{segments}}async function _evaluateWatchExpressionsOnFrame(v8Api,callFrameId,watchExpressions){let results={};for(let watch of watchExpressions.values())try{let result=await v8Api.evaluateOnCallFrame(callFrameId,watch.expression);result.exceptionDetails?results[watch.expression]=`[Error: ${result.exceptionDetails.text||"Evaluation failed"}]`:results[watch.expression]=await v8Api.extractValueDeep(result.result,2)}catch(e){results[watch.expression]=`[Error: ${e.message||"Unknown error"}]`}return results}function getStoreKey(pid,wsUrl){return wsUrl||`pid:${pid}`}async function enableDebugging(storeKey,session,processInfo,v8Options,config){if(STORES.get(storeKey)?.enabled)return;let v8Api=new V8Api(session,v8Options),sourceMapResolver=new SourceMapResolver(_createNodeSourceMapFetcher()),mergedConfig={...DEFAULT_NODE_DEBUG_CONFIG,...config},store={v8Api,sourceMapResolver,probes:new Map,locationIndex:new Map,watchExpressions:new Map,snapshots:[],snapshotSequence:0,consoleMessages:[],consoleMessageSequence:0,config:mergedConfig,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none",session,processInfo};STORES.set(storeKey,store),await store.v8Api.enable(),session.on("Runtime.consoleAPICalled",params=>{let type=params.type||"log",args=params.args||[],text=_cdpArgsToText(args),levelName=_cdpConsoleTypeToLevelName(type),levelCode=ConsoleMessageLevel[levelName]?.code??1,location,stackTrace=params.stackTrace;if(stackTrace?.callFrames?.length){let frame=stackTrace.callFrames[0];location={url:frame.url||"",lineNumber:frame.lineNumber??0,columnNumber:frame.columnNumber??0}}store.consoleMessageSequence++,store.consoleMessages.push({type,text,level:{name:levelName,code:levelCode},location,timestamp:Date.now(),sequenceNumber:store.consoleMessageSequence}),store.consoleMessages.length>NODE_CONSOLE_MESSAGES_BUFFER_SIZE&&store.consoleMessages.splice(0,store.consoleMessages.length-NODE_CONSOLE_MESSAGES_BUFFER_SIZE)}),store.v8Api.on("scriptParsed",script=>{store.sourceMapResolver.registerScript(script)});for(let script of store.v8Api.getScripts())store.sourceMapResolver.registerScript(script);store.v8Api.on("paused",async event=>{let startTime=Date.now();try{let isException=event.reason==="exception"||event.reason==="promiseRejection",hitBreakpointIds=event.hitBreakpoints||[],hitProbes=[];for(let probe of store.probes.values()){if(!probe.enabled)continue;if(probe.v8BreakpointIds.some(id=>hitBreakpointIds.includes(id))){let conditionMet=!0;probe.hitCondition&&(conditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1)),hitProbes.push({probe,conditionMet})}}let shouldCaptureBreakpoint=hitProbes.some(p=>p.conditionMet),shouldCaptureException=isException&&store.exceptionBreakpoint!=="none";for(let{probe}of hitProbes)probe.hitCount++,probe.lastHitAt=Date.now();if((shouldCaptureBreakpoint||shouldCaptureException)&&event.callFrames.length>0){let topFrame=event.callFrames[0],exceptionInfo;if(isException&&event.data){let excData=event.data;exceptionInfo={type:event.reason==="promiseRejection"?"promiseRejection":"exception",message:excData.description||excData.value||String(excData),name:excData.className,stack:excData.description}}let originalLocation,resolvedLoc=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.location.columnNumber??0);resolvedLoc&&(originalLocation={source:resolvedLoc.source,line:resolvedLoc.line+1,column:resolvedLoc.column!==void 0?resolvedLoc.column+1:void 0,name:resolvedLoc.name});let callStackFull=[],framesToProcess=event.callFrames.slice(0,store.config.maxCallStackDepth);for(let i=0;i<framesToProcess.length;i++)callStackFull.push(await _callFrameToSnapshot(store.v8Api,framesToProcess[i],i<store.config.maxFramesWithScopes,store.sourceMapResolver));let asyncStackTraceFull=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment),traceContext=await _getOtelTraceContext(store.session),probesToCapture=hitProbes.filter(p=>p.conditionMet),snapshotCount=probesToCapture.length+(shouldCaptureException?1:0);for(let s=0;s<snapshotCount;s++){let probeId,isLogpoint,logResult,callStack,asyncStackTrace,watchResults;if(s<probesToCapture.length){let{probe}=probesToCapture[s];if(probeId=probe.id,isLogpoint=probe.kind==="logpoint",isLogpoint&&probe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,probe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}else logResult=void 0;isLogpoint?(callStack=[],asyncStackTrace=void 0,watchResults=void 0):(callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0)}else probeId="__exception__",isLogpoint=!1,logResult=void 0,callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0;let snapshot={id:_generateId(),probeId,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,url:topFrame.url||"",lineNumber:topFrame.location.lineNumber+1,columnNumber:topFrame.location.columnNumber!==void 0?topFrame.location.columnNumber+1:void 0,originalLocation,exception:exceptionInfo,callStack,asyncStackTrace,logResult,watchResults,captureTimeMs:Date.now()-startTime,traceContext:traceContext??void 0};store.snapshots.push(snapshot)}store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots)}}finally{await store.v8Api.resume()}}),store.enabled=!0,store.sourceMapResolver.loadAllSourceMaps().then(()=>{store.sourceMapsLoaded=!0}).catch(()=>{})}async function disableDebugging(storeKey){let store=STORES.get(storeKey);if(store?.enabled){for(let entry of store.locationIndex.values())try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.clear(),store.probes.clear(),store.snapshots.length=0,store.snapshotSequence=0,store.consoleMessages.length=0,store.consoleMessageSequence=0,await store.v8Api.disable(),store.enabled=!1}}async function detachDebugging(storeKey){let store=STORES.get(storeKey);store&&(await disableDebugging(storeKey),await store.v8Api.detach(),STORES.delete(storeKey))}function isDebuggingEnabled(storeKey){return STORES.get(storeKey)?.enabled??!1}function getStore(storeKey){return STORES.get(storeKey)}async function resolveSourceLocation(storeKey,url,line,column=1){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Not connected to Node.js process or debugging not enabled. Call debug_connect first.");let resolved=await store.sourceMapResolver.resolveLocationByUrl(url,line,column);return resolved?{source:resolved.source,line:resolved.line+1,column:resolved.column+1,name:resolved.name}:null}function listStoreKeys(){return Array.from(STORES.keys())}async function setExceptionBreakpoint(storeKey,state){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");await store.v8Api.setPauseOnExceptions(state),store.exceptionBreakpoint=state}function getExceptionBreakpoint(storeKey){return STORES.get(storeKey)?.exceptionBreakpoint??"none"}async function createProbe(storeKey,options){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");let probeId=_generateId(),columnNormalized=options.columnNumber??1,locationKey=_locationKey(options.urlPattern,options.lineNumber,columnNormalized),existingEntry=store.locationIndex.get(locationKey);if(existingEntry){existingEntry.refCount++;let probe2={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[existingEntry.breakpointId],resolvedLocations:existingEntry.resolvedLocations,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe2),probe2}let fullCondition=options.condition?`(${options.condition})`:"true",line0based=options.lineNumber-1,column0based=columnNormalized-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)try{breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1}catch{let scriptUrl=store.sourceMapResolver.getScriptUrl(resolved.scriptId);if(scriptUrl){let result=await store.v8Api.setBreakpointByUrl({url:scriptUrl,lineNumber:resolved.location.line,columnNumber:resolved.location.column,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}else throw new Error("Failed to set breakpoint at resolved location and could not fall back (script URL unknown). A probe may already exist at this line; remove it first or use a different line.")}else{let urlRegex=options.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),result=await store.v8Api.setBreakpointByUrl({urlRegex,lineNumber:options.lineNumber-1,columnNumber:options.columnNumber?options.columnNumber-1:void 0,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}store.locationIndex.set(locationKey,{breakpointId,resolvedLocations:resolvedLocationsCount,refCount:1});let probe={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[breakpointId],resolvedLocations:resolvedLocationsCount,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe),probe}async function removeProbe(storeKey,probeId){let store=STORES.get(storeKey);if(!store)return!1;let probe=store.probes.get(probeId);if(!probe)return!1;let locationKey=_locationKey(probe.urlPattern,probe.lineNumber,probe.columnNumber??1),entry=store.locationIndex.get(locationKey);if(entry&&(entry.refCount--,entry.refCount===0)){try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.delete(locationKey)}return store.probes.delete(probeId),!0}function listProbes(storeKey){return Array.from(STORES.get(storeKey)?.probes.values()??[])}async function clearProbes(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.probes.size;for(let entry of store.locationIndex.values())try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}return store.locationIndex.clear(),store.probes.clear(),count}function getSnapshots(storeKey){return[...STORES.get(storeKey)?.snapshots??[]]}function getSnapshotsByProbe(storeKey,probeId){return(STORES.get(storeKey)?.snapshots??[]).filter(s=>s.probeId===probeId)}function clearSnapshots(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.snapshots.length;return store.snapshots.length=0,count}function clearSnapshotsByProbe(storeKey,probeId){let store=STORES.get(storeKey);if(!store)return 0;let before=store.snapshots.length;return store.snapshots=store.snapshots.filter(s=>s.probeId!==probeId),before-store.snapshots.length}function getSnapshotStats(storeKey){let store=STORES.get(storeKey);if(!store||store.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let snapshotsByProbe={},totalCaptureTime=0;for(let s of store.snapshots)snapshotsByProbe[s.probeId]=(snapshotsByProbe[s.probeId]||0)+1,totalCaptureTime+=s.captureTimeMs;return{totalSnapshots:store.snapshots.length,snapshotsByProbe,oldestTimestamp:store.snapshots[0].timestamp,newestTimestamp:store.snapshots[store.snapshots.length-1].timestamp,averageCaptureTimeMs:totalCaptureTime/store.snapshots.length}}function getConsoleMessages(storeKey){return[...STORES.get(storeKey)?.consoleMessages??[]]}function addWatchExpression(storeKey,expression){let store=STORES.get(storeKey);if(!store)throw new Error("Debug store not initialized");let id=_generateId(),watchExpr={id,expression,createdAt:Date.now()};return store.watchExpressions.set(id,watchExpr),watchExpr}function removeWatchExpression(storeKey,watchExpressionId){return STORES.get(storeKey)?.watchExpressions.delete(watchExpressionId)??!1}function listWatchExpressions(storeKey){return Array.from(STORES.get(storeKey)?.watchExpressions.values()??[])}function clearWatchExpressions(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.watchExpressions.size;return store.watchExpressions.clear(),count}async function loadSourceMaps(storeKey){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");let loaded=await store.sourceMapResolver.loadAllSourceMaps(),sources=store.sourceMapResolver.getOriginalSources();return store.sourceMapsLoaded=!0,{loaded,sources}}function hasSourceMaps(storeKey){return STORES.get(storeKey)?.sourceMapResolver.hasSourceMaps()??!1}function getOriginalSources(storeKey){return STORES.get(storeKey)?.sourceMapResolver.getOriginalSources()??[]}function getScripts(storeKey){return STORES.get(storeKey)?.v8Api.getScripts()??[]}async function evaluateInNode(storeKey,expression,timeoutMs=5e3){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Not connected to Node.js process or debugging not enabled. Call debug_connect first.");let result=await store.session.send("Runtime.evaluate",{expression,returnByValue:!0,timeout:Math.min(timeoutMs,3e4)});if(result.exceptionDetails){let exc=result.exceptionDetails,text=exc.text??exc.description??exc.exception?.description;throw new Error(`Node evaluation failed: ${text??JSON.stringify(exc)}`)}return store.v8Api.extractValue(result.result)}export{PORT,PLATFORM,SESSION_IDLE_SECONDS,SESSION_IDLE_CHECK_SECONDS,SESSION_CLOSE_ON_SOCKET_CLOSE,BROWSER_HEADLESS_ENABLE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_USE_INSTALLED_ON_SYSTEM,BROWSER_EXECUTABLE_PATH,BROWSER_LOCALE,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,OTEL_ENABLE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,OTEL_ASSETS_DIR,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_EXPORTER_TYPE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_HTTP_HEADERS,AWS_REGION,AWS_PROFILE,AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,DAEMON_PORT,DAEMON_SESSION_IDLE_SECONDS,DAEMON_SESSION_IDLE_CHECK_SECONDS,enable,disable,isDebugEnabled,debug,info,warn,error,toJson,V8Api,SourceMapResolver,ConsoleMessageLevelName,ConsoleMessageLevel,ProbeKind,DEFAULT_NODE_DEBUG_CONFIG,getStoreKey,enableDebugging,disableDebugging,detachDebugging,isDebuggingEnabled,getStore,resolveSourceLocation,listStoreKeys,setExceptionBreakpoint,getExceptionBreakpoint,createProbe,removeProbe,listProbes,clearProbes,getSnapshots,getSnapshotsByProbe,clearSnapshots,clearSnapshotsByProbe,getSnapshotStats,getConsoleMessages,addWatchExpression,removeWatchExpression,listWatchExpressions,clearWatchExpressions,loadSourceMaps,hasSourceMaps,getOriginalSources,getScripts,evaluateInNode};
13
+ })()`;try{let result=await session.send("Runtime.evaluate",{expression,returnByValue:!0,timeout:500});if(result.exceptionDetails)return null;let value=result.result?.value;if(value&&typeof value.traceId=="string"&&typeof value.spanId=="string")return{traceId:value.traceId,spanId:value.spanId}}catch{}return null}function _createNodeSourceMapFetcher(){return async(url,baseUrl)=>{let filePath=null;if(url.startsWith("file://"))filePath=url.replace("file://","");else if(url.startsWith("/")||url.match(/^[a-zA-Z]:\\/))filePath=url;else if(baseUrl.startsWith("file://")){let baseDir=path2.dirname(baseUrl.replace("file://",""));filePath=path2.resolve(baseDir,url)}else if(!url.startsWith("http://")&&!url.startsWith("https://")){let baseDir=path2.dirname(baseUrl.replace(/^file:\/\//,""));filePath=path2.resolve(baseDir,url)}if(filePath)try{return fs.readFileSync(filePath,"utf-8")}catch{}return url.startsWith("http://")||url.startsWith("https://")?new Promise(resolve2=>{let req=(url.startsWith("https://")?https:http).get(url,res=>{if(res.statusCode!==200){resolve2(null);return}let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>resolve2(data))});req.on("error",()=>resolve2(null)),req.setTimeout(5e3,()=>{req.destroy(),resolve2(null)})}):null}}async function _captureScopes(v8Api,callFrame,maxDepth=3){let scopeSnapshots=[];for(let scope of callFrame.scopeChain)if(!(scope.type==="global"||scope.type==="script"||scope.type==="with"||scope.type==="eval"||scope.type==="wasm-expression-stack")){if(scopeSnapshots.length>=maxDepth)break;try{let variables=await v8Api.getScopeVariables(scope),scopeVariables=[];for(let[name,value]of Object.entries(variables))scopeVariables.push({name,value,type:typeof value});scopeSnapshots.push({type:scope.type,name:scope.name,variables:scopeVariables})}catch{}}return scopeSnapshots}async function _callFrameToSnapshot(v8Api,frame,captureScopes,sourceMapResolver){let scopes=[];captureScopes&&(scopes=await _captureScopes(v8Api,frame));let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}return{functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,scriptId:frame.location.scriptId,scopes,originalLocation}}function _convertAsyncStackTrace(asyncTrace,sourceMapResolver,maxSegments,maxFramesPerSegment){if(!asyncTrace)return;let maxSegs=maxSegments??DEFAULT_NODE_DEBUG_CONFIG.maxAsyncStackSegments,maxFrames=maxFramesPerSegment??DEFAULT_NODE_DEBUG_CONFIG.maxFramesPerAsyncSegment,segments=[],currentTrace=asyncTrace,segmentCount=0;for(;currentTrace&&segmentCount<maxSegs;){let asyncFrames=[];for(let frame of currentTrace.callFrames.slice(0,maxFrames)){let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}asyncFrames.push({functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,originalLocation})}asyncFrames.length>0&&segments.push({description:currentTrace.description,callFrames:asyncFrames}),currentTrace=currentTrace.parent,segmentCount++}return segments.length===0?void 0:{segments}}async function _evaluateWatchExpressionsOnFrame(v8Api,callFrameId,watchExpressions){let results={};for(let watch of watchExpressions.values())try{let result=await v8Api.evaluateOnCallFrame(callFrameId,watch.expression);result.exceptionDetails?results[watch.expression]=`[Error: ${result.exceptionDetails.text||"Evaluation failed"}]`:results[watch.expression]=await v8Api.extractValueDeep(result.result,2)}catch(e){results[watch.expression]=`[Error: ${e.message||"Unknown error"}]`}return results}function getStoreKey(pid,wsUrl){return wsUrl||`pid:${pid}`}async function enableDebugging(storeKey,session,processInfo,v8Options,config){if(STORES.get(storeKey)?.enabled)return;let v8Api=new V8Api(session,v8Options),sourceMapResolver=new SourceMapResolver(_createNodeSourceMapFetcher()),mergedConfig={...DEFAULT_NODE_DEBUG_CONFIG,...config},store={v8Api,sourceMapResolver,probes:new Map,locationIndex:new Map,watchExpressions:new Map,snapshots:[],snapshotSequence:0,consoleMessages:[],consoleMessageSequence:0,config:mergedConfig,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none",session,processInfo};STORES.set(storeKey,store),await store.v8Api.enable(),session.on("Runtime.consoleAPICalled",params=>{let type=params.type||"log",args=params.args||[],text=_cdpArgsToText(args),levelName=_cdpConsoleTypeToLevelName(type),levelCode=ConsoleMessageLevel[levelName]?.code??1,location,stackTrace=params.stackTrace;if(stackTrace?.callFrames?.length){let frame=stackTrace.callFrames[0];location={url:frame.url||"",lineNumber:frame.lineNumber??0,columnNumber:frame.columnNumber??0}}store.consoleMessageSequence++,store.consoleMessages.push({type,text,level:{name:levelName,code:levelCode},location,timestamp:Date.now(),sequenceNumber:store.consoleMessageSequence}),store.consoleMessages.length>NODE_CONSOLE_MESSAGES_BUFFER_SIZE&&store.consoleMessages.splice(0,store.consoleMessages.length-NODE_CONSOLE_MESSAGES_BUFFER_SIZE)}),store.v8Api.on("scriptParsed",script=>{store.sourceMapResolver.registerScript(script)});for(let script of store.v8Api.getScripts())store.sourceMapResolver.registerScript(script);store.v8Api.on("paused",async event=>{let startTime=Date.now();try{let isException=event.reason==="exception"||event.reason==="promiseRejection",hitBreakpointIds=event.hitBreakpoints||[],hitProbes=[];for(let probe of store.probes.values()){if(!probe.enabled)continue;if(probe.v8BreakpointIds.some(id=>hitBreakpointIds.includes(id))){let conditionMet=!0;probe.hitCondition&&(conditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1)),hitProbes.push({probe,conditionMet})}}let shouldCaptureBreakpoint=hitProbes.some(p=>p.conditionMet),shouldCaptureException=isException&&store.exceptionBreakpoint!=="none";for(let{probe}of hitProbes)probe.hitCount++,probe.lastHitAt=Date.now();if((shouldCaptureBreakpoint||shouldCaptureException)&&event.callFrames.length>0){let topFrame=event.callFrames[0],exceptionInfo;if(isException&&event.data){let excData=event.data;exceptionInfo={type:event.reason==="promiseRejection"?"promiseRejection":"exception",message:excData.description||excData.value||String(excData),name:excData.className,stack:excData.description}}let originalLocation,resolvedLoc=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.location.columnNumber??0);resolvedLoc&&(originalLocation={source:resolvedLoc.source,line:resolvedLoc.line+1,column:resolvedLoc.column!==void 0?resolvedLoc.column+1:void 0,name:resolvedLoc.name});let callStackFull=[],framesToProcess=event.callFrames.slice(0,store.config.maxCallStackDepth);for(let i=0;i<framesToProcess.length;i++)callStackFull.push(await _callFrameToSnapshot(store.v8Api,framesToProcess[i],i<store.config.maxFramesWithScopes,store.sourceMapResolver));let asyncStackTraceFull=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment),traceContext=await _getOtelTraceContext(store.session),probesToCapture=hitProbes.filter(p=>p.conditionMet),snapshotCount=probesToCapture.length+(shouldCaptureException?1:0);for(let s=0;s<snapshotCount;s++){let probeId,isLogpoint,logResult,callStack,asyncStackTrace,watchResults;if(s<probesToCapture.length){let{probe}=probesToCapture[s];if(probeId=probe.id,isLogpoint=probe.kind==="logpoint",isLogpoint&&probe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,probe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}else logResult=void 0;isLogpoint?(callStack=[],asyncStackTrace=void 0,watchResults=void 0):(callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0)}else probeId="__exception__",isLogpoint=!1,logResult=void 0,callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0;let snapshot={id:_generateId(),probeId,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,url:topFrame.url||"",lineNumber:topFrame.location.lineNumber+1,columnNumber:topFrame.location.columnNumber!==void 0?topFrame.location.columnNumber+1:void 0,originalLocation,exception:exceptionInfo,callStack,asyncStackTrace,logResult,watchResults,captureTimeMs:Date.now()-startTime,traceContext:traceContext??void 0};store.snapshots.push(snapshot)}store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots)}}finally{await store.v8Api.resume()}}),store.enabled=!0,store.sourceMapResolver.loadAllSourceMaps().then(()=>{store.sourceMapsLoaded=!0}).catch(()=>{})}async function disableDebugging(storeKey){let store=STORES.get(storeKey);if(store?.enabled){for(let entry of store.locationIndex.values())try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.clear(),store.probes.clear(),store.snapshots.length=0,store.snapshotSequence=0,store.consoleMessages.length=0,store.consoleMessageSequence=0,await store.v8Api.disable(),store.enabled=!1}}async function detachDebugging(storeKey){let store=STORES.get(storeKey);store&&(await disableDebugging(storeKey),await store.v8Api.detach(),STORES.delete(storeKey))}function isDebuggingEnabled(storeKey){return STORES.get(storeKey)?.enabled??!1}function getStore(storeKey){return STORES.get(storeKey)}async function resolveSourceLocation(storeKey,url,line,column=1){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Not connected to Node.js process or debugging not enabled. Call debug_connect first.");let resolved=await store.sourceMapResolver.resolveLocationByUrl(url,line,column);return resolved?{source:resolved.source,line:resolved.line+1,column:resolved.column+1,name:resolved.name}:null}function listStoreKeys(){return Array.from(STORES.keys())}async function setExceptionBreakpoint(storeKey,state){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");await store.v8Api.setPauseOnExceptions(state),store.exceptionBreakpoint=state}function getExceptionBreakpoint(storeKey){return STORES.get(storeKey)?.exceptionBreakpoint??"none"}async function createProbe(storeKey,options){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");let probeId=_generateId(),columnNormalized=options.columnNumber??1,locationKey=_locationKey(options.urlPattern,options.lineNumber,columnNormalized),existingEntry=store.locationIndex.get(locationKey);if(existingEntry){existingEntry.refCount++;let probe2={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[existingEntry.breakpointId],resolvedLocations:existingEntry.resolvedLocations,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe2),probe2}let fullCondition=options.condition?`(${options.condition})`:"true",line0based=options.lineNumber-1,column0based=columnNormalized-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)try{breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1}catch{let scriptUrl=store.sourceMapResolver.getScriptUrl(resolved.scriptId);if(scriptUrl){let result=await store.v8Api.setBreakpointByUrl({url:scriptUrl,lineNumber:resolved.location.line,columnNumber:resolved.location.column,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}else throw new Error("Failed to set breakpoint at resolved location and could not fall back (script URL unknown). A probe may already exist at this line; remove it first or use a different line.")}else{let urlRegex=options.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),result=await store.v8Api.setBreakpointByUrl({urlRegex,lineNumber:options.lineNumber-1,columnNumber:options.columnNumber?options.columnNumber-1:void 0,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}store.locationIndex.set(locationKey,{breakpointId,resolvedLocations:resolvedLocationsCount,refCount:1});let probe={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[breakpointId],resolvedLocations:resolvedLocationsCount,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe),probe}async function removeProbe(storeKey,probeId){let store=STORES.get(storeKey);if(!store)return!1;let probe=store.probes.get(probeId);if(!probe)return!1;let locationKey=_locationKey(probe.urlPattern,probe.lineNumber,probe.columnNumber??1),entry=store.locationIndex.get(locationKey);if(entry&&(entry.refCount--,entry.refCount===0)){try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.delete(locationKey)}return store.probes.delete(probeId),!0}function listProbes(storeKey){return Array.from(STORES.get(storeKey)?.probes.values()??[])}async function clearProbes(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.probes.size;for(let entry of store.locationIndex.values())try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}return store.locationIndex.clear(),store.probes.clear(),count}function getSnapshots(storeKey){return[...STORES.get(storeKey)?.snapshots??[]]}function getSnapshotsByProbe(storeKey,probeId){return(STORES.get(storeKey)?.snapshots??[]).filter(s=>s.probeId===probeId)}function clearSnapshots(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.snapshots.length;return store.snapshots.length=0,count}function clearSnapshotsByProbe(storeKey,probeId){let store=STORES.get(storeKey);if(!store)return 0;let before=store.snapshots.length;return store.snapshots=store.snapshots.filter(s=>s.probeId!==probeId),before-store.snapshots.length}function getSnapshotStats(storeKey){let store=STORES.get(storeKey);if(!store||store.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let snapshotsByProbe={},totalCaptureTime=0;for(let s of store.snapshots)snapshotsByProbe[s.probeId]=(snapshotsByProbe[s.probeId]||0)+1,totalCaptureTime+=s.captureTimeMs;return{totalSnapshots:store.snapshots.length,snapshotsByProbe,oldestTimestamp:store.snapshots[0].timestamp,newestTimestamp:store.snapshots[store.snapshots.length-1].timestamp,averageCaptureTimeMs:totalCaptureTime/store.snapshots.length}}function getConsoleMessages(storeKey){return[...STORES.get(storeKey)?.consoleMessages??[]]}function addWatchExpression(storeKey,expression){let store=STORES.get(storeKey);if(!store)throw new Error("Debug store not initialized");let id=_generateId(),watchExpr={id,expression,createdAt:Date.now()};return store.watchExpressions.set(id,watchExpr),watchExpr}function removeWatchExpression(storeKey,watchExpressionId){return STORES.get(storeKey)?.watchExpressions.delete(watchExpressionId)??!1}function listWatchExpressions(storeKey){return Array.from(STORES.get(storeKey)?.watchExpressions.values()??[])}function clearWatchExpressions(storeKey){let store=STORES.get(storeKey);if(!store)return 0;let count=store.watchExpressions.size;return store.watchExpressions.clear(),count}async function loadSourceMaps(storeKey){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Debugging is not enabled");let loaded=await store.sourceMapResolver.loadAllSourceMaps(),sources=store.sourceMapResolver.getOriginalSources();return store.sourceMapsLoaded=!0,{loaded,sources}}function hasSourceMaps(storeKey){return STORES.get(storeKey)?.sourceMapResolver.hasSourceMaps()??!1}function getOriginalSources(storeKey){return STORES.get(storeKey)?.sourceMapResolver.getOriginalSources()??[]}function getScripts(storeKey){return STORES.get(storeKey)?.v8Api.getScripts()??[]}function wrapScriptInAsyncIIFE(script){return`(async function() { ${script} })()`}function formatEvaluationException(exc){let parts=[],text=exc.text??exc.description;text&&parts.push(text);let desc=exc.exception?.description;desc&&desc!==text&&parts.push(desc);let className=exc.exception?.className;className&&!parts.some(p=>p.includes(className))&&parts.push(className),parts.length===0&&parts.push(JSON.stringify(exc));let stack=exc.stackTrace?.callFrames?.slice(0,5).map(f=>` at ${f.functionName??"?"} (${f.url??"?"}:${f.lineNumber??0})`).join(`
14
+ `);return stack&&parts.push(`Stack:
15
+ ${stack}`),parts.join(" \u2014 ")}async function evaluateInNode(storeKey,expression,timeoutMs=5e3){let store=STORES.get(storeKey);if(!store?.enabled)throw new Error("Not connected to Node.js process or debugging not enabled. Call debug_connect first.");let codeToRun=wrapScriptInAsyncIIFE(expression),result=await store.session.send("Runtime.evaluate",{expression:codeToRun,returnByValue:!0,awaitPromise:!0,timeout:Math.min(timeoutMs,3e4)});if(result.exceptionDetails){let exc=result.exceptionDetails,message=formatEvaluationException(exc);throw new Error(`Node evaluation failed: ${message}`)}return store.v8Api.extractValue(result.result)}export{PORT,TOOL_OUTPUT_SCHEMA_DISABLE,AVAILABLE_TOOL_DOMAINS,PLATFORM,SESSION_IDLE_SECONDS,SESSION_IDLE_CHECK_SECONDS,SESSION_CLOSE_ON_SOCKET_CLOSE,BROWSER_HEADLESS_ENABLE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_USE_INSTALLED_ON_SYSTEM,BROWSER_EXECUTABLE_PATH,BROWSER_LOCALE,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,OTEL_ENABLE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,OTEL_ASSETS_DIR,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_EXPORTER_TYPE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_HTTP_HEADERS,AWS_REGION,AWS_PROFILE,AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,DAEMON_PORT,DAEMON_SESSION_IDLE_SECONDS,DAEMON_SESSION_IDLE_CHECK_SECONDS,enable,disable,isDebugEnabled,debug,info,warn,error,toJson,V8Api,SourceMapResolver,ConsoleMessageLevelName,ConsoleMessageLevel,ProbeKind,DEFAULT_NODE_DEBUG_CONFIG,getStoreKey,enableDebugging,disableDebugging,detachDebugging,isDebuggingEnabled,getStore,resolveSourceLocation,listStoreKeys,setExceptionBreakpoint,getExceptionBreakpoint,createProbe,removeProbe,listProbes,clearProbes,getSnapshots,getSnapshotsByProbe,clearSnapshots,clearSnapshotsByProbe,getSnapshotStats,getConsoleMessages,addWatchExpression,removeWatchExpression,listWatchExpressions,clearWatchExpressions,loadSourceMaps,hasSourceMaps,getOriginalSources,getScripts,evaluateInNode};
@@ -1 +1 @@
1
- import{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint}from"./core-ARVKOWOZ.js";export{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint};
1
+ import{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint}from"./core-247YVH6X.js";export{DEFAULT_NODE_DEBUG_CONFIG,ProbeKind,addWatchExpression,clearProbes,clearSnapshots,clearSnapshotsByProbe,clearWatchExpressions,createProbe,detachDebugging,disableDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getOriginalSources,getScripts,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStore,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listStoreKeys,listWatchExpressions,loadSourceMaps,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint};
@@ -1,9 +1,19 @@
1
- import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_EXECUTABLE_PATH,BROWSER_HEADLESS_ENABLE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,BROWSER_LOCALE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_USE_INSTALLED_ON_SYSTEM,ConsoleMessageLevel,ConsoleMessageLevelName,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,OTEL_ASSETS_DIR,OTEL_ENABLE,OTEL_EXPORTER_HTTP_HEADERS,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,PLATFORM,SourceMapResolver,V8Api,addWatchExpression,clearWatchExpressions,createProbe,debug,detachDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-ARVKOWOZ.js";var INTERACTIVE_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]),CONTENT_ROLES=new Set(["heading","cell","gridcell","columnheader","rowheader","listitem","article","region","main","navigation"]),STRUCTURAL_ROLES=new Set(["generic","group","list","table","row","rowgroup","grid","treegrid","menu","menubar","toolbar","tablist","tree","directory","document","application","presentation","none"]),refCounter=0;function nextRef(){return`e${++refCounter}`}function buildSelectorDescriptor(role,name){if(name!==void 0&&name!==""){let escaped=JSON.stringify(name);return`getByRole('${role}', { name: ${escaped}, exact: true })`}return`getByRole('${role}')`}function createRoleNameTracker(){let counts=new Map,refsByKey=new Map;return{getKey(role,name){return`${role}:${name??""}`},getNextIndex(role,name){let key=this.getKey(role,name),current=counts.get(key)??0;return counts.set(key,current+1),current},trackRef(role,name,ref){let key=this.getKey(role,name),refs=refsByKey.get(key)??[];refs.push(ref),refsByKey.set(key,refs)},getDuplicateKeys(){let duplicates=new Set;for(let[key,refs]of refsByKey)refs.length>1&&duplicates.add(key);return duplicates}}}function removeNthFromNonDuplicates(refs,tracker){let duplicateKeys=tracker.getDuplicateKeys();for(let entry of Object.values(refs)){let key=tracker.getKey(entry.role,entry.name);duplicateKeys.has(key)||delete entry.nth}}function getIndentLevel(line){let m=line.match(/^(\s*)/);return m?Math.floor(m[1].length/2):0}function processLine(line,refs,options,tracker){let depth=getIndentLevel(line);if(options.maxDepth!==void 0&&depth>options.maxDepth)return null;let match=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!match)return options.interactiveOnly?null:line;let[,prefix,role,name,suffix]=match,roleLower=role.toLowerCase();if(role.startsWith("/"))return line;let isInteractive=INTERACTIVE_ROLES.has(roleLower),isContent=CONTENT_ROLES.has(roleLower),isStructural=STRUCTURAL_ROLES.has(roleLower);if(options.interactiveOnly&&!isInteractive||options.compact&&isStructural&&!name)return null;if(!(isInteractive||isContent&&name))return line;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref);let entry={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};refs[ref]=entry;let enhanced=`${prefix}${role}`;return name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.trim()&&(enhanced+=suffix),enhanced}function compactTree(tree){let lines=tree.split(`
1
+ import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_EXECUTABLE_PATH,BROWSER_HEADLESS_ENABLE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,BROWSER_LOCALE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_USE_INSTALLED_ON_SYSTEM,ConsoleMessageLevel,ConsoleMessageLevelName,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,OTEL_ASSETS_DIR,OTEL_ENABLE,OTEL_EXPORTER_HTTP_HEADERS,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,PLATFORM,SourceMapResolver,V8Api,addWatchExpression,clearWatchExpressions,createProbe,debug,detachDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-247YVH6X.js";var INTERACTIVE_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]),CONTENT_ROLES=new Set(["heading","cell","gridcell","columnheader","rowheader","listitem","article","region","main","navigation"]),STRUCTURAL_ROLES=new Set(["generic","group","list","table","row","rowgroup","grid","treegrid","menu","menubar","toolbar","tablist","tree","directory","document","application","presentation","none"]),refCounter=0;function nextRef(){return`e${++refCounter}`}function buildSelectorDescriptor(role,name){if(name!==void 0&&name!==""){let escaped=JSON.stringify(name);return`getByRole('${role}', { name: ${escaped}, exact: true })`}return`getByRole('${role}')`}function createRoleNameTracker(){let counts=new Map,refsByKey=new Map;return{getKey(role,name){return`${role}:${name??""}`},getNextIndex(role,name){let key=this.getKey(role,name),current=counts.get(key)??0;return counts.set(key,current+1),current},trackRef(role,name,ref){let key=this.getKey(role,name),refs=refsByKey.get(key)??[];refs.push(ref),refsByKey.set(key,refs)},getDuplicateKeys(){let duplicates=new Set;for(let[key,refs]of refsByKey)refs.length>1&&duplicates.add(key);return duplicates}}}function removeNthFromNonDuplicates(refs,tracker){let duplicateKeys=tracker.getDuplicateKeys();for(let entry of Object.values(refs)){let key=tracker.getKey(entry.role,entry.name);duplicateKeys.has(key)||delete entry.nth}}function getIndentLevel(line){let m=line.match(/^(\s*)/);return m?Math.floor(m[1].length/2):0}function processLine(line,refs,options,tracker){let depth=getIndentLevel(line);if(options.maxDepth!==void 0&&depth>options.maxDepth)return null;let match=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!match)return options.interactiveOnly?null:line;let[,prefix,role,name,suffix]=match,roleLower=role.toLowerCase();if(role.startsWith("/"))return line;let isInteractive=INTERACTIVE_ROLES.has(roleLower),isContent=CONTENT_ROLES.has(roleLower),isStructural=STRUCTURAL_ROLES.has(roleLower);if(options.interactiveOnly&&!isInteractive||options.compact&&isStructural&&!name)return null;if(!(isInteractive||isContent&&name))return line;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref);let entry={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};refs[ref]=entry;let enhanced=`${prefix}${role}`;return name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.trim()&&(enhanced+=suffix),enhanced}function compactTree(tree){let lines=tree.split(`
2
2
  `),result=[];for(let i=0;i<lines.length;i++){let line=lines[i];if(line.includes("[ref=")){result.push(line);continue}if(line.includes(":")&&!line.endsWith(":")){result.push(line);continue}let currentIndent=getIndentLevel(line),hasRelevantChildren=!1;for(let j=i+1;j<lines.length&&!(getIndentLevel(lines[j])<=currentIndent);j++)if(lines[j].includes("[ref=")){hasRelevantChildren=!0;break}hasRelevantChildren&&result.push(line)}return result.join(`
3
3
  `)}function processAriaTreeWithRefs(ariaTree,options={}){refCounter=0;let refs={};if(!ariaTree||!ariaTree.trim())return{tree:"(empty)",refs:{}};let lines=ariaTree.split(`
4
4
  `),result=[],tracker=createRoleNameTracker();if(options.interactiveOnly){for(let line of lines){let m=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!m)continue;let[,,role,name,suffix]=m,roleLower=role.toLowerCase();if(!INTERACTIVE_ROLES.has(roleLower))continue;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref),refs[ref]={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};let enhanced=`- ${role}`;name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.includes("[")&&(enhanced+=suffix),result.push(enhanced)}return removeNthFromNonDuplicates(refs,tracker),{tree:result.length?result.join(`
5
5
  `):"(no interactive elements)",refs}}for(let line of lines){let processed=processLine(line,refs,options,tracker);processed!==null&&result.push(processed)}removeNthFromNonDuplicates(refs,tracker);let tree=result.join(`
6
- `);return options.compact&&(tree=compactTree(tree)),{tree,refs}}var NATIVE_INTERACTIVE_TAGS=new Set(["BUTTON","A","INPUT","SELECT","TEXTAREA"]),INTERACTIVE_ARIA_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]);async function findCursorInteractiveElements(page,rootSelector){let interactiveRolesList=[...INTERACTIVE_ARIA_ROLES],nativeTagsList=[...NATIVE_INTERACTIVE_TAGS],rootSel=rootSelector??null,result=await page.evaluate(args=>{let root=args.rootSel!=null?document.querySelector(args.rootSel):document.body;if(!root)return[];let interactiveRoles=new Set(args.interactiveRoles.map(r=>r.toLowerCase())),nativeTags=new Set(args.nativeTags.map(t=>t.toUpperCase())),candidates=[];function isCursorInteractive(el){let style=getComputedStyle(el);if(style.visibility==="hidden"||style.display==="none")return!1;let tag=el.tagName.toUpperCase();if(nativeTags.has(tag))return!1;let role=(el.getAttribute("role")||"").toLowerCase();if(role&&interactiveRoles.has(role))return!1;if(style.cursor==="pointer"||el.hasAttribute("onclick")||typeof el.onclick=="function")return!0;let tabindex=el.getAttribute("tabindex");return tabindex!=null&&parseInt(tabindex,10)>=0}function buildSelector(el){let tid=el.getAttribute("data-testid");if(tid&&tid.trim())try{return`[data-testid="${CSS.escape(tid)}"]`}catch{return`[data-testid="${tid.replace(/"/g,"\\22 ")}"]`}let id=el.id;if(id&&typeof document.querySelectorAll=="function")try{let escaped=CSS.escape(id);if(document.querySelectorAll(`#${escaped}`).length===1)return`#${escaped}`}catch{}let tag=el.tagName.toLowerCase(),cn=el.className&&typeof el.className=="string"?el.className.trim().split(/\s+/).filter(Boolean).slice(0,3).map(c=>"."+CSS.escape(c)).join(""):"";return tag+cn||tag}function getName(el){return(el.getAttribute("aria-label")||el.getAttribute("title")||el.textContent||"").trim().slice(0,80)||void 0}let all=root.querySelectorAll("*");for(let i=0;i<all.length;i++){let el=all[i];if(!isCursorInteractive(el))continue;let rect=el.getBoundingClientRect();if(rect.width===0&&rect.height===0)continue;let role=getComputedStyle(el).cursor==="pointer"||el.onclick!=null||el.hasAttribute("onclick")?"clickable":"focusable";candidates.push({selector:buildSelector(el),role,name:getName(el)})}let bySelector={};candidates.forEach(c=>{bySelector[c.selector]||(bySelector[c.selector]=[]),bySelector[c.selector].push(c)});let out=[];return candidates.forEach(c=>{let arr=bySelector[c.selector],idx=arr.indexOf(c);out.push({selector:c.selector,role:c.role,name:c.name,nth:arr.length>1?idx:void 0})}),out},{rootSel,interactiveRoles:interactiveRolesList,nativeTags:nativeTagsList});return Array.isArray(result)?result:[]}import{z}from"zod";var refMapEntrySchema=z.object({role:z.string(),name:z.string().optional(),selector:z.string(),nth:z.number().optional()}),TakeAriaSnapshot=class{name(){return"a11y_take-aria-snapshot"}description(){return`
6
+ `);return options.compact&&(tree=compactTree(tree)),{tree,refs}}var NATIVE_INTERACTIVE_TAGS=new Set(["BUTTON","A","INPUT","SELECT","TEXTAREA"]),INTERACTIVE_ARIA_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]);async function findCursorInteractiveElements(page,rootSelector){let interactiveRolesList=[...INTERACTIVE_ARIA_ROLES],nativeTagsList=[...NATIVE_INTERACTIVE_TAGS],rootSel=rootSelector??null,result=await page.evaluate(args=>{let root=args.rootSel!=null?document.querySelector(args.rootSel):document.body;if(!root)return[];let interactiveRoles=new Set(args.interactiveRoles.map(r=>r.toLowerCase())),nativeTags=new Set(args.nativeTags.map(t=>t.toUpperCase())),candidates=[];function isCursorInteractive(el){let style=getComputedStyle(el);if(style.visibility==="hidden"||style.display==="none")return!1;let tag=el.tagName.toUpperCase();if(nativeTags.has(tag))return!1;let role=(el.getAttribute("role")||"").toLowerCase();if(role&&interactiveRoles.has(role))return!1;if(style.cursor==="pointer"||el.hasAttribute("onclick")||typeof el.onclick=="function")return!0;let tabindex=el.getAttribute("tabindex");return tabindex!=null&&parseInt(tabindex,10)>=0}function buildSelector(el){let tid=el.getAttribute("data-testid");if(tid&&tid.trim())try{return`[data-testid="${CSS.escape(tid)}"]`}catch{return`[data-testid="${tid.replace(/"/g,"\\22 ")}"]`}let id=el.id;if(id&&typeof document.querySelectorAll=="function")try{let escaped=CSS.escape(id);if(document.querySelectorAll(`#${escaped}`).length===1)return`#${escaped}`}catch{}let tag=el.tagName.toLowerCase(),cn=el.className&&typeof el.className=="string"?el.className.trim().split(/\s+/).filter(Boolean).slice(0,3).map(c=>"."+CSS.escape(c)).join(""):"";return tag+cn||tag}function getName(el){return(el.getAttribute("aria-label")||el.getAttribute("title")||el.textContent||"").trim().slice(0,80)||void 0}let all=root.querySelectorAll("*");for(let i=0;i<all.length;i++){let el=all[i];if(!isCursorInteractive(el))continue;let rect=el.getBoundingClientRect();if(rect.width===0&&rect.height===0)continue;let role=getComputedStyle(el).cursor==="pointer"||el.onclick!=null||el.hasAttribute("onclick")?"clickable":"focusable";candidates.push({selector:buildSelector(el),role,name:getName(el)})}let bySelector={};candidates.forEach(c=>{bySelector[c.selector]||(bySelector[c.selector]=[]),bySelector[c.selector].push(c)});let out=[];return candidates.forEach(c=>{let arr=bySelector[c.selector],idx=arr.indexOf(c);out.push({selector:c.selector,role:c.role,name:c.name,nth:arr.length>1?idx:void 0})}),out},{rootSel,interactiveRoles:interactiveRolesList,nativeTags:nativeTagsList});return Array.isArray(result)?result:[]}import{z}from"zod";async function takeAriaSnapshotWithRefs(context,options={}){let rawSnapshot=await context.page.locator(options.selector||"body").first().ariaSnapshot(),{tree,refs}=processAriaTreeWithRefs(rawSnapshot,{interactiveOnly:options.interactiveOnly,maxDepth:options.maxDepth,compact:options.compact}),mergedTree=tree,mergedRefs={...refs};if(options.cursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,options.selector||null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>{let n=parseInt(k.replace(/^e/,""),10)||0;return n>m?n:m},0),lines=[];cursorEntries.forEach((entry,i)=>{let ref=`e${maxNum+1+i}`;mergedRefs[ref]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth};let line=`- ${entry.role}`;entry.name&&(line+=` "${entry.name}"`),line+=` [ref=${ref}]`,entry.nth!==void 0&&entry.nth>0&&(line+=` [nth=${entry.nth}]`),lines.push(line)}),mergedTree=mergedTree+(mergedTree?`
7
+ `:"")+`# Cursor-interactive elements
8
+ `+lines.join(`
9
+ `)}}return context.setRefMap(mergedRefs),{output:`
10
+ - Page URL: ${context.page.url()}
11
+ - Page Title: ${await context.page.title()}
12
+ - Page/Component Snapshot (use refs in interaction tools, e.g. selector "@e1" or "e1"):
13
+ \`\`\`yaml
14
+ ${mergedTree}
15
+ \`\`\`
16
+ `.trim(),refs:mergedRefs}}var refMapEntrySchema=z.object({role:z.string(),name:z.string().optional(),selector:z.string(),nth:z.number().optional()}),TakeAriaSnapshot=class{name(){return"a11y_take-aria-snapshot"}description(){return`
7
17
  Captures an ARIA (accessibility) snapshot of the current page or a specific element.
8
18
  Returns an enhanced tree with refs ([ref=e1], [ref=e2], ...) and a refs object.
9
19
  Use the refs in interaction tools: e.g. interaction_click with selector "e1" or "@e1" to click that element.
@@ -16,17 +26,7 @@ The output includes the page URL, title, and a YAML-formatted accessibility tree
16
26
  - Provides semantic structure and accessibility roles with stable refs for interaction
17
27
  - Refs are valid until the next snapshot or navigation; re-snapshot after page changes
18
28
  - Set cursorInteractive: true to also get refs for custom clickable elements (e.g. div/span with cursor:pointer or onclick) that have no ARIA role
19
- `.trim()}inputSchema(){return{selector:z.string().describe("CSS selector for element to take snapshot.").optional(),interactiveOnly:z.boolean().optional().describe("If true, only interactive elements get refs (buttons, links, inputs, etc.). If false or omitted, content roles with a name also get refs (e.g. heading, listitem, cell, article, region, main, navigation)."),maxDepth:z.number().int().min(0).optional().describe("Maximum depth of tree (0 = root only)."),compact:z.boolean().optional().describe("If true, remove empty structural elements from the tree."),cursorInteractive:z.boolean().optional().default(!1).describe("If true, also find and assign refs to elements that are clickable/focusable by CSS (cursor:pointer, onclick, tabindex) but have no ARIA role (e.g. custom div/span buttons).")}}outputSchema(){return{output:z.string().describe("Includes the page URL, title, and a YAML-formatted accessibility tree with [ref=e1] etc."),refs:z.record(z.string(),refMapEntrySchema).describe('Map of ref id to { role, name?, selector, nth? }. Use selector "e1" or "@e1" in interaction tools.')}}async handle(context,args){let rawSnapshot=await context.page.locator(args.selector||"body").ariaSnapshot(),{tree,refs}=processAriaTreeWithRefs(rawSnapshot,{interactiveOnly:args.interactiveOnly,maxDepth:args.maxDepth,compact:args.compact}),mergedTree=tree,mergedRefs={...refs};if(args.cursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,args.selector||null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>{let n=parseInt(k.replace(/^e/,""),10)||0;return n>m?n:m},0),lines=[];cursorEntries.forEach((entry,i)=>{let ref=`e${maxNum+1+i}`;mergedRefs[ref]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth};let line=`- ${entry.role}`;entry.name&&(line+=` "${entry.name}"`),line+=` [ref=${ref}]`,entry.nth!==void 0&&entry.nth>0&&(line+=` [nth=${entry.nth}]`),lines.push(line)}),mergedTree=mergedTree+(mergedTree?`
20
- `:"")+`# Cursor-interactive elements
21
- `+lines.join(`
22
- `)}}return context.setRefMap(mergedRefs),{output:`
23
- - Page URL: ${context.page.url()}
24
- - Page Title: ${await context.page.title()}
25
- - Page/Component Snapshot (use refs in interaction tools, e.g. selector "@e1" or "e1"):
26
- \`\`\`yaml
27
- ${mergedTree}
28
- \`\`\`
29
- `.trim(),refs:mergedRefs}}};import{z as z2}from"zod";var DEFAULT_INCLUDE_STYLES=!0,DEFAULT_INCLUDE_RUNTIME_VISUAL=!0,DEFAULT_CHECK_OCCLUSION=!1,DEFAULT_ONLY_VISIBLE=!1,DEFAULT_ONLY_IN_VIEWPORT=!1,DEFAULT_TEXT_PREVIEW_MAX_LENGTH=80,DEFAULT_STYLE_PROPERTIES=["display","visibility","opacity","pointer-events","position","z-index","color","background-color","border-color","border-width","border-style","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-decoration-line","white-space","overflow","overflow-x","overflow-y","transform","cursor"],DEFAULT_INTERESTING_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","switch","tab","menuitem","dialog","heading","listbox","listitem","option"]),INTERNAL_CONCURRENCY=12,INTERNAL_SAFETY_CAP=2e3;function attrsToObj(attrs){let result={};if(!attrs)return result;for(let i=0;i<attrs.length;i+=2){let key=String(attrs[i]),value=String(attrs[i+1]??"");result[key]=value}return result}var TakeAxTreeSnapshot=class{name(){return"accessibility_take-ax-tree-snapshot"}description(){return`
29
+ `.trim()}inputSchema(){return{selector:z.string().describe("CSS selector for element to take snapshot.").optional(),interactiveOnly:z.boolean().optional().describe("If true, only interactive elements get refs (buttons, links, inputs, etc.). If false or omitted, content roles with a name also get refs (e.g. heading, listitem, cell, article, region, main, navigation)."),maxDepth:z.number().int().min(0).optional().describe("Maximum depth of tree (0 = root only)."),compact:z.boolean().optional().describe("If true, remove empty structural elements from the tree."),cursorInteractive:z.boolean().optional().default(!1).describe("If true, also find and assign refs to elements that are clickable/focusable by CSS (cursor:pointer, onclick, tabindex) but have no ARIA role (e.g. custom div/span buttons).")}}outputSchema(){return{output:z.string().describe("Includes the page URL, title, and a YAML-formatted accessibility tree with [ref=e1] etc."),refs:z.record(z.string(),refMapEntrySchema).describe('Map of ref id to { role, name?, selector, nth? }. Use selector "e1" or "@e1" in interaction tools.')}}async handle(context,args){return takeAriaSnapshotWithRefs(context,{selector:args.selector,interactiveOnly:args.interactiveOnly,maxDepth:args.maxDepth,compact:args.compact,cursorInteractive:args.cursorInteractive})}};import{z as z2}from"zod";var DEFAULT_INCLUDE_STYLES=!0,DEFAULT_INCLUDE_RUNTIME_VISUAL=!0,DEFAULT_CHECK_OCCLUSION=!1,DEFAULT_ONLY_VISIBLE=!1,DEFAULT_ONLY_IN_VIEWPORT=!1,DEFAULT_TEXT_PREVIEW_MAX_LENGTH=80,DEFAULT_STYLE_PROPERTIES=["display","visibility","opacity","pointer-events","position","z-index","color","background-color","border-color","border-width","border-style","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-decoration-line","white-space","overflow","overflow-x","overflow-y","transform","cursor"],DEFAULT_INTERESTING_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","switch","tab","menuitem","dialog","heading","listbox","listitem","option"]),INTERNAL_CONCURRENCY=12,INTERNAL_SAFETY_CAP=2e3;function attrsToObj(attrs){let result={};if(!attrs)return result;for(let i=0;i<attrs.length;i+=2){let key=String(attrs[i]),value=String(attrs[i+1]??"");result[key]=value}return result}var TakeAxTreeSnapshot=class{name(){return"accessibility_take-ax-tree-snapshot"}description(){return`
30
30
  Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.
31
31
 
32
32
  Use this tool to detect UI issues like:
@@ -277,9 +277,9 @@ By default, all <script> tags are removed from the output unless "removeScripts"
277
277
  `}inputSchema(){return{selector:z3.string().describe("CSS selector to limit the HTML content to a specific container.").optional(),removeScripts:z3.boolean().describe('Remove all script tags from the HTML (default: "true").').optional().default(!0),removeComments:z3.boolean().describe('Remove all HTML comments (default: "false").').optional().default(!1),removeStyles:z3.boolean().describe('Remove all style tags from the HTML (default: "false").').optional().default(!1),removeMeta:z3.boolean().describe('Remove all meta tags from the HTML (default: "false").').optional().default(!1),cleanHtml:z3.boolean().describe('Perform comprehensive HTML cleaning (default: "false").').optional().default(!1),minify:z3.boolean().describe('Minify the HTML output (default: "false").').optional().default(!1),maxLength:z3.number().int().positive().describe(`Maximum number of characters to return (default: "${DEFAULT_MAX_HTML_LENGTH}").`).optional().default(DEFAULT_MAX_HTML_LENGTH)}}outputSchema(){return{output:z3.string().describe("The requested HTML content of the page.")}}async handle(context,args){let{selector,removeScripts,removeComments,removeStyles,removeMeta,minify,cleanHtml,maxLength}=args,htmlContent;if(selector){let element=await context.page.$(selector);if(!element)throw new Error(`Element with selector "${selector}" not found`);htmlContent=await element.evaluate(el=>el.outerHTML)}else htmlContent=await context.page.content();let shouldRemoveScripts=removeScripts||cleanHtml,shouldRemoveComments=removeComments||cleanHtml,shouldRemoveStyles=removeStyles||cleanHtml,shouldRemoveMeta=removeMeta||cleanHtml;(shouldRemoveScripts||shouldRemoveComments||shouldRemoveStyles||shouldRemoveMeta||minify)&&(htmlContent=await context.page.evaluate(params=>{let html=params.html,removeScripts2=params.removeScripts,removeComments2=params.removeComments,removeStyles2=params.removeStyles,removeMeta2=params.removeMeta,minify2=params.minify,template=document.createElement("template");template.innerHTML=html;let root=template.content;if(removeScripts2&&root.querySelectorAll("script").forEach(script=>script.remove()),removeStyles2&&root.querySelectorAll("style").forEach(style=>style.remove()),removeMeta2&&root.querySelectorAll("meta").forEach(meta=>meta.remove()),removeComments2){let removeComments3=node=>{let childNodes=node.childNodes;for(let i=childNodes.length-1;i>=0;i--){let child=childNodes[i];child.nodeType===8?node.removeChild(child):child.nodeType===1&&removeComments3(child)}};removeComments3(root)}let result=template.innerHTML;return minify2&&(result=result.replace(/>\s+</g,"><").trim()),result},{html:htmlContent,removeScripts:shouldRemoveScripts,removeComments:shouldRemoveComments,removeStyles:shouldRemoveStyles,removeMeta:shouldRemoveMeta,minify}));let output=htmlContent;return output.length>maxLength&&(output=output.slice(0,maxLength)+`
278
278
  <!-- Output truncated due to size limits -->`),{output}}};import{z as z4}from"zod";var DEFAULT_MAX_TEXT_LENGTH=5e4,GetAsText=class{name(){return"content_get-as-text"}description(){return"Gets the visible text content of the current page."}inputSchema(){return{selector:z4.string().describe("CSS selector to limit the text content to a specific container.").optional(),maxLength:z4.number().int().positive().describe(`Maximum number of characters to return (default: "${DEFAULT_MAX_TEXT_LENGTH}").`).optional().default(DEFAULT_MAX_TEXT_LENGTH)}}outputSchema(){return{output:z4.string().describe("The requested text content of the page.")}}async handle(context,args){let{selector,maxLength}=args,output=await context.page.evaluate(params=>{let selector2=params.selector,root=selector2?document.querySelector(selector2):document.body;if(!root)throw new Error(`Element with selector "${selector2}" not found`);let walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,{acceptNode:node2=>{let style=window.getComputedStyle(node2.parentElement);return style.display!=="none"&&style.visibility!=="hidden"?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}}),text="",node;for(;node=walker.nextNode();){let trimmedText=node.textContent?.trim();trimmedText&&(text+=trimmedText+`
279
279
  `)}return text.trim()},{selector});return output.length>maxLength&&(output=output.slice(0,maxLength)+`
280
- [Output truncated due to size limits]`),{output}}};function getEnumKeyTuples(enumObj){let values=Object.keys(enumObj).filter(key=>isNaN(Number(key))).map(key=>enumObj[key]);if(values.length===0)throw new Error("Enum has no values");return values}function createEnumTransformer(enumObj,opts){let values=Object.keys(enumObj).filter(k=>isNaN(Number(k))).map(k=>enumObj[k]),caseInsensitive=opts?.caseInsensitive??!0,lookup=new Map(values.map(v=>[caseInsensitive?v.toLowerCase():v,v]));return value=>{let key=caseInsensitive?value.toLowerCase():value,found=lookup.get(key);if(found===void 0)throw new Error(`Invalid enum value: "${value}"`);return found}}function formattedTimeForFilename(date=new Date){let pad=n=>String(n).padStart(2,"0");return date.getFullYear()+pad(date.getMonth()+1)+pad(date.getDate())+"-"+pad(date.getHours())+pad(date.getMinutes())+pad(date.getSeconds())}import os from"node:os";import path from"node:path";import{z as z5}from"zod";var SizeUnit=(SizeUnit2=>(SizeUnit2.PIXEL="px",SizeUnit2.INCH="in",SizeUnit2.CENTIMETER="cm",SizeUnit2.MILLIMETER="mm",SizeUnit2))(SizeUnit||{}),PageFormat=(PageFormat2=>(PageFormat2.LETTER="Letter",PageFormat2.LEGAL="Legal",PageFormat2.TABLOID="Tabloid",PageFormat2.LEDGER="Ledger",PageFormat2.A0="A0",PageFormat2.A1="A1",PageFormat2.A2="A2",PageFormat2.A3="A3",PageFormat2.A4="A4",PageFormat2.A5="A5",PageFormat2.A6="A6",PageFormat2))(PageFormat||{}),DEFAULT_NAME="page",DEFAULT_MARGIN="1cm",DEFAULT_FORMAT="A4",DEFAULT_MARGINS={top:DEFAULT_MARGIN,right:DEFAULT_MARGIN,bottom:DEFAULT_MARGIN,left:DEFAULT_MARGIN},SaveAsPdf=class{name(){return"content_save-as-pdf"}description(){return"Saves the current page as a PDF file."}inputSchema(){return{outputPath:z5.string().describe("Directory path where PDF will be saved. By default OS tmp directory is used.").optional().default(os.tmpdir()),name:z5.string().describe(`Name of the save/export. Default value is "${DEFAULT_NAME}". Note that final saved/exported file name is in the "{name}-{time}.pdf" format in which "{time}" is in the "YYYYMMDD-HHmmss" format.`).optional().default(DEFAULT_NAME),format:z5.enum(getEnumKeyTuples(PageFormat)).transform(createEnumTransformer(PageFormat)).describe(`Page format. Valid values are: ${getEnumKeyTuples(PageFormat)}.`).optional().default(DEFAULT_FORMAT),printBackground:z5.boolean().describe('Whether to print background graphics (default: "false").').optional().default(!1),margin:z5.object({top:z5.string().describe("Top margin.").default(DEFAULT_MARGIN),right:z5.string().describe("Right margin.").default(DEFAULT_MARGIN),bottom:z5.string().describe("Bottom margin.").default(DEFAULT_MARGIN),left:z5.string().describe("Left margin.").default(DEFAULT_MARGIN)}).describe(`Page margins. Numeric margin values labeled with units ("100px", "10cm", etc ...). Unlabeled values are treated as pixels. Valid units are: ${getEnumKeyTuples(SizeUnit)}.`).optional()}}outputSchema(){return{filePath:z5.string().describe("Full path of the saved PDF file.")}}async handle(context,args){let filename=`${args.name||DEFAULT_NAME}-${formattedTimeForFilename()}.pdf`,filePath=path.resolve(args.outputPath,filename),options={path:filePath,format:args.format,printBackground:args.printBackground,margin:args.margin||DEFAULT_MARGINS};return await context.page.pdf(options),{filePath}}};function parseRef(arg){let s=arg.trim();return s.startsWith("@")?s.slice(1):s.toLowerCase().startsWith("ref=")?s.slice(4):/^e\d+$/.test(s)?s:null}function getLocatorFromRef(page,refMap,ref){let data=refMap[ref];if(!data)return null;if(data.role==="clickable"||data.role==="focusable"){let locator2=page.locator(data.selector);return data.nth!==void 0?locator2.nth(data.nth):locator2}let locator;return data.name!==void 0&&data.name!==""?locator=page.getByRole(data.role,{name:data.name,exact:!0}):locator=page.getByRole(data.role),data.nth!==void 0&&(locator=locator.nth(data.nth)),locator}function resolveSelectorOrRef(context,selectorOrRef){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(ref&&Object.keys(refMap).length>0){let locator=getLocatorFromRef(context.page,refMap,ref);if(locator)return locator}return context.page.locator(selectorOrRef)}import os2 from"node:os";import path2 from"node:path";import jpegjs from"jpeg-js";import{PNG}from"pngjs";import{z as z6}from"zod";var ANNOTATION_OVERLAY_ID="__browser_devtools_mcp_annotations__",ANNOTATE_BOX_TIMEOUT_MS=2e3,ScreenshotType=(ScreenshotType2=>(ScreenshotType2.PNG="png",ScreenshotType2.JPEG="jpeg",ScreenshotType2))(ScreenshotType||{}),DEFAULT_SCREENSHOT_NAME="screenshot",DEFAULT_SCREENSHOT_TYPE="png",DEFAULT_SCREENSHOT_QUALITY=100,TakeScreenshot=class{name(){return"content_take-screenshot"}description(){return`Takes a screenshot of the current page or a specific element.
280
+ [Output truncated due to size limits]`),{output}}};function getEnumKeyTuples(enumObj){let values=Object.keys(enumObj).filter(key=>isNaN(Number(key))).map(key=>enumObj[key]);if(values.length===0)throw new Error("Enum has no values");return values}function createEnumTransformer(enumObj,opts){let values=Object.keys(enumObj).filter(k=>isNaN(Number(k))).map(k=>enumObj[k]),caseInsensitive=opts?.caseInsensitive??!0,lookup=new Map(values.map(v=>[caseInsensitive?v.toLowerCase():v,v]));return value=>{let key=caseInsensitive?value.toLowerCase():value,found=lookup.get(key);if(found===void 0)throw new Error(`Invalid enum value: "${value}"`);return found}}function formattedTimeForFilename(date=new Date){let pad=n=>String(n).padStart(2,"0");return date.getFullYear()+pad(date.getMonth()+1)+pad(date.getDate())+"-"+pad(date.getHours())+pad(date.getMinutes())+pad(date.getSeconds())}import os from"node:os";import path from"node:path";import{z as z5}from"zod";var SizeUnit=(SizeUnit2=>(SizeUnit2.PIXEL="px",SizeUnit2.INCH="in",SizeUnit2.CENTIMETER="cm",SizeUnit2.MILLIMETER="mm",SizeUnit2))(SizeUnit||{}),PageFormat=(PageFormat2=>(PageFormat2.LETTER="Letter",PageFormat2.LEGAL="Legal",PageFormat2.TABLOID="Tabloid",PageFormat2.LEDGER="Ledger",PageFormat2.A0="A0",PageFormat2.A1="A1",PageFormat2.A2="A2",PageFormat2.A3="A3",PageFormat2.A4="A4",PageFormat2.A5="A5",PageFormat2.A6="A6",PageFormat2))(PageFormat||{}),DEFAULT_NAME="page",DEFAULT_MARGIN="1cm",DEFAULT_FORMAT="A4",DEFAULT_MARGINS={top:DEFAULT_MARGIN,right:DEFAULT_MARGIN,bottom:DEFAULT_MARGIN,left:DEFAULT_MARGIN},SaveAsPdf=class{name(){return"content_save-as-pdf"}description(){return"Saves the current page as a PDF file."}inputSchema(){return{outputPath:z5.string().describe("Directory path where PDF will be saved. By default OS tmp directory is used.").optional().default(os.tmpdir()),name:z5.string().describe(`Name of the save/export. Default value is "${DEFAULT_NAME}". Note that final saved/exported file name is in the "{name}-{time}.pdf" format in which "{time}" is in the "YYYYMMDD-HHmmss" format.`).optional().default(DEFAULT_NAME),format:z5.enum(getEnumKeyTuples(PageFormat)).transform(createEnumTransformer(PageFormat)).describe(`Page format. Valid values are: ${getEnumKeyTuples(PageFormat)}.`).optional().default(DEFAULT_FORMAT),printBackground:z5.boolean().describe('Whether to print background graphics (default: "false").').optional().default(!1),margin:z5.object({top:z5.string().describe("Top margin.").default(DEFAULT_MARGIN),right:z5.string().describe("Right margin.").default(DEFAULT_MARGIN),bottom:z5.string().describe("Bottom margin.").default(DEFAULT_MARGIN),left:z5.string().describe("Left margin.").default(DEFAULT_MARGIN)}).describe(`Page margins. Numeric margin values labeled with units ("100px", "10cm", etc ...). Unlabeled values are treated as pixels. Valid units are: ${getEnumKeyTuples(SizeUnit)}.`).optional()}}outputSchema(){return{filePath:z5.string().describe("Full path of the saved PDF file.")}}async handle(context,args){let filename=`${args.name||DEFAULT_NAME}-${formattedTimeForFilename()}.pdf`,filePath=path.resolve(args.outputPath,filename),options={path:filePath,format:args.format,printBackground:args.printBackground,margin:args.margin||DEFAULT_MARGINS};return await context.page.pdf(options),{filePath}}};var REF_ROLES_INTERACTIVE=new Set(["button","link","textbox","searchbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","slider","spinbutton","switch","tab","tabpanel","treeitem","listitem","row","cell","gridcell","columnheader","rowheader","separator","dialog","alertdialog","clickable","focusable"]);function parseRef(arg){let s=arg.trim();return s.startsWith("@")?s.slice(1):s.toLowerCase().startsWith("ref=")?s.slice(4):/^e\d+$/.test(s)?s:null}function getLocatorFromRef(page,refMap,ref){let data=refMap[ref];if(!data)return null;if(data.role==="clickable"||data.role==="focusable"){let locator2=page.locator(data.selector);return data.nth!==void 0?locator2.nth(data.nth):locator2.first()}let locator;return data.name!==void 0&&data.name!==""?locator=page.getByRole(data.role,{name:data.name,exact:!0}):locator=page.getByRole(data.role),data.nth!==void 0?locator.nth(data.nth):locator.first()}function assertRefRole(context,selectorOrRef,allowedRoles,actionLabel,hint){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(!ref||!refMap[ref])return;let entry=refMap[ref];if(allowedRoles.has(entry.role))return;let namePart=entry.name?` "${entry.name}"`:"";throw new Error(`Ref ${ref} points to a ${entry.role}${namePart}, which is not valid for ${actionLabel}. ${hint}`)}function resolveSelectorOrRef(context,selectorOrRef){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(ref&&Object.keys(refMap).length>0){let locator=getLocatorFromRef(context.page,refMap,ref);if(locator)return locator}return context.page.locator(selectorOrRef).first()}import os2 from"node:os";import path2 from"node:path";import jpegjs from"jpeg-js";import{PNG}from"pngjs";import{z as z6}from"zod";var ANNOTATION_OVERLAY_ID="__browser_devtools_mcp_annotations__",ANNOTATE_BOX_TIMEOUT_MS=2e3,ScreenshotType=(ScreenshotType2=>(ScreenshotType2.PNG="png",ScreenshotType2.JPEG="jpeg",ScreenshotType2))(ScreenshotType||{}),DEFAULT_SCREENSHOT_NAME="screenshot",DEFAULT_SCREENSHOT_TYPE="png",DEFAULT_SCREENSHOT_QUALITY=100,TakeScreenshot=class{name(){return"content_take-screenshot"}description(){return`Takes a screenshot of the current page or a specific element.
281
281
 
282
- **Do NOT use this tool to understand page structure.** Do NOT call this as the first inspection after navigation \u2014 call a11y_take-aria-snapshot first, then a11y_take-ax-tree-snapshot if needed. Use this tool ONLY when you need to verify how the UI looks visually (e.g. design check, visual bug) or when ARIA/AX snapshots are insufficient.
282
+ **Do NOT use this tool to understand page structure.** For structure, use the ARIA snapshot from the last navigation (go-to, go-back, go-forward, reload) or call a11y_take-aria-snapshot / a11y_take-ax-tree-snapshot. Use this tool ONLY when you need to verify a visual problem (e.g. design check, visual bug, contrast, layout) \u2014 not to "see" the page. When in doubt, use snapshots first; screenshot only if snapshots cannot answer the visual question.
283
283
 
284
284
  The screenshot is always saved to disk; the file path is returned. Do NOT set includeBase64 to true unless the assistant cannot read the file from the returned path (e.g. remote server, container).`}inputSchema(){return{outputPath:z6.string().describe("Directory path where screenshot will be saved. By default OS tmp directory is used.").optional().default(os2.tmpdir()),name:z6.string().describe(`Name of the screenshot. Default value is "${DEFAULT_SCREENSHOT_NAME}". Note that final saved/exported file name is in the "{name}-{time}.{type}" format in which "{time}" is in the "YYYYMMDD-HHmmss" format.`).optional().default(DEFAULT_SCREENSHOT_NAME),selector:z6.string().describe("CSS selector for element to take screenshot.").optional(),fullPage:z6.boolean().describe('Whether to take a screenshot of the full scrollable page, instead of the currently visible viewport (default: "false").').optional().default(!1),type:z6.enum(getEnumKeyTuples(ScreenshotType)).transform(createEnumTransformer(ScreenshotType)).describe(`Page format. Valid values are: ${getEnumKeyTuples(ScreenshotType)}`).optional().default(DEFAULT_SCREENSHOT_TYPE),quality:z6.number().int().min(0).max(DEFAULT_SCREENSHOT_QUALITY).describe("The quality of the image, between 0-100. Not applicable to png images.").optional(),includeBase64:z6.boolean().describe("If true, includes base64 image data in the response (increases payload size). Default is false. The screenshot is always saved to disk; use the returned file path to read it. Set to true ONLY when the assistant cannot access the MCP server file system (e.g. remote/container). Avoid setting to true otherwise.").optional().default(!1),annotate:z6.boolean().optional().default(!1).describe("When true, overlay numbered labels on interactive elements (from last a11y_take-aria-snapshot refs). Labels [1],[2],... map to refs e1,e2,... When selector is also set, only elements overlapping the scoped element are annotated and returned; annotation box coordinates are relative to that element."),annotateContent:z6.boolean().optional().default(!1).describe("When true with annotate, also include content elements (headings, list items, etc.) in the overlay. Uses interactiveOnly: false when building refs for this screenshot."),annotateCursorInteractive:z6.boolean().optional().default(!1).describe("When true with annotate, also include cursor-interactive elements (clickable/focusable by CSS but no ARIA role) in the overlay. Takes a fresh ARIA snapshot with cursorInteractive for this screenshot.")}}outputSchema(){return{filePath:z6.string().describe("Full path of the saved screenshot file."),image:z6.object({data:z6.any().describe("Base64-encoded image data."),mimeType:z6.string().describe("MIME type of the image.")}).optional().describe('Image data included only when "includeBase64" input parameter is set to true.'),annotations:z6.array(z6.object({ref:z6.string(),number:z6.number(),role:z6.string(),name:z6.string().optional(),box:z6.object({x:z6.number(),y:z6.number(),width:z6.number(),height:z6.number()})})).optional().describe("When annotate is true, list of refs and bounding boxes. When selector is set: only annotations overlapping the element, box relative to that element. When fullPage is true: box is document-relative (matches the full-page image). Otherwise: viewport-relative.")}}_scaleImageToSize(image,size){let{data:src,width:w1,height:h1}=image,w2=Math.max(1,Math.floor(size.width)),h2=Math.max(1,Math.floor(size.height));if(w1===w2&&h1===h2)return image;if(w1<=0||h1<=0)throw new Error("Invalid input image");if(size.width<=0||size.height<=0||!isFinite(size.width)||!isFinite(size.height))throw new Error("Invalid output dimensions");let clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v,weights=(t,o)=>{let t2=t*t,t3=t2*t;o[0]=-.5*t+1*t2-.5*t3,o[1]=1-2.5*t2+1.5*t3,o[2]=.5*t+2*t2-1.5*t3,o[3]=-.5*t2+.5*t3},srcRowStride=w1*4,dstRowStride=w2*4,xOff=new Int32Array(w2*4),xW=new Float32Array(w2*4),wx=new Float32Array(4),xScale=w1/w2;for(let x=0;x<w2;x++){let sx=(x+.5)*xScale-.5,sxi=Math.floor(sx),t=sx-sxi;weights(t,wx);let b=x*4,i0=clamp(sxi-1,0,w1-1),i1=clamp(sxi+0,0,w1-1),i2=clamp(sxi+1,0,w1-1),i3=clamp(sxi+2,0,w1-1);xOff[b+0]=i0<<2,xOff[b+1]=i1<<2,xOff[b+2]=i2<<2,xOff[b+3]=i3<<2,xW[b+0]=wx[0],xW[b+1]=wx[1],xW[b+2]=wx[2],xW[b+3]=wx[3]}let yRow=new Int32Array(h2*4),yW=new Float32Array(h2*4),wy=new Float32Array(4),yScale=h1/h2;for(let y=0;y<h2;y++){let sy=(y+.5)*yScale-.5,syi=Math.floor(sy),t=sy-syi;weights(t,wy);let b=y*4,j0=clamp(syi-1,0,h1-1),j1=clamp(syi+0,0,h1-1),j2=clamp(syi+1,0,h1-1),j3=clamp(syi+2,0,h1-1);yRow[b+0]=j0*srcRowStride,yRow[b+1]=j1*srcRowStride,yRow[b+2]=j2*srcRowStride,yRow[b+3]=j3*srcRowStride,yW[b+0]=wy[0],yW[b+1]=wy[1],yW[b+2]=wy[2],yW[b+3]=wy[3]}let dst=new Uint8Array(w2*h2*4);for(let y=0;y<h2;y++){let yb=y*4,rb0=yRow[yb+0],rb1=yRow[yb+1],rb2=yRow[yb+2],rb3=yRow[yb+3],wy0=yW[yb+0],wy1=yW[yb+1],wy2=yW[yb+2],wy3=yW[yb+3],dstBase=y*dstRowStride;for(let x=0;x<w2;x++){let xb=x*4,xo0=xOff[xb+0],xo1=xOff[xb+1],xo2=xOff[xb+2],xo3=xOff[xb+3],wx0=xW[xb+0],wx1=xW[xb+1],wx2=xW[xb+2],wx3=xW[xb+3],di=dstBase+(x<<2);for(let c=0;c<4;c++){let r0=src[rb0+xo0+c]*wx0+src[rb0+xo1+c]*wx1+src[rb0+xo2+c]*wx2+src[rb0+xo3+c]*wx3,r1=src[rb1+xo0+c]*wx0+src[rb1+xo1+c]*wx1+src[rb1+xo2+c]*wx2+src[rb1+xo3+c]*wx3,r2=src[rb2+xo0+c]*wx0+src[rb2+xo1+c]*wx1+src[rb2+xo2+c]*wx2+src[rb2+xo3+c]*wx3,r3=src[rb3+xo0+c]*wx0+src[rb3+xo1+c]*wx1+src[rb3+xo2+c]*wx2+src[rb3+xo3+c]*wx3,v=r0*wy0+r1*wy1+r2*wy2+r3*wy3;dst[di+c]=v<0?0:v>255?255:v|0}}}return{data:Buffer.from(dst.buffer),width:w2,height:h2}}_scaleImageToFitMessage(buffer,screenshotType){let MAX_PIXELS=12058624e-1,MAX_LINEAR_SIZE=1568,image=screenshotType==="png"?PNG.sync.read(buffer):jpegjs.decode(buffer,{maxMemoryUsageInMB:512}),pixels=image.width*image.height,shrink=Math.min(MAX_LINEAR_SIZE/image.width,MAX_LINEAR_SIZE/image.height,Math.sqrt(MAX_PIXELS/pixels));shrink>1&&(shrink=1);let width=image.width*shrink|0,height=image.height*shrink|0,scaledImage=this._scaleImageToSize(image,{width,height}),result,currentType=screenshotType,quality=screenshotType==="png"?75:70;screenshotType==="png"?(result=jpegjs.encode(scaledImage,quality).data,currentType="jpeg"):result=jpegjs.encode(scaledImage,quality).data;let iterations=0,MAX_ITERATIONS=5;for(;result.length>819200&&iterations<MAX_ITERATIONS;)quality=Math.max(50,quality-10),quality<=50&&result.length>819200&&(shrink*=.85,width=Math.max(200,image.width*shrink|0),height=Math.max(200,image.height*shrink|0),scaledImage=this._scaleImageToSize(image,{width,height})),result=jpegjs.encode(scaledImage,quality).data,iterations++;return result}async handle(context,args){let screenshotType=args.type||DEFAULT_SCREENSHOT_TYPE,filename=`${args.name||DEFAULT_SCREENSHOT_NAME}-${formattedTimeForFilename()}.${screenshotType}`,filePath=path2.resolve(args.outputPath,filename),quality=screenshotType==="png"?void 0:args.quality??DEFAULT_SCREENSHOT_QUALITY,overlayInjected=!1,annotations,scopedElement=null;if(args.selector&&(scopedElement=await context.page.$(args.selector),!scopedElement))throw new Error(`Element not found: ${args.selector}`);if(args.annotate){let refMap=context.getRefMap(),wantContent=args.annotateContent===!0,wantCursorInteractive=args.annotateCursorInteractive===!0;if(Object.keys(refMap).length===0||wantCursorInteractive||wantContent){let raw=await context.page.locator("body").ariaSnapshot(),{refs}=processAriaTreeWithRefs(raw,{interactiveOnly:!wantContent}),mergedRefs={...refs};if(wantCursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>{let n=parseInt(k.replace(/^e/,""),10)||0;return n>m?n:m},0);cursorEntries.forEach((entry,i)=>{let ref=`e${maxNum+1+i}`;mergedRefs[ref]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth}})}}context.setRefMap(mergedRefs),refMap=mergedRefs}let entries=Object.entries(refMap),allBoxes=[];for(let[ref,data]of entries){let locator=getLocatorFromRef(context.page,refMap,ref);if(!locator)continue;let box=await locator.boundingBox({timeout:ANNOTATE_BOX_TIMEOUT_MS}).catch(()=>null);if(!box||box.width===0||box.height===0)continue;let num=parseInt(ref.replace(/^e/,""),10)||0;allBoxes.push({ref,number:num,role:data.role,name:data.name,box:{x:Math.round(box.x),y:Math.round(box.y),width:Math.round(box.width),height:Math.round(box.height)}})}allBoxes.sort((a,b)=>a.number-b.number);let boxes=allBoxes,overlayBoxesViewport=allBoxes;if(scopedElement){let elementBox=await scopedElement.boundingBox();if(elementBox&&elementBox.width>0&&elementBox.height>0){let overlapping=allBoxes.filter(a=>{let b=a.box;return b.x<elementBox.x+elementBox.width&&b.x+b.width>elementBox.x&&b.y<elementBox.y+elementBox.height&&b.y+b.height>elementBox.y});overlayBoxesViewport=overlapping,boxes=overlapping.map(a=>{let b=a.box,relX=Math.max(0,b.x-elementBox.x),relY=Math.max(0,b.y-elementBox.y),relRight=Math.min(b.x+b.width-elementBox.x,elementBox.width),relBottom=Math.min(b.y+b.height-elementBox.y,elementBox.height);return{...a,box:{x:Math.round(relX),y:Math.round(relY),width:Math.round(Math.max(0,relRight-relX)),height:Math.round(Math.max(0,relBottom-relY))}}})}}if(boxes.length>0){let overlayPayload={data:overlayBoxesViewport.map(a=>({number:a.number,x:a.box.x,y:a.box.y,width:a.box.width,height:a.box.height})),id:ANNOTATION_OVERLAY_ID};if(await context.page.evaluate(({data,id})=>{let sx=window.scrollX||0,sy=window.scrollY||0,c=document.createElement("div");c.id=id,c.style.cssText="position:absolute;top:0;left:0;width:0;height:0;pointer-events:none;z-index:2147483647;";for(let it of data){let dx=it.x+sx,dy=it.y+sy,b=document.createElement("div");b.style.cssText=`position:absolute;left:${dx}px;top:${dy}px;width:${it.width}px;height:${it.height}px;border:2px solid rgba(255,0,0,0.8);box-sizing:border-box;pointer-events:none;`;let l=document.createElement("div");l.textContent=String(it.number);let labelTop=dy<14?"2px":"-14px";l.style.cssText=`position:absolute;top:${labelTop};left:-2px;background:rgba(255,0,0,0.9);color:#fff;font:bold 11px/14px monospace;padding:0 4px;border-radius:2px;white-space:nowrap;`,b.appendChild(l),c.appendChild(b)}document.documentElement.appendChild(c)},overlayPayload),overlayInjected=!0,args.fullPage&&!scopedElement&&boxes.length>0){let scroll=await context.page.evaluate(()=>({x:window.scrollX||0,y:window.scrollY||0}));annotations=boxes.map(a=>({...a,box:{x:a.box.x+scroll.x,y:a.box.y+scroll.y,width:a.box.width,height:a.box.height}}))}else annotations=boxes}}let options={path:filePath,type:screenshotType,fullPage:!!args.fullPage,quality};scopedElement&&(options.element=scopedElement);try{let screenshot=await context.page.screenshot(options),result={filePath,...annotations&&annotations.length>0?{annotations}:{}};return args.includeBase64&&(result.image={data:this._scaleImageToFitMessage(screenshot,screenshotType),mimeType:`image/${screenshotType}`}),result}finally{overlayInjected&&await context.page.evaluate(id=>{let el=document.getElementById(id);el&&el.remove()},ANNOTATION_OVERLAY_ID).catch(()=>{})}}};var tools2=[new GetAsHtml,new GetAsText,new SaveAsPdf,new TakeScreenshot];import{z as z7}from"zod";var DEFAULT_DEBUG_CONFIG={maxSnapshots:1e3,maxCallStackDepth:20,maxFramesWithScopes:5,maxAsyncStackSegments:10,maxFramesPerAsyncSegment:10,maxDOMMutations:100,maxDOMHtmlSnippetLength:200,maxPendingRequests:1e3,maxResponseBodyLength:1e4,networkCleanupTimeoutMs:5e3},STORE_BY_CONTEXT=new WeakMap;function _generateId(){let t=Date.now(),r=Math.floor(Math.random()*1e6);return`${t.toString(36)}-${r.toString(36)}`}function _locationKey(urlPattern,lineNumber,columnNumber){return`${urlPattern}:${lineNumber}:${columnNumber}`}function _evaluateHitCondition(hitCondition,hitCount){try{let condition=hitCondition.trim();return/^[=<>!%]/.test(condition)&&(condition=`hitCount ${condition}`),!!new Function("hitCount",`return (${condition});`)(hitCount)}catch{return!1}}async function _evaluateWatchExpressionsOnFrame(v8Api,callFrameId,watchExpressions){let results={};for(let watch of watchExpressions.values())try{let result=await v8Api.evaluateOnCallFrame(callFrameId,watch.expression);if(result.exceptionDetails)results[watch.expression]=`[Error: ${result.exceptionDetails.text||"Evaluation failed"}]`;else{let value=await v8Api.extractValueDeep(result.result,2);results[watch.expression]=value}}catch(e){results[watch.expression]=`[Error: ${e.message||"Unknown error"}]`}return results}function _ensureStore(ctx,page,v8Options,config){let existing=STORE_BY_CONTEXT.get(ctx);if(existing)return existing;let v8Api=new V8Api(page,v8Options),sourceMapResolver=new SourceMapResolver(page),mergedConfig={...DEFAULT_DEBUG_CONFIG,...config},store={v8Api,sourceMapResolver,probes:new Map,locationIndex:new Map,watchExpressions:new Map,domBreakpoints:new Map,networkBreakpoints:new Map,snapshots:[],snapshotSequence:0,config:mergedConfig,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none",networkInterceptionEnabled:!1,recentDOMMutations:[]};return STORE_BY_CONTEXT.set(ctx,store),store}function _getStore(ctx){return STORE_BY_CONTEXT.get(ctx)}async function _captureScopes(v8Api,callFrame,maxDepth=3){let scopeSnapshots=[];for(let scope of callFrame.scopeChain)if(!(scope.type==="global"||scope.type==="script"||scope.type==="with"||scope.type==="eval"||scope.type==="wasm-expression-stack")){if(scopeSnapshots.length>=maxDepth)break;try{let variables=await v8Api.getScopeVariables(scope),scopeVariables=[];for(let[name,value]of Object.entries(variables))scopeVariables.push({name,value,type:typeof value});scopeSnapshots.push({type:scope.type,name:scope.name,variables:scopeVariables})}catch{}}return scopeSnapshots}async function _callFrameToSnapshot(v8Api,frame,captureScopes,sourceMapResolver){let scopes=[];captureScopes&&(scopes=await _captureScopes(v8Api,frame));let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}return{functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,scriptId:frame.location.scriptId,scopes,originalLocation}}function _convertAsyncStackTrace(asyncTrace,sourceMapResolver,maxSegments,maxFramesPerSegment){if(!asyncTrace)return;let maxSegs=maxSegments??DEFAULT_DEBUG_CONFIG.maxAsyncStackSegments,maxFrames=maxFramesPerSegment??DEFAULT_DEBUG_CONFIG.maxFramesPerAsyncSegment,segments=[],currentTrace=asyncTrace,segmentCount=0;for(;currentTrace&&segmentCount<maxSegs;){let asyncFrames=[];for(let frame of currentTrace.callFrames.slice(0,maxFrames)){let originalLocation;if(sourceMapResolver){let resolved=sourceMapResolver.generatedToOriginal(frame.location.scriptId,frame.location.lineNumber,frame.location.columnNumber??0);resolved&&(originalLocation={source:resolved.source,line:resolved.line+1,column:resolved.column!==void 0?resolved.column+1:void 0,name:resolved.name})}asyncFrames.push({functionName:frame.functionName||"(anonymous)",url:frame.url||"",lineNumber:frame.location.lineNumber+1,columnNumber:frame.location.columnNumber!==void 0?frame.location.columnNumber+1:void 0,originalLocation})}asyncFrames.length>0&&segments.push({description:currentTrace.description,callFrames:asyncFrames}),currentTrace=currentTrace.parent,segmentCount++}if(segments.length!==0)return{segments}}async function enableDebugging2(ctx,page,options){let store=_ensureStore(ctx,page,options?.v8Options,options?.config);if(!store.enabled){await store.v8Api.enable(),store.v8Api.on("scriptParsed",script=>{store.sourceMapResolver.registerScript(script)});for(let script of store.v8Api.getScripts())store.sourceMapResolver.registerScript(script);store.v8Api.on("paused",async event=>{let startTime=Date.now();try{let isException=event.reason==="exception"||event.reason==="promiseRejection",isDOMBreakpoint=event.reason==="DOM",hitBreakpointIds=event.hitBreakpoints||[],hitProbes=[],hitDOMBreakpoint,domChangeInfo;if(isDOMBreakpoint&&event.data){let domData=event.data;for(let domBp of store.domBreakpoints.values())if(domBp.enabled&&(domBp.nodeId===domData.nodeId||!domData.nodeId)){hitDOMBreakpoint=domBp,domChangeInfo={type:domBp.type,selector:domBp.selector,targetNode:domData.targetNode?`<${domData.targetNode.nodeName?.toLowerCase()||"unknown"}>`:void 0,attributeName:domData.attributeName||domBp.attributeName};break}}if(hitDOMBreakpoint&&domChangeInfo)try{let mutationData=await page.evaluate(breakpointId=>{let win=window;if(!win.__domBreakpointMutations)return null;let mutations=win.__domBreakpointMutations;for(let i=mutations.length-1;i>=0;i--)if(mutations[i].breakpointId===breakpointId)return mutations[i];return null},hitDOMBreakpoint.id);mutationData&&(domChangeInfo.oldValue=mutationData.oldValue,domChangeInfo.newValue=mutationData.newValue,domChangeInfo.targetNode=mutationData.targetOuterHTML,mutationData.attributeName&&(domChangeInfo.attributeName=mutationData.attributeName))}catch{}for(let probe of store.probes.values()){if(!probe.enabled)continue;if(probe.v8BreakpointIds.some(id=>hitBreakpointIds.includes(id))){let conditionMet=!0;probe.hitCondition&&(conditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1)),hitProbes.push({probe,conditionMet})}}let shouldCaptureBreakpoint=hitProbes.some(p=>p.conditionMet),shouldCaptureException=isException&&store.exceptionBreakpoint!=="none",shouldCaptureDOMBreakpoint=hitDOMBreakpoint!==void 0;for(let{probe}of hitProbes)probe.hitCount++,probe.lastHitAt=Date.now();if(hitDOMBreakpoint&&(hitDOMBreakpoint.hitCount++,hitDOMBreakpoint.lastHitAt=Date.now()),(shouldCaptureBreakpoint||shouldCaptureException||shouldCaptureDOMBreakpoint)&&event.callFrames.length>0){let topFrame=event.callFrames[0],exceptionInfo;if(isException&&event.data){let excData=event.data;exceptionInfo={type:event.reason==="promiseRejection"?"promiseRejection":"exception",message:excData.description||excData.value||String(excData),name:excData.className,stack:excData.description}}let originalLocation,resolvedLoc=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.location.columnNumber??0);resolvedLoc&&(originalLocation={source:resolvedLoc.source,line:resolvedLoc.line+1,column:resolvedLoc.column!==void 0?resolvedLoc.column+1:void 0,name:resolvedLoc.name});let baseSnapshot={url:topFrame.url||"",lineNumber:topFrame.location.lineNumber+1,columnNumber:topFrame.location.columnNumber!==void 0?topFrame.location.columnNumber+1:void 0,originalLocation,exception:exceptionInfo,domChange:domChangeInfo,captureTimeMs:Date.now()-startTime},callStackFull=[],framesToProcess=event.callFrames.slice(0,store.config.maxCallStackDepth);for(let i=0;i<framesToProcess.length;i++){let frame=framesToProcess[i],captureScopes=i<store.config.maxFramesWithScopes;callStackFull.push(await _callFrameToSnapshot(store.v8Api,frame,captureScopes,store.sourceMapResolver))}let asyncStackTraceFull=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment),probesToCapture=hitProbes.filter(p=>p.conditionMet),hasExceptionOrDOM=shouldCaptureException||shouldCaptureDOMBreakpoint,snapshotCount=probesToCapture.length+(hasExceptionOrDOM?1:0);for(let s=0;s<snapshotCount;s++){let probeId,isLogpoint,logResult,callStack,asyncStackTrace,watchResults;if(s<probesToCapture.length){let{probe}=probesToCapture[s];if(probeId=probe.id,isLogpoint=probe.kind==="logpoint",isLogpoint&&probe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,probe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}else logResult=void 0;isLogpoint?(callStack=[],asyncStackTrace=void 0,watchResults=void 0):(callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0)}else probeId=hitDOMBreakpoint?.id??"__exception__",isLogpoint=!1,logResult=void 0,callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0;let snapshot={id:_generateId(),probeId,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,...baseSnapshot,callStack,asyncStackTrace,logResult,watchResults};store.snapshots.push(snapshot)}store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots)}}finally{await store.v8Api.resume()}}),store.enabled=!0,store.sourceMapResolver.loadAllSourceMaps().then(()=>{store.sourceMapsLoaded=!0}).catch(()=>{})}}function isDebuggingEnabled2(ctx){return _getStore(ctx)?.enabled??!1}async function resolveSourceLocation2(ctx,page,url,line,column=1){let store=_ensureStore(ctx,page);store.enabled||await enableDebugging2(ctx,page);let resolved=await store.sourceMapResolver.resolveLocationByUrl(url,line,column);return resolved?{source:resolved.source,line:resolved.line+1,column:resolved.column+1,name:resolved.name}:null}async function setExceptionBreakpoint2(ctx,state){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");await store.v8Api.setPauseOnExceptions(state),store.exceptionBreakpoint=state}function getExceptionBreakpoint2(ctx){return _getStore(ctx)?.exceptionBreakpoint??"none"}function hasSourceMaps2(ctx){return _getStore(ctx)?.sourceMapResolver.hasSourceMaps()??!1}async function createProbe2(ctx,options){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");let probeId=_generateId(),columnNormalized=options.columnNumber??1,locationKey=_locationKey(options.urlPattern,options.lineNumber,columnNormalized),existingEntry=store.locationIndex.get(locationKey);if(existingEntry){existingEntry.refCount++;let probe2={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[existingEntry.breakpointId],resolvedLocations:existingEntry.resolvedLocations,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe2),probe2}let fullCondition;options.condition?fullCondition=`(${options.condition})`:fullCondition="true";let line0based=options.lineNumber-1,column0based=columnNormalized-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)try{breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1}catch{let scriptUrl=store.sourceMapResolver.getScriptUrl(resolved.scriptId);if(scriptUrl){let result=await store.v8Api.setBreakpointByUrl({url:scriptUrl,lineNumber:resolved.location.line,columnNumber:resolved.location.column,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}else throw new Error("Failed to set breakpoint at resolved location and could not fall back (script URL unknown). A probe may already exist at this line; remove it first or use a different line.")}else{let urlRegex=options.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),result=await store.v8Api.setBreakpointByUrl({urlRegex,lineNumber:options.lineNumber-1,columnNumber:options.columnNumber?options.columnNumber-1:void 0,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}store.locationIndex.set(locationKey,{breakpointId,resolvedLocations:resolvedLocationsCount,refCount:1});let probe={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[breakpointId],resolvedLocations:resolvedLocationsCount,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe),probe}async function removeProbe2(ctx,probeId){let store=_getStore(ctx);if(!store)return!1;let probe=store.probes.get(probeId);if(!probe)return!1;let locationKey=_locationKey(probe.urlPattern,probe.lineNumber,probe.columnNumber??1),entry=store.locationIndex.get(locationKey);if(entry&&(entry.refCount--,entry.refCount===0)){try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.delete(locationKey)}return store.probes.delete(probeId),!0}function listProbes2(ctx){let store=_getStore(ctx);return store?Array.from(store.probes.values()):[]}function getProbe(ctx,probeId){let store=_getStore(ctx);if(store)return store.probes.get(probeId)}function getSnapshots2(ctx){let store=_getStore(ctx);return store?[...store.snapshots]:[]}function getSnapshotsByProbe2(ctx,probeId){let store=_getStore(ctx);return store?store.snapshots.filter(s=>s.probeId===probeId):[]}function clearSnapshotsByProbe(ctx,probeId){let store=_getStore(ctx);if(!store)return 0;let before=store.snapshots.length;return store.snapshots=store.snapshots.filter(s=>s.probeId!==probeId),before-store.snapshots.length}function getSnapshotStats2(ctx){let store=_getStore(ctx);if(!store||store.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let snapshotsByProbe={},totalCaptureTime=0;for(let snapshot of store.snapshots)snapshotsByProbe[snapshot.probeId]=(snapshotsByProbe[snapshot.probeId]||0)+1,totalCaptureTime+=snapshot.captureTimeMs;return{totalSnapshots:store.snapshots.length,snapshotsByProbe,oldestTimestamp:store.snapshots[0].timestamp,newestTimestamp:store.snapshots[store.snapshots.length-1].timestamp,averageCaptureTimeMs:totalCaptureTime/store.snapshots.length}}function addWatchExpression2(ctx,expression){let store=_getStore(ctx);if(!store)throw new Error("Debug store not initialized");let id=_generateId(),watchExpr={id,expression,createdAt:Date.now()};return store.watchExpressions.set(id,watchExpr),watchExpr}function removeWatchExpression2(ctx,watchExpressionId){let store=_getStore(ctx);return store?store.watchExpressions.delete(watchExpressionId):!1}function listWatchExpressions2(ctx){let store=_getStore(ctx);return store?Array.from(store.watchExpressions.values()):[]}function clearWatchExpressions2(ctx){let store=_getStore(ctx);if(!store)return 0;let count=store.watchExpressions.size;return store.watchExpressions.clear(),count}async function setDOMBreakpoint(ctx,page,options){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");let cdp=await store.v8Api.getCdp();await cdp.send("DOM.enable");let{root}=await cdp.send("DOM.getDocument",{depth:0}),{nodeId}=await cdp.send("DOM.querySelector",{nodeId:root.nodeId,selector:options.selector});if(!nodeId||nodeId===0)throw new Error(`Element not found: ${options.selector}`);let cdpType=options.type==="subtree-modified"?"subtree-modified":options.type==="attribute-modified"?"attribute-modified":"node-removed";await cdp.send("DOMDebugger.setDOMBreakpoint",{nodeId,type:cdpType});let id=_generateId(),domBreakpoint={id,selector:options.selector,type:options.type,attributeName:options.attributeName,enabled:!0,nodeId,hitCount:0,createdAt:Date.now()};return store.domBreakpoints.set(id,domBreakpoint),await page.evaluate(params=>{let selector=params.selector,breakpointId=params.breakpointId,type=params.type,attrName=params.attrName,maxMutations=params.maxMutations,maxHtmlSnippetLength=params.maxHtmlSnippetLength,element=document.querySelector(selector);if(!element)return;let win=window;win.__domBreakpointData=win.__domBreakpointData||{},win.__domBreakpointMutations=win.__domBreakpointMutations||[],win.__domBreakpointData[breakpointId]={selector,type,attrName,currentAttrs:{}};for(let attr of element.attributes)win.__domBreakpointData[breakpointId].currentAttrs[attr.name]=attr.value;let observer=new MutationObserver(mutations=>{for(let mutation of mutations){let target=mutation.target;if(mutation.type==="attributes"){let attrNameChanged=mutation.attributeName||"";if(attrName&&attrName!==attrNameChanged)continue;let mutationRecord={breakpointId,selector,type:"attribute-modified",attributeName:attrNameChanged,oldValue:mutation.oldValue,newValue:target.getAttribute(attrNameChanged),targetOuterHTML:target.outerHTML.substring(0,maxHtmlSnippetLength),timestamp:Date.now()};win.__domBreakpointMutations.push(mutationRecord),win.__domBreakpointMutations.length>maxMutations&&win.__domBreakpointMutations.shift(),win.__domBreakpointData[breakpointId].currentAttrs[attrNameChanged]=target.getAttribute(attrNameChanged)}else if(mutation.type==="childList"){let mutationRecord={breakpointId,selector,type:"subtree-modified",addedNodes:mutation.addedNodes.length,removedNodes:mutation.removedNodes.length,targetOuterHTML:target.outerHTML.substring(0,maxHtmlSnippetLength),timestamp:Date.now()};win.__domBreakpointMutations.push(mutationRecord),win.__domBreakpointMutations.length>maxMutations&&win.__domBreakpointMutations.shift()}}}),config={attributes:type==="attribute-modified",attributeOldValue:type==="attribute-modified",childList:type==="subtree-modified"||type==="node-removed",subtree:type==="subtree-modified"};attrName&&(config.attributeFilter=[attrName]),observer.observe(element,config),win.__domBreakpointObservers=win.__domBreakpointObservers||{},win.__domBreakpointObservers[breakpointId]=observer},{selector:options.selector,breakpointId:id,type:options.type,attrName:options.attributeName,maxMutations:store.config.maxDOMMutations,maxHtmlSnippetLength:store.config.maxDOMHtmlSnippetLength}),domBreakpoint}async function removeDOMBreakpoint(ctx,domBreakpointId,page){let store=_getStore(ctx);if(!store)return!1;let domBreakpoint=store.domBreakpoints.get(domBreakpointId);if(!domBreakpoint||!domBreakpoint.nodeId)return!1;try{let cdp=await store.v8Api.getCdp(),cdpType=domBreakpoint.type==="subtree-modified"?"subtree-modified":domBreakpoint.type==="attribute-modified"?"attribute-modified":"node-removed";await cdp.send("DOMDebugger.removeDOMBreakpoint",{nodeId:domBreakpoint.nodeId,type:cdpType})}catch{}if(page)try{await page.evaluate(breakpointId=>{let win=window;win.__domBreakpointObservers&&win.__domBreakpointObservers[breakpointId]&&(win.__domBreakpointObservers[breakpointId].disconnect(),delete win.__domBreakpointObservers[breakpointId]),win.__domBreakpointData&&delete win.__domBreakpointData[breakpointId],win.__domBreakpointMutations&&(win.__domBreakpointMutations=win.__domBreakpointMutations.filter(m=>m.breakpointId!==breakpointId))},domBreakpointId)}catch{}return store.domBreakpoints.delete(domBreakpointId)}function listDOMBreakpoints(ctx){let store=_getStore(ctx);return store?Array.from(store.domBreakpoints.values()):[]}async function clearDOMBreakpoints(ctx,page){let store=_getStore(ctx);if(!store)return 0;let ids=Array.from(store.domBreakpoints.keys());for(let id of ids)await removeDOMBreakpoint(ctx,id,page);return ids.length}async function _enableNetworkInterception(store,page){if(store.networkInterceptionEnabled)return;let cdp=await store.v8Api.getCdp();await cdp.send("Fetch.enable",{patterns:[{urlPattern:"*",requestStage:"Request"}]}),cdp.on("Fetch.requestPaused",async event=>{let requestId=event.requestId,requestUrl=event.request.url,method=event.request.method;try{let matchedBreakpoint;for(let bp of store.networkBreakpoints.values()){if(!bp.enabled)continue;let unescapedPattern=bp.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1");if(new RegExp(unescapedPattern.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*")).test(requestUrl)&&!(bp.method&&bp.method.toUpperCase()!==method.toUpperCase())&&bp.timing==="request"){matchedBreakpoint=bp;break}}if(matchedBreakpoint&&matchedBreakpoint.timing==="request"){let requestInfo={url:requestUrl,method,requestHeaders:event.request.headers,requestBody:event.request.postData,resourceType:event.resourceType,timing:"request"},snapshot={id:_generateId(),probeId:matchedBreakpoint.id,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,url:requestUrl,lineNumber:0,networkRequest:requestInfo,callStack:[],captureTimeMs:0};store.watchExpressions.size>0&&(snapshot.watchResults=await _evaluateWatchExpressions(store,page)),store.snapshots.push(snapshot),store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots),matchedBreakpoint.hitCount++,matchedBreakpoint.lastHitAt=Date.now()}await cdp.send("Fetch.continueRequest",{requestId})}catch{try{await cdp.send("Fetch.continueRequest",{requestId})}catch{}}}),await cdp.send("Network.enable");let pendingRequests=new Map;cdp.on("Network.requestWillBeSent",event=>{if(pendingRequests.set(event.requestId,{method:event.request.method,postData:event.request.postData}),pendingRequests.size>store.config.maxPendingRequests){let firstKey=pendingRequests.keys().next().value;firstKey&&pendingRequests.delete(firstKey)}}),cdp.on("Network.responseReceived",async event=>{let requestId=event.requestId,requestUrl=event.response.url,requestInfo=pendingRequests.get(requestId),method=requestInfo?.method||event.type||"GET",status=event.response.status;for(let bp of store.networkBreakpoints.values()){if(!bp.enabled||bp.timing!=="response")continue;let unescapedPattern=bp.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1");if(!new RegExp(unescapedPattern.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*")).test(requestUrl)||bp.method&&bp.method.toUpperCase()!==method.toUpperCase()||bp.onError&&status<400)continue;let responseBody;try{let bodyResult=await cdp.send("Network.getResponseBody",{requestId});bodyResult.base64Encoded?responseBody=Buffer.from(bodyResult.body,"base64").toString("utf-8"):responseBody=bodyResult.body,responseBody&&responseBody.length>store.config.maxResponseBodyLength&&(responseBody=responseBody.substring(0,store.config.maxResponseBodyLength)+"... [truncated]")}catch{}let networkRequestInfo={url:requestUrl,method,requestBody:requestInfo?.postData,status,statusText:event.response.statusText,responseHeaders:event.response.headers,responseBody,resourceType:event.type,timing:"response"},snapshot={id:_generateId(),probeId:bp.id,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,url:requestUrl,lineNumber:0,networkRequest:networkRequestInfo,callStack:[],captureTimeMs:0};store.watchExpressions.size>0&&(snapshot.watchResults=await _evaluateWatchExpressions(store,page)),store.snapshots.push(snapshot),store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots),bp.hitCount++,bp.lastHitAt=Date.now(),pendingRequests.delete(requestId);break}}),cdp.on("Network.loadingFinished",event=>{setTimeout(()=>{pendingRequests.delete(event.requestId)},store.config.networkCleanupTimeoutMs)}),store.networkInterceptionEnabled=!0}async function _evaluateWatchExpressions(store,page){let results={};for(let watch of store.watchExpressions.values())try{let value=await page.evaluate(expr=>{try{return(0,eval)(expr)}catch(e){return`[Error: ${e.message}]`}},watch.expression);results[watch.expression]=value}catch(e){results[watch.expression]=`[Error: ${e.message}]`}return results}async function setNetworkBreakpoint(ctx,page,options){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");await _enableNetworkInterception(store,page);let id=_generateId(),networkBreakpoint={id,urlPattern:options.urlPattern,method:options.method,timing:options.timing||"request",onError:options.onError,enabled:!0,hitCount:0,createdAt:Date.now()};return store.networkBreakpoints.set(id,networkBreakpoint),networkBreakpoint}function removeNetworkBreakpoint(ctx,networkBreakpointId){let store=_getStore(ctx);return store?store.networkBreakpoints.delete(networkBreakpointId):!1}function listNetworkBreakpoints(ctx){let store=_getStore(ctx);return store?Array.from(store.networkBreakpoints.values()):[]}function clearNetworkBreakpoints(ctx){let store=_getStore(ctx);if(!store)return 0;let count=store.networkBreakpoints.size;return store.networkBreakpoints.clear(),count}var Status=class{name(){return"debug_status"}description(){return`
285
285
  Returns the current debugging status including:
@@ -503,7 +503,7 @@ How to use it effectively:
503
503
  - Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration (e.g. AWS_REGION, inference profile, etc).
504
504
 
505
505
  This tool is designed for UI regression checks, design parity checks, and "does this page still match the intended layout?" validation.
506
- `.trim()}inputSchema(){return{figmaFileKey:z41.string().min(1).describe("Figma file key (the part after /file/ in Figma URL)."),figmaNodeId:z41.string().min(1).describe('Figma node id to render (frame/component node id like "12:34").'),selector:z41.string().optional().describe("Optional CSS selector to compare only a specific region of the page."),fullPage:z41.boolean().optional().default(DEFAULT_FULL_PAGE).describe("If true, captures the full scrollable page. Ignored when selector is provided."),figmaScale:z41.number().int().positive().optional().describe("Optional scale factor for Figma raster export (e.g. 1, 2, 3)."),figmaFormat:z41.enum(["png","jpg"]).optional().describe("Optional raster format for Figma snapshot."),weights:z41.object({mssim:z41.number().positive().optional().describe("Weight for MSSIM signal."),imageEmbedding:z41.number().positive().optional().describe("Weight for image embedding signal."),textEmbedding:z41.number().positive().optional().describe("Weight for vision\u2192text\u2192text embedding signal.")}).optional().describe("Optional weights to combine signals. Only active signals participate."),mssimMode:z41.enum(["raw","semantic"]).optional().default(DEFAULT_MSSIM_MODE).describe("MSSIM mode. semantic is more robust for real-data vs design-data comparisons."),maxDim:z41.number().int().positive().optional().describe("Optional preprocessing max dimension forwarded to compare pipeline."),jpegQuality:z41.number().int().min(50).max(100).optional().describe("Optional JPEG quality forwarded to compare pipeline (used only when JPEG encoding is selected internally).")}}outputSchema(){return{score:z41.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:z41.array(z41.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:z41.object({pageUrl:z41.string().describe("URL of the page that was compared."),pageTitle:z41.string().describe("Title of the page that was compared."),figmaFileKey:z41.string().describe("Figma file key used for the design snapshot."),figmaNodeId:z41.string().describe("Figma node id used for the design snapshot."),selector:z41.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:z41.boolean().describe("Whether the page screenshot was full-page."),pageImageType:z41.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:z41.enum(["png","jpeg"]).describe("Image type of the captured Figma snapshot.")}).describe("Metadata about what was compared.")}}async handle(context,args){let pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),figmaFormat=args.figmaFormat??"png",figmaScale=typeof args.figmaScale=="number"?args.figmaScale:void 0,figmaSnapshot=await getFigmaDesignScreenshot({fileKey:args.figmaFileKey,nodeId:args.figmaNodeId,format:figmaFormat,scale:figmaScale}),pagePng;if(typeof args.selector=="string"&&args.selector.trim()){let selector=args.selector.trim(),locator=context.page.locator(selector);if(await locator.count()===0)throw new Error(`Element not found for selector: ${selector}`);pagePng=await locator.first().screenshot({type:DEFAULT_SCREENSHOT_TYPE2})}else{let fullPage=args.fullPage!==!1;pagePng=await context.page.screenshot({type:DEFAULT_SCREENSHOT_TYPE2,fullPage})}let pageSs={image:pagePng,type:"png",name:"page"},figmaSs={image:figmaSnapshot.image,type:figmaSnapshot.type==="jpeg"?"jpeg":"png",name:"figma"},result=await compareWithNotes(pageSs,figmaSs,{weights:args.weights?{mssim:args.weights.mssim,vectorEmbedding:args.weights.imageEmbedding,textEmbedding:args.weights.textEmbedding}:void 0,mssim:{mode:args.mssimMode??DEFAULT_MSSIM_MODE},imageEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0},textEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0}});return{score:result.score,notes:result.notes,meta:{pageUrl,pageTitle,figmaFileKey:args.figmaFileKey,figmaNodeId:args.figmaNodeId,selector:typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():null,fullPage:typeof args.selector=="string"&&args.selector.trim()?!1:args.fullPage!==!1,pageImageType:"png",figmaImageType:figmaSnapshot.type}}}};var tools4=[new ComparePageWithDesign];import{z as z42}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS=1e4,Click=class{name(){return"interaction_click"}description(){return"Clicks an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z42.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the element to click."),timeoutMs:z42.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS;return await resolveSelectorOrRef(context,args.selector).click({timeout}),{}}};import{z as z43}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS2=1e4,Drag=class{name(){return"interaction_drag"}description(){return"Drags an element to a target location. Accepts CSS selectors or refs (e.g. e1, @e1) from the last ARIA snapshot."}inputSchema(){return{sourceSelector:z43.string().describe("CSS selector or ref (e.g. e1, @e1) for the element to drag."),targetSelector:z43.string().describe("CSS selector or ref (e.g. e2, @e2) for the target location."),timeoutMs:z43.number().int().positive().optional().describe("Timeout in ms to wait for source and target elements (default 10000). Use a shorter value (e.g. 5000) to fail faster if selectors might be invalid.")}}outputSchema(){return{}}async handle(context,args){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS2,sourceLocator=resolveSelectorOrRef(context,args.sourceSelector),targetLocator=resolveSelectorOrRef(context,args.targetSelector),sourceBound=await sourceLocator.boundingBox({timeout}),targetBound=await targetLocator.boundingBox({timeout});if(!sourceBound||!targetBound)throw new Error("Could not get element positions for drag operation");return await context.page.mouse.move(sourceBound.x+sourceBound.width/2,sourceBound.y+sourceBound.height/2),await context.page.mouse.down(),await context.page.mouse.move(targetBound.x+targetBound.width/2,targetBound.y+targetBound.height/2),await context.page.mouse.up(),{}}};import{z as z44}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS3=1e4,Fill=class{name(){return"interaction_fill"}description(){return"Fills out an input field. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z44.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the input field."),value:z44.string().describe("Value to fill."),timeoutMs:z44.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS3;return await resolveSelectorOrRef(context,args.selector).fill(args.value,{timeout}),{}}};import{z as z45}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS4=1e4,Hover=class{name(){return"interaction_hover"}description(){return"Hovers an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z45.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the element to hover."),timeoutMs:z45.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS4;return await resolveSelectorOrRef(context,args.selector).hover({timeout}),{}}};import{z as z46}from"zod";var DEFAULT_REPEAT_INTERVAL_MS=50,MIN_REPEAT_INTERVAL_MS=10,DEFAULT_SELECTOR_TIMEOUT_MS5=1e4,PressKey=class{name(){return"interaction_press-key"}description(){return`
506
+ `.trim()}inputSchema(){return{figmaFileKey:z41.string().min(1).describe("Figma file key (the part after /file/ in Figma URL)."),figmaNodeId:z41.string().min(1).describe('Figma node id to render (frame/component node id like "12:34").'),selector:z41.string().optional().describe("Optional CSS selector to compare only a specific region of the page."),fullPage:z41.boolean().optional().default(DEFAULT_FULL_PAGE).describe("If true, captures the full scrollable page. Ignored when selector is provided."),figmaScale:z41.number().int().positive().optional().describe("Optional scale factor for Figma raster export (e.g. 1, 2, 3)."),figmaFormat:z41.enum(["png","jpg"]).optional().describe("Optional raster format for Figma snapshot."),weights:z41.object({mssim:z41.number().positive().optional().describe("Weight for MSSIM signal."),imageEmbedding:z41.number().positive().optional().describe("Weight for image embedding signal."),textEmbedding:z41.number().positive().optional().describe("Weight for vision\u2192text\u2192text embedding signal.")}).optional().describe("Optional weights to combine signals. Only active signals participate."),mssimMode:z41.enum(["raw","semantic"]).optional().default(DEFAULT_MSSIM_MODE).describe("MSSIM mode. semantic is more robust for real-data vs design-data comparisons."),maxDim:z41.number().int().positive().optional().describe("Optional preprocessing max dimension forwarded to compare pipeline."),jpegQuality:z41.number().int().min(50).max(100).optional().describe("Optional JPEG quality forwarded to compare pipeline (used only when JPEG encoding is selected internally).")}}outputSchema(){return{score:z41.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:z41.array(z41.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:z41.object({pageUrl:z41.string().describe("URL of the page that was compared."),pageTitle:z41.string().describe("Title of the page that was compared."),figmaFileKey:z41.string().describe("Figma file key used for the design snapshot."),figmaNodeId:z41.string().describe("Figma node id used for the design snapshot."),selector:z41.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:z41.boolean().describe("Whether the page screenshot was full-page."),pageImageType:z41.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:z41.enum(["png","jpeg"]).describe("Image type of the captured Figma snapshot.")}).describe("Metadata about what was compared.")}}async handle(context,args){let pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),figmaFormat=args.figmaFormat??"png",figmaScale=typeof args.figmaScale=="number"?args.figmaScale:void 0,figmaSnapshot=await getFigmaDesignScreenshot({fileKey:args.figmaFileKey,nodeId:args.figmaNodeId,format:figmaFormat,scale:figmaScale}),pagePng;if(typeof args.selector=="string"&&args.selector.trim()){let selector=args.selector.trim(),locator=context.page.locator(selector);if(await locator.count()===0)throw new Error(`Element not found for selector: ${selector}`);pagePng=await locator.first().screenshot({type:DEFAULT_SCREENSHOT_TYPE2})}else{let fullPage=args.fullPage!==!1;pagePng=await context.page.screenshot({type:DEFAULT_SCREENSHOT_TYPE2,fullPage})}let pageSs={image:pagePng,type:"png",name:"page"},figmaSs={image:figmaSnapshot.image,type:figmaSnapshot.type==="jpeg"?"jpeg":"png",name:"figma"},result=await compareWithNotes(pageSs,figmaSs,{weights:args.weights?{mssim:args.weights.mssim,vectorEmbedding:args.weights.imageEmbedding,textEmbedding:args.weights.textEmbedding}:void 0,mssim:{mode:args.mssimMode??DEFAULT_MSSIM_MODE},imageEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0},textEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0}});return{score:result.score,notes:result.notes,meta:{pageUrl,pageTitle,figmaFileKey:args.figmaFileKey,figmaNodeId:args.figmaNodeId,selector:typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():null,fullPage:typeof args.selector=="string"&&args.selector.trim()?!1:args.fullPage!==!1,pageImageType:"png",figmaImageType:figmaSnapshot.type}}}};var tools4=[new ComparePageWithDesign];import{z as z42}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS=1e4,Click=class{name(){return"interaction_click"}description(){return"Clicks an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z42.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the element to click."),timeoutMs:z42.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"click","Use a ref for an interactive element (button, link, etc.) from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS;return await resolveSelectorOrRef(context,args.selector).click({timeout}),{}}};import{z as z43}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS2=1e4,Drag=class{name(){return"interaction_drag"}description(){return"Drags an element to a target location. Accepts CSS selectors or refs (e.g. e1, @e1) from the last ARIA snapshot."}inputSchema(){return{sourceSelector:z43.string().describe("CSS selector or ref (e.g. e1, @e1) for the element to drag."),targetSelector:z43.string().describe("CSS selector or ref (e.g. e2, @e2) for the target location."),timeoutMs:z43.number().int().positive().optional().describe("Timeout in ms to wait for source and target elements (default 10000). Use a shorter value (e.g. 5000) to fail faster if selectors might be invalid.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.sourceSelector,REF_ROLES_INTERACTIVE,"drag (source)","Use a ref for a draggable element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS2,sourceLocator=resolveSelectorOrRef(context,args.sourceSelector),targetLocator=resolveSelectorOrRef(context,args.targetSelector),sourceBound=await sourceLocator.boundingBox({timeout}),targetBound=await targetLocator.boundingBox({timeout});if(!sourceBound||!targetBound)throw new Error("Could not get element positions for drag operation");return await context.page.mouse.move(sourceBound.x+sourceBound.width/2,sourceBound.y+sourceBound.height/2),await context.page.mouse.down(),await context.page.mouse.move(targetBound.x+targetBound.width/2,targetBound.y+targetBound.height/2),await context.page.mouse.up(),{}}};import{z as z44}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS3=1e4,FILLABLE_ROLES=new Set(["textbox","searchbox","spinbutton","combobox"]),Fill=class{name(){return"interaction_fill"}description(){return"Fills out an input field. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z44.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the input field."),value:z44.string().describe("Value to fill."),timeoutMs:z44.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,FILLABLE_ROLES,"fill","Use a ref for a textbox, searchbox, or input (e.g. from the latest a11y_take-aria-snapshot on this page). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS3;return await resolveSelectorOrRef(context,args.selector).fill(args.value,{timeout}),{}}};import{z as z45}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS4=1e4,Hover=class{name(){return"interaction_hover"}description(){return"Hovers an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z45.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the element to hover."),timeoutMs:z45.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"hover","Use a ref for an interactive element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS4;return await resolveSelectorOrRef(context,args.selector).hover({timeout}),{}}};import{z as z46}from"zod";var DEFAULT_REPEAT_INTERVAL_MS=50,MIN_REPEAT_INTERVAL_MS=10,DEFAULT_SELECTOR_TIMEOUT_MS5=1e4,PressKey=class{name(){return"interaction_press-key"}description(){return`
507
507
  Presses a keyboard key with optional "hold" and auto-repeat behavior.
508
508
 
509
509
  Key facts:
@@ -540,7 +540,7 @@ so the page layout follows the OS window size.
540
540
  Important:
541
541
  - If Playwright viewport emulation is enabled (viewport is NOT null), resizing the OS window may not change page layout.
542
542
  - On non-Chromium browsers (Firefox/WebKit), CDP is not available and this tool will fail.
543
- `.trim()}inputSchema(){return{width:z48.number().int().min(MIN_WINDOW_WIDTH).optional().describe('Target window width in pixels (required when state="normal").'),height:z48.number().int().min(MIN_WINDOW_HEIGHT).optional().describe('Target window height in pixels (required when state="normal").'),state:z48.enum(["normal","maximized","minimized","fullscreen"]).optional().default("normal").describe('Target window state. If not "normal", width/height may be ignored by the browser.')}}outputSchema(){return{requested:z48.object({width:z48.number().int().nullable().describe("Requested window width (pixels). Null if not provided."),height:z48.number().int().nullable().describe("Requested window height (pixels). Null if not provided."),state:z48.enum(["normal","maximized","minimized","fullscreen"]).describe("Requested window state.")}).describe("Requested window change parameters."),before:z48.object({windowId:z48.number().int().describe("CDP window id for the current target."),state:z48.string().nullable().describe("Window state before resizing."),left:z48.number().int().nullable().describe("Window left position before resizing."),top:z48.number().int().nullable().describe("Window top position before resizing."),width:z48.number().int().nullable().describe("Window width before resizing."),height:z48.number().int().nullable().describe("Window height before resizing.")}).describe("Window bounds before resizing."),after:z48.object({windowId:z48.number().int().describe("CDP window id for the current target."),state:z48.string().nullable().describe("Window state after resizing."),left:z48.number().int().nullable().describe("Window left position after resizing."),top:z48.number().int().nullable().describe("Window top position after resizing."),width:z48.number().int().nullable().describe("Window width after resizing."),height:z48.number().int().nullable().describe("Window height after resizing.")}).describe("Window bounds after resizing."),viewport:z48.object({innerWidth:z48.number().int().describe("window.innerWidth after resizing (CSS pixels)."),innerHeight:z48.number().int().describe("window.innerHeight after resizing (CSS pixels)."),outerWidth:z48.number().int().describe("window.outerWidth after resizing (CSS pixels)."),outerHeight:z48.number().int().describe("window.outerHeight after resizing (CSS pixels)."),devicePixelRatio:z48.number().describe("window.devicePixelRatio after resizing.")}).describe("Page viewport metrics after resizing (helps verify responsive behavior).")}}async handle(context,args){let state=args.state??"normal",width=args.width,height=args.height;if(state==="normal"&&(typeof width!="number"||typeof height!="number"))throw new Error('state="normal" requires both width and height.');let page=context.page,cdp=await page.context().newCDPSession(page);try{let info=await cdp.send("Browser.getWindowForTarget",{}),windowId=Number(info.windowId),beforeBounds=info.bounds??{},before={windowId,state:typeof beforeBounds.windowState=="string"?beforeBounds.windowState:null,left:typeof beforeBounds.left=="number"?beforeBounds.left:null,top:typeof beforeBounds.top=="number"?beforeBounds.top:null,width:typeof beforeBounds.width=="number"?beforeBounds.width:null,height:typeof beforeBounds.height=="number"?beforeBounds.height:null},boundsToSet={};state!=="normal"?boundsToSet.windowState=state:(boundsToSet.windowState="normal",boundsToSet.width=width,boundsToSet.height=height),await cdp.send("Browser.setWindowBounds",{windowId,bounds:boundsToSet});let afterBounds=(await cdp.send("Browser.getWindowForTarget",{})).bounds??{},after={windowId,state:typeof afterBounds.windowState=="string"?afterBounds.windowState:null,left:typeof afterBounds.left=="number"?afterBounds.left:null,top:typeof afterBounds.top=="number"?afterBounds.top:null,width:typeof afterBounds.width=="number"?afterBounds.width:null,height:typeof afterBounds.height=="number"?afterBounds.height:null},metrics=await page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio})),viewport={innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)};return{requested:{width:typeof width=="number"?width:null,height:typeof height=="number"?height:null,state},before,after,viewport}}catch(e){let msg=String(e?.message??e);throw new Error(`Failed to resize real browser window via CDP. This tool works best on Chromium-based browsers. Original error: ${msg}`)}finally{await cdp.detach().catch(()=>{})}}};import{z as z49}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS6=1e4,Select=class{name(){return"interaction_select"}description(){return"Select an option in a dropdown. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z49.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the select element."),value:z49.string().describe("Value to select."),timeoutMs:z49.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS6;return await resolveSelectorOrRef(context,args.selector).selectOption(args.value,{timeout}),{}}};import{z as z50}from"zod";var DEFAULT_BEHAVIOR="auto",DEFAULT_MODE="by",Scroll=class{name(){return"interaction_scroll"}description(){return`
543
+ `.trim()}inputSchema(){return{width:z48.number().int().min(MIN_WINDOW_WIDTH).optional().describe('Target window width in pixels (required when state="normal").'),height:z48.number().int().min(MIN_WINDOW_HEIGHT).optional().describe('Target window height in pixels (required when state="normal").'),state:z48.enum(["normal","maximized","minimized","fullscreen"]).optional().default("normal").describe('Target window state. If not "normal", width/height may be ignored by the browser.')}}outputSchema(){return{requested:z48.object({width:z48.number().int().nullable().describe("Requested window width (pixels). Null if not provided."),height:z48.number().int().nullable().describe("Requested window height (pixels). Null if not provided."),state:z48.enum(["normal","maximized","minimized","fullscreen"]).describe("Requested window state.")}).describe("Requested window change parameters."),before:z48.object({windowId:z48.number().int().describe("CDP window id for the current target."),state:z48.string().nullable().describe("Window state before resizing."),left:z48.number().int().nullable().describe("Window left position before resizing."),top:z48.number().int().nullable().describe("Window top position before resizing."),width:z48.number().int().nullable().describe("Window width before resizing."),height:z48.number().int().nullable().describe("Window height before resizing.")}).describe("Window bounds before resizing."),after:z48.object({windowId:z48.number().int().describe("CDP window id for the current target."),state:z48.string().nullable().describe("Window state after resizing."),left:z48.number().int().nullable().describe("Window left position after resizing."),top:z48.number().int().nullable().describe("Window top position after resizing."),width:z48.number().int().nullable().describe("Window width after resizing."),height:z48.number().int().nullable().describe("Window height after resizing.")}).describe("Window bounds after resizing."),viewport:z48.object({innerWidth:z48.number().int().describe("window.innerWidth after resizing (CSS pixels)."),innerHeight:z48.number().int().describe("window.innerHeight after resizing (CSS pixels)."),outerWidth:z48.number().int().describe("window.outerWidth after resizing (CSS pixels)."),outerHeight:z48.number().int().describe("window.outerHeight after resizing (CSS pixels)."),devicePixelRatio:z48.number().describe("window.devicePixelRatio after resizing.")}).describe("Page viewport metrics after resizing (helps verify responsive behavior).")}}async handle(context,args){let state=args.state??"normal",width=args.width,height=args.height;if(state==="normal"&&(typeof width!="number"||typeof height!="number"))throw new Error('state="normal" requires both width and height.');let page=context.page,cdp=await page.context().newCDPSession(page);try{let info=await cdp.send("Browser.getWindowForTarget",{}),windowId=Number(info.windowId),beforeBounds=info.bounds??{},before={windowId,state:typeof beforeBounds.windowState=="string"?beforeBounds.windowState:null,left:typeof beforeBounds.left=="number"?beforeBounds.left:null,top:typeof beforeBounds.top=="number"?beforeBounds.top:null,width:typeof beforeBounds.width=="number"?beforeBounds.width:null,height:typeof beforeBounds.height=="number"?beforeBounds.height:null},boundsToSet={};state!=="normal"?boundsToSet.windowState=state:(boundsToSet.windowState="normal",boundsToSet.width=width,boundsToSet.height=height),await cdp.send("Browser.setWindowBounds",{windowId,bounds:boundsToSet});let afterBounds=(await cdp.send("Browser.getWindowForTarget",{})).bounds??{},after={windowId,state:typeof afterBounds.windowState=="string"?afterBounds.windowState:null,left:typeof afterBounds.left=="number"?afterBounds.left:null,top:typeof afterBounds.top=="number"?afterBounds.top:null,width:typeof afterBounds.width=="number"?afterBounds.width:null,height:typeof afterBounds.height=="number"?afterBounds.height:null},metrics=await page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio})),viewport={innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)};return{requested:{width:typeof width=="number"?width:null,height:typeof height=="number"?height:null,state},before,after,viewport}}catch(e){let msg=String(e?.message??e);throw new Error(`Failed to resize real browser window via CDP. This tool works best on Chromium-based browsers. Original error: ${msg}`)}finally{await cdp.detach().catch(()=>{})}}};import{z as z49}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS6=1e4,SELECTABLE_ROLES=new Set(["listbox","combobox"]),Select=class{name(){return"interaction_select"}description(){return"Select an option in a dropdown. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z49.string().describe("CSS selector or ref (e.g. e1, @e1) from a11y_take-aria-snapshot for the select element."),value:z49.string().describe("Value to select."),timeoutMs:z49.number().int().positive().optional().describe("Timeout in ms to wait for the element (default 10000). Use a shorter value (e.g. 5000) to fail faster if the selector might be invalid.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,SELECTABLE_ROLES,"select","Use a ref for a listbox or combobox (e.g. from the latest a11y_take-aria-snapshot). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS6;return await resolveSelectorOrRef(context,args.selector).selectOption(args.value,{timeout}),{}}};import{z as z50}from"zod";var DEFAULT_BEHAVIOR="auto",DEFAULT_MODE="by",Scroll=class{name(){return"interaction_scroll"}description(){return`
544
544
  Scrolls the page viewport or a specific scrollable element.
545
545
 
546
546
  Modes:
@@ -556,19 +556,25 @@ Use this tool to:
556
556
  - Jump to the top/bottom without knowing exact positions
557
557
  - Bring elements into view before clicking
558
558
  - Inspect lazy-loaded content that appears on scroll
559
- `.trim()}inputSchema(){return{mode:z50.enum(["by","to","top","bottom","left","right"]).optional().default(DEFAULT_MODE).describe('Scroll mode. "by" uses dx/dy, "to" uses x/y, and top/bottom/left/right jump to edges.'),selector:z50.string().optional().describe("Optional CSS selector or ref (e.g. e1, @e1) for a scrollable container. If omitted, scrolls the document viewport."),dx:z50.number().optional().describe('Horizontal scroll delta in pixels (used when mode="by"). Default: 0.'),dy:z50.number().optional().describe('Vertical scroll delta in pixels (used when mode="by"). Default: 0.'),x:z50.number().optional().describe('Absolute horizontal scroll position in pixels (used when mode="to").'),y:z50.number().optional().describe('Absolute vertical scroll position in pixels (used when mode="to").'),behavior:z50.enum(["auto","smooth"]).optional().default(DEFAULT_BEHAVIOR).describe('Native scroll behavior. Use "auto" for deterministic automation.')}}outputSchema(){return{mode:z50.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:z50.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:z50.enum(["auto","smooth"]).describe("The scroll behavior used."),before:z50.object({x:z50.number().describe("ScrollLeft before scrolling."),y:z50.number().describe("ScrollTop before scrolling."),scrollWidth:z50.number().describe("Total scrollable width before scrolling."),scrollHeight:z50.number().describe("Total scrollable height before scrolling."),clientWidth:z50.number().describe("Viewport/container client width before scrolling."),clientHeight:z50.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:z50.object({x:z50.number().describe("ScrollLeft after scrolling."),y:z50.number().describe("ScrollTop after scrolling."),scrollWidth:z50.number().describe("Total scrollable width after scrolling."),scrollHeight:z50.number().describe("Total scrollable height after scrolling."),clientWidth:z50.number().describe("Viewport/container client width after scrolling."),clientHeight:z50.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:z50.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:z50.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:z50.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:z50.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:z50.boolean().describe("Whether the scroll position is at the far left."),isAtRight:z50.boolean().describe("Whether the scroll position is at the far right."),isAtTop:z50.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:z50.boolean().describe("Whether the scroll position is at the very bottom.")}}async handle(context,args){let mode=args.mode??DEFAULT_MODE,selector=args.selector,behavior=args.behavior??DEFAULT_BEHAVIOR,dx=args.dx??0,dy=args.dy??0,x=args.x,y=args.y;if(mode==="to"&&typeof x!="number"&&typeof y!="number")throw new Error('mode="to" requires at least one of x or y.');if(mode==="by"&&dx===0&&dy===0)throw new Error('mode="by" requires dx and/or dy to be non-zero.');let params={modeEval:mode,selectorEval:selector,dxEval:dx,dyEval:dy,xEval:x,yEval:y,behaviorEval:behavior},result;return selector?result=await resolveSelectorOrRef(context,selector).evaluate((el,p)=>{let before={x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight},readMetrics=elem2=>({x:elem2.scrollLeft,y:elem2.scrollTop,scrollWidth:elem2.scrollWidth,scrollHeight:elem2.scrollHeight,clientWidth:elem2.clientWidth,clientHeight:elem2.clientHeight}),clamp=(v,min,max)=>Math.max(min,Math.min(max,v)),elem=el,maxX=Math.max(0,elem.scrollWidth-elem.clientWidth),maxY=Math.max(0,elem.scrollHeight-elem.clientHeight);p.modeEval==="by"?elem.scrollTo({left:clamp(elem.scrollLeft+p.dxEval,0,maxX),top:clamp(elem.scrollTop+p.dyEval,0,maxY),behavior:p.behaviorEval}):p.modeEval==="to"?elem.scrollTo({left:typeof p.xEval=="number"?clamp(p.xEval,0,maxX):elem.scrollLeft,top:typeof p.yEval=="number"?clamp(p.yEval,0,maxY):elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="top"?elem.scrollTo({top:0,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="bottom"?elem.scrollTo({top:maxY,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="left"?elem.scrollTo({left:0,top:elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="right"&&elem.scrollTo({left:maxX,top:elem.scrollTop,behavior:p.behaviorEval});let after=readMetrics(elem);return{before,after,canScrollX:after.scrollWidth>after.clientWidth,canScrollY:after.scrollHeight>after.clientHeight,maxScrollX:Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY:Math.max(0,after.scrollHeight-after.clientHeight),isAtLeft:after.x<=1,isAtRight:after.x>=Math.max(0,after.scrollWidth-after.clientWidth)-1,isAtTop:after.y<=1,isAtBottom:after.y>=Math.max(0,after.scrollHeight-after.clientHeight)-1}},params):result=await context.page.evaluate(params2=>{let modeEval=params2.modeEval,selectorEval=params2.selectorEval,dxEval=params2.dxEval,dyEval=params2.dyEval,xEval=params2.xEval,yEval=params2.yEval,behaviorEval=params2.behaviorEval,getTarget=()=>{if(selectorEval){let el=document.querySelector(selectorEval);if(!el)throw new Error(`Element with selector "${selectorEval}" not found`);return el}let scrolling=document.scrollingElement||document.documentElement||document.body;if(!scrolling)throw new Error("No scrolling element available.");return scrolling},readMetrics=el=>({x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight}),clamp=(v,min,max)=>v<min?min:v>max?max:v,doScroll=el=>{let maxX=Math.max(0,el.scrollWidth-el.clientWidth),maxY=Math.max(0,el.scrollHeight-el.clientHeight);if(modeEval==="by"){let nextX=clamp(el.scrollLeft+dxEval,0,maxX),nextY=clamp(el.scrollTop+dyEval,0,maxY);el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="to"){let nextX=typeof xEval=="number"?clamp(xEval,0,maxX):el.scrollLeft,nextY=typeof yEval=="number"?clamp(yEval,0,maxY):el.scrollTop;el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="top"){el.scrollTo({top:0,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="bottom"){el.scrollTo({top:maxY,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="left"){el.scrollTo({left:0,top:el.scrollTop,behavior:behaviorEval});return}if(modeEval==="right"){el.scrollTo({left:maxX,top:el.scrollTop,behavior:behaviorEval});return}},target=getTarget(),before=readMetrics(target);doScroll(target);let after=readMetrics(target),maxScrollX=Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY=Math.max(0,after.scrollHeight-after.clientHeight),canScrollX=after.scrollWidth>after.clientWidth,canScrollY=after.scrollHeight>after.clientHeight,eps=1,isAtLeft=after.x<=eps,isAtRight=after.x>=maxScrollX-eps,isAtTop=after.y<=eps,isAtBottom=after.y>=maxScrollY-eps;return{before,after,canScrollX,canScrollY,maxScrollX,maxScrollY,isAtLeft,isAtRight,isAtTop,isAtBottom}},params),{mode,selector:selector??null,behavior,before:result.before,after:result.after,canScrollX:result.canScrollX,canScrollY:result.canScrollY,maxScrollX:result.maxScrollX,maxScrollY:result.maxScrollY,isAtLeft:result.isAtLeft,isAtRight:result.isAtRight,isAtTop:result.isAtTop,isAtBottom:result.isAtBottom}}};var tools5=[new Click,new Drag,new Fill,new Hover,new PressKey,new ResizeViewport,new ResizeWindow,new Select,new Scroll];import{z as z51}from"zod";var DEFAULT_TIMEOUT_MS=0,DEFAULT_WAIT_UNTIL="load",GoBack=class{name(){return"navigation_go-back"}description(){return`
559
+ `.trim()}inputSchema(){return{mode:z50.enum(["by","to","top","bottom","left","right"]).optional().default(DEFAULT_MODE).describe('Scroll mode. "by" uses dx/dy, "to" uses x/y, and top/bottom/left/right jump to edges.'),selector:z50.string().optional().describe("Optional CSS selector or ref (e.g. e1, @e1) for a scrollable container. If omitted, scrolls the document viewport."),dx:z50.number().optional().describe('Horizontal scroll delta in pixels (used when mode="by"). Default: 0.'),dy:z50.number().optional().describe('Vertical scroll delta in pixels (used when mode="by"). Default: 0.'),x:z50.number().optional().describe('Absolute horizontal scroll position in pixels (used when mode="to").'),y:z50.number().optional().describe('Absolute vertical scroll position in pixels (used when mode="to").'),behavior:z50.enum(["auto","smooth"]).optional().default(DEFAULT_BEHAVIOR).describe('Native scroll behavior. Use "auto" for deterministic automation.')}}outputSchema(){return{mode:z50.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:z50.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:z50.enum(["auto","smooth"]).describe("The scroll behavior used."),before:z50.object({x:z50.number().describe("ScrollLeft before scrolling."),y:z50.number().describe("ScrollTop before scrolling."),scrollWidth:z50.number().describe("Total scrollable width before scrolling."),scrollHeight:z50.number().describe("Total scrollable height before scrolling."),clientWidth:z50.number().describe("Viewport/container client width before scrolling."),clientHeight:z50.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:z50.object({x:z50.number().describe("ScrollLeft after scrolling."),y:z50.number().describe("ScrollTop after scrolling."),scrollWidth:z50.number().describe("Total scrollable width after scrolling."),scrollHeight:z50.number().describe("Total scrollable height after scrolling."),clientWidth:z50.number().describe("Viewport/container client width after scrolling."),clientHeight:z50.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:z50.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:z50.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:z50.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:z50.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:z50.boolean().describe("Whether the scroll position is at the far left."),isAtRight:z50.boolean().describe("Whether the scroll position is at the far right."),isAtTop:z50.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:z50.boolean().describe("Whether the scroll position is at the very bottom.")}}async handle(context,args){let mode=args.mode??DEFAULT_MODE,selector=args.selector,behavior=args.behavior??DEFAULT_BEHAVIOR,dx=args.dx??0,dy=args.dy??0,x=args.x,y=args.y;if(mode==="to"&&typeof x!="number"&&typeof y!="number")throw new Error('mode="to" requires at least one of x or y.');if(mode==="by"&&dx===0&&dy===0)throw new Error('mode="by" requires dx and/or dy to be non-zero.');let params={modeEval:mode,selectorEval:selector,dxEval:dx,dyEval:dy,xEval:x,yEval:y,behaviorEval:behavior},result;return selector?result=await resolveSelectorOrRef(context,selector).evaluate((el,p)=>{let before={x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight},readMetrics=elem2=>({x:elem2.scrollLeft,y:elem2.scrollTop,scrollWidth:elem2.scrollWidth,scrollHeight:elem2.scrollHeight,clientWidth:elem2.clientWidth,clientHeight:elem2.clientHeight}),clamp=(v,min,max)=>Math.max(min,Math.min(max,v)),elem=el,maxX=Math.max(0,elem.scrollWidth-elem.clientWidth),maxY=Math.max(0,elem.scrollHeight-elem.clientHeight);p.modeEval==="by"?elem.scrollTo({left:clamp(elem.scrollLeft+p.dxEval,0,maxX),top:clamp(elem.scrollTop+p.dyEval,0,maxY),behavior:p.behaviorEval}):p.modeEval==="to"?elem.scrollTo({left:typeof p.xEval=="number"?clamp(p.xEval,0,maxX):elem.scrollLeft,top:typeof p.yEval=="number"?clamp(p.yEval,0,maxY):elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="top"?elem.scrollTo({top:0,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="bottom"?elem.scrollTo({top:maxY,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="left"?elem.scrollTo({left:0,top:elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="right"&&elem.scrollTo({left:maxX,top:elem.scrollTop,behavior:p.behaviorEval});let after=readMetrics(elem);return{before,after,canScrollX:after.scrollWidth>after.clientWidth,canScrollY:after.scrollHeight>after.clientHeight,maxScrollX:Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY:Math.max(0,after.scrollHeight-after.clientHeight),isAtLeft:after.x<=1,isAtRight:after.x>=Math.max(0,after.scrollWidth-after.clientWidth)-1,isAtTop:after.y<=1,isAtBottom:after.y>=Math.max(0,after.scrollHeight-after.clientHeight)-1}},params):result=await context.page.evaluate(params2=>{let modeEval=params2.modeEval,selectorEval=params2.selectorEval,dxEval=params2.dxEval,dyEval=params2.dyEval,xEval=params2.xEval,yEval=params2.yEval,behaviorEval=params2.behaviorEval,getTarget=()=>{if(selectorEval){let el=document.querySelector(selectorEval);if(!el)throw new Error(`Element with selector "${selectorEval}" not found`);return el}let scrolling=document.scrollingElement||document.documentElement||document.body;if(!scrolling)throw new Error("No scrolling element available.");return scrolling},readMetrics=el=>({x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight}),clamp=(v,min,max)=>v<min?min:v>max?max:v,doScroll=el=>{let maxX=Math.max(0,el.scrollWidth-el.clientWidth),maxY=Math.max(0,el.scrollHeight-el.clientHeight);if(modeEval==="by"){let nextX=clamp(el.scrollLeft+dxEval,0,maxX),nextY=clamp(el.scrollTop+dyEval,0,maxY);el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="to"){let nextX=typeof xEval=="number"?clamp(xEval,0,maxX):el.scrollLeft,nextY=typeof yEval=="number"?clamp(yEval,0,maxY):el.scrollTop;el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="top"){el.scrollTo({top:0,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="bottom"){el.scrollTo({top:maxY,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="left"){el.scrollTo({left:0,top:el.scrollTop,behavior:behaviorEval});return}if(modeEval==="right"){el.scrollTo({left:maxX,top:el.scrollTop,behavior:behaviorEval});return}},target=getTarget(),before=readMetrics(target);doScroll(target);let after=readMetrics(target),maxScrollX=Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY=Math.max(0,after.scrollHeight-after.clientHeight),canScrollX=after.scrollWidth>after.clientWidth,canScrollY=after.scrollHeight>after.clientHeight,eps=1,isAtLeft=after.x<=eps,isAtRight=after.x>=maxScrollX-eps,isAtTop=after.y<=eps,isAtBottom=after.y>=maxScrollY-eps;return{before,after,canScrollX,canScrollY,maxScrollX,maxScrollY,isAtLeft,isAtRight,isAtTop,isAtBottom}},params),{mode,selector:selector??null,behavior,before:result.before,after:result.after,canScrollX:result.canScrollX,canScrollY:result.canScrollY,maxScrollX:result.maxScrollX,maxScrollY:result.maxScrollY,isAtLeft:result.isAtLeft,isAtRight:result.isAtRight,isAtTop:result.isAtTop,isAtBottom:result.isAtBottom}}};var tools5=[new Click,new Drag,new Fill,new Hover,new PressKey,new ResizeViewport,new ResizeWindow,new Select,new Scroll];import{z as z51}from"zod";var DEFAULT_TIMEOUT_MS=0,DEFAULT_WAIT_UNTIL="load",refMapEntrySchema2=z51.object({role:z51.string(),name:z51.string().optional(),selector:z51.string(),nth:z51.number().optional()}),GoBack=class{name(){return"navigation_go-back"}description(){return`
560
560
  Navigates to the previous page in history.
561
561
  In case of multiple redirects, the navigation will resolve with the response of the last redirect.
562
562
  If cannot go back, returns empty response.
563
- `}inputSchema(){return{timeout:z51.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS),waitUntil:z51.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL)}}outputSchema(){return{url:z51.string().describe("Contains the URL of the navigated page.").optional(),status:z51.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z51.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z51.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(context,args){let response=await context.page.goBack({timeout:args.timeout,waitUntil:args.waitUntil});return{url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()}}};import{z as z52}from"zod";var DEFAULT_TIMEOUT_MS2=0,DEFAULT_WAIT_UNTIL2="load",GoForward=class{name(){return"navigation_go-forward"}description(){return`
563
+
564
+ By default (includeSnapshot: true), an ARIA snapshot with refs is returned; use snapshotInteractiveOnly and snapshotCursorInteractive to control which elements get refs (same as a11y_take-aria-snapshot).
565
+ `}inputSchema(){return{timeout:z51.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS),waitUntil:z51.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL),includeSnapshot:z51.boolean().optional().default(!0).describe("When true (default), take an ARIA snapshot with refs after navigation and include output and refs in the response."),snapshotInteractiveOnly:z51.boolean().optional(),snapshotCursorInteractive:z51.boolean().optional()}}outputSchema(){return{url:z51.string().describe("Contains the URL of the navigated page.").optional(),status:z51.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z51.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z51.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),output:z51.string().optional(),refs:z51.record(z51.string(),refMapEntrySchema2).optional()}}async handle(context,args){let response=await context.page.goBack({timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()};if(args.includeSnapshot!==!1)try{let snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:args.snapshotInteractiveOnly,cursorInteractive:args.snapshotCursorInteractive});return{...base,output:snapshot.output,refs:snapshot.refs}}catch{return base}return base}};import{z as z52}from"zod";var DEFAULT_TIMEOUT_MS2=0,DEFAULT_WAIT_UNTIL2="load",refMapEntrySchema3=z52.object({role:z52.string(),name:z52.string().optional(),selector:z52.string(),nth:z52.number().optional()}),GoForward=class{name(){return"navigation_go-forward"}description(){return`
564
566
  Navigates to the next page in history.
565
567
  In case of multiple redirects, the navigation will resolve with the response of the last redirect.
566
- If cannot go back, returns empty response.
567
- `}inputSchema(){return{timeout:z52.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS2),waitUntil:z52.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL2)}}outputSchema(){return{url:z52.string().describe("Contains the URL of the navigated page.").optional(),status:z52.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z52.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z52.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(context,args){let response=await context.page.goForward({timeout:args.timeout,waitUntil:args.waitUntil});return{url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()}}};import{z as z53}from"zod";var DEFAULT_TIMEOUT_MS3=0,DEFAULT_WAIT_UNTIL3="load",GoTo=class{name(){return"navigation_go-to"}description(){return"\nNavigates to the given URL.\n**NOTE**: The tool either throws an error or returns a main resource response. \nThe only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, \nwhich would succeed and return empty response.\n\n**After navigating:** To understand the page, call `a11y_take-aria-snapshot` (or `a11y_take-ax-tree-snapshot`) first. Do NOT call `content_take-screenshot` as the first inspection.\n "}inputSchema(){return{url:z53.string().describe("URL to navigate page to. The url should include scheme, e.g. `http://`, `https://`."),timeout:z53.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS3),waitUntil:z53.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL3)}}outputSchema(){return{url:z53.string().describe("Contains the URL of the navigated page.").optional(),status:z53.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z53.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z53.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(context,args){let response=await context.page.goto(args.url,{timeout:args.timeout,waitUntil:args.waitUntil});return{url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()}}};import{z as z54}from"zod";var DEFAULT_TIMEOUT_MS4=0,DEFAULT_WAIT_UNTIL4="load",Reload=class{name(){return"navigation_reload"}description(){return`
568
+ If cannot go forward, returns empty response.
569
+
570
+ By default (includeSnapshot: true), an ARIA snapshot with refs is returned; use snapshotInteractiveOnly and snapshotCursorInteractive to control which elements get refs (same as a11y_take-aria-snapshot).
571
+ `}inputSchema(){return{timeout:z52.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS2),waitUntil:z52.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL2),includeSnapshot:z52.boolean().optional().default(!0).describe("When true (default), take an ARIA snapshot with refs after navigation and include output and refs in the response."),snapshotInteractiveOnly:z52.boolean().optional(),snapshotCursorInteractive:z52.boolean().optional()}}outputSchema(){return{url:z52.string().describe("Contains the URL of the navigated page.").optional(),status:z52.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z52.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z52.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),output:z52.string().optional(),refs:z52.record(z52.string(),refMapEntrySchema3).optional()}}async handle(context,args){let response=await context.page.goForward({timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()};if(args.includeSnapshot!==!1)try{let snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:args.snapshotInteractiveOnly,cursorInteractive:args.snapshotCursorInteractive});return{...base,output:snapshot.output,refs:snapshot.refs}}catch{return base}return base}};import{z as z53}from"zod";var DEFAULT_TIMEOUT_MS3=0,DEFAULT_WAIT_UNTIL3="load",refMapEntrySchema4=z53.object({role:z53.string(),name:z53.string().optional(),selector:z53.string(),nth:z53.number().optional()}),GoTo=class{name(){return"navigation_go-to"}description(){return"\nNavigates to the given URL.\n**NOTE**: The tool either throws an error or returns a main resource response. \nThe only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, \nwhich would succeed and return empty response.\n\n**By default** (`includeSnapshot: true`), an ARIA snapshot with refs is taken after navigation and returned in `output` and `refs`; you can use refs (e1, e2, ...) in interaction tools without calling `a11y_take-aria-snapshot` separately. Set `includeSnapshot: false` to get only url/status/ok.\n "}inputSchema(){return{url:z53.string().describe("URL to navigate page to. The url should include scheme, e.g. `http://`, `https://`."),timeout:z53.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS3),waitUntil:z53.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL3),includeSnapshot:z53.boolean().optional().default(!0).describe("When true (default), take an ARIA snapshot with refs after navigation and include output and refs in the response."),snapshotInteractiveOnly:z53.boolean().optional().describe("When includeSnapshot is true: if true, only interactive elements get refs; if false or omitted, content roles (headings, etc.) also get refs."),snapshotCursorInteractive:z53.boolean().optional().describe("When includeSnapshot is true: if true, also include cursor-interactive elements (e.g. div/span with cursor:pointer or onclick) in refs.")}}outputSchema(){return{url:z53.string().describe("Contains the URL of the navigated page.").optional(),status:z53.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z53.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z53.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),output:z53.string().optional().describe("When includeSnapshot is true: page URL, title, and ARIA tree with refs."),refs:z53.record(z53.string(),refMapEntrySchema4).optional().describe("When includeSnapshot is true: map of ref id (e1, e2, ...) to role/name/selector for use in interaction tools.")}}async handle(context,args){let response=await context.page.goto(args.url,{timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()};if(args.includeSnapshot!==!1)try{let snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:args.snapshotInteractiveOnly,cursorInteractive:args.snapshotCursorInteractive});return{...base,output:snapshot.output,refs:snapshot.refs}}catch{return base}return base}};import{z as z54}from"zod";var DEFAULT_TIMEOUT_MS4=0,DEFAULT_WAIT_UNTIL4="load",refMapEntrySchema5=z54.object({role:z54.string(),name:z54.string().optional(),selector:z54.string(),nth:z54.number().optional()}),Reload=class{name(){return"navigation_reload"}description(){return`
568
572
  Reloads the current page.
569
573
  In case of multiple redirects, the navigation resolves with the response of the last redirect.
570
574
  If the reload does not produce a response, returns empty response.
571
- `}inputSchema(){return{timeout:z54.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS4),waitUntil:z54.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL4)}}outputSchema(){return{url:z54.string().describe("Contains the URL of the reloaded page.").optional(),status:z54.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:z54.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:z54.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional()}}async handle(context,args){let response=await context.page.reload({timeout:args.timeout,waitUntil:args.waitUntil});return{url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()}}};var tools6=[new GoBack,new GoForward,new GoTo,new Reload];var HttpMethod=(HttpMethod3=>(HttpMethod3.GET="GET",HttpMethod3.POST="POST",HttpMethod3.PUT="PUT",HttpMethod3.PATCH="PATCH",HttpMethod3.DELETE="DELETE",HttpMethod3.HEAD="HEAD",HttpMethod3.OPTIONS="OPTIONS",HttpMethod3))(HttpMethod||{}),HttpResourceType=(HttpResourceType2=>(HttpResourceType2.DOCUMENT="document",HttpResourceType2.STYLESHEET="stylesheet",HttpResourceType2.IMAGE="image",HttpResourceType2.MEDIA="media",HttpResourceType2.FONT="font",HttpResourceType2.SCRIPT="script",HttpResourceType2.TEXTTRACK="texttrack",HttpResourceType2.XHR="xhr",HttpResourceType2.FETCH="fetch",HttpResourceType2.EVENTSOURCE="eventsource",HttpResourceType2.WEBSOCKET="websocket",HttpResourceType2.MANIFEST="manifest",HttpResourceType2.OTHER="other",HttpResourceType2))(HttpResourceType||{});import{z as z55}from"zod";var GetConsoleMessages=class{name(){return"o11y_get-console-messages"}description(){return"Retrieves console messages/logs from the browser with filtering options."}inputSchema(){return{type:z55.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).describe(`
575
+
576
+ By default (includeSnapshot: true), an ARIA snapshot with refs is returned; use snapshotInteractiveOnly and snapshotCursorInteractive to control which elements get refs (same as a11y_take-aria-snapshot).
577
+ `}inputSchema(){return{timeout:z54.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(DEFAULT_TIMEOUT_MS4),waitUntil:z54.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(DEFAULT_WAIT_UNTIL4),includeSnapshot:z54.boolean().optional().default(!0).describe("When true (default), take an ARIA snapshot with refs after reload and include output and refs in the response."),snapshotInteractiveOnly:z54.boolean().optional(),snapshotCursorInteractive:z54.boolean().optional()}}outputSchema(){return{url:z54.string().describe("Contains the URL of the reloaded page.").optional(),status:z54.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:z54.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:z54.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional(),output:z54.string().optional(),refs:z54.record(z54.string(),refMapEntrySchema5).optional()}}async handle(context,args){let response=await context.page.reload({timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()};if(args.includeSnapshot!==!1)try{let snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:args.snapshotInteractiveOnly,cursorInteractive:args.snapshotCursorInteractive});return{...base,output:snapshot.output,refs:snapshot.refs}}catch{return base}return base}};var tools6=[new GoBack,new GoForward,new GoTo,new Reload];var HttpMethod=(HttpMethod3=>(HttpMethod3.GET="GET",HttpMethod3.POST="POST",HttpMethod3.PUT="PUT",HttpMethod3.PATCH="PATCH",HttpMethod3.DELETE="DELETE",HttpMethod3.HEAD="HEAD",HttpMethod3.OPTIONS="OPTIONS",HttpMethod3))(HttpMethod||{}),HttpResourceType=(HttpResourceType2=>(HttpResourceType2.DOCUMENT="document",HttpResourceType2.STYLESHEET="stylesheet",HttpResourceType2.IMAGE="image",HttpResourceType2.MEDIA="media",HttpResourceType2.FONT="font",HttpResourceType2.SCRIPT="script",HttpResourceType2.TEXTTRACK="texttrack",HttpResourceType2.XHR="xhr",HttpResourceType2.FETCH="fetch",HttpResourceType2.EVENTSOURCE="eventsource",HttpResourceType2.WEBSOCKET="websocket",HttpResourceType2.MANIFEST="manifest",HttpResourceType2.OTHER="other",HttpResourceType2))(HttpResourceType||{});import{z as z55}from"zod";var GetConsoleMessages=class{name(){return"o11y_get-console-messages"}description(){return"Retrieves console messages/logs from the browser with filtering options."}inputSchema(){return{type:z55.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).describe(`
572
578
  Type of console messages to retrieve.
573
579
  When specified, console messages with equal or higher levels are retrieved.
574
580
  Valid values are (in ascending order according to their levels): ${getEnumKeyTuples(ConsoleMessageLevelName)}.`).optional(),search:z55.string().describe("Text to search for in console messages.").optional(),timestamp:z55.number().int().nonnegative().describe(`
@@ -887,12 +893,13 @@ Core capabilities include:
887
893
  UI debugging guidance for AI agents:
888
894
 
889
895
  *** INSPECTION PREFERENCE (follow this order) ***
890
- 1. Use "a11y_take-aria-snapshot" first for page structure, roles, and semantics. Use it when you need to understand what is on the page, hierarchy, or accessibility.
896
+ 1. For page structure, roles, and semantics: use the ARIA snapshot you already have. After navigation (go-to, go-back, go-forward, reload) the response includes output and refs by default \u2014 use that. If you did not navigate or the page changed, call "a11y_take-aria-snapshot". Do NOT take a screenshot just to understand what is on the page.
891
897
  2. If you need layout, bounding boxes, visibility, or occlusion: use "a11y_take-ax-tree-snapshot" (optionally with checkOcclusion:true). This often suffices instead of a screenshot.
892
- 3. Use "content_take-screenshot" ONLY when you need to verify visual appearance (e.g. design check, visual bug) or when ARIA/AX are insufficient. Do not use screenshot to understand page structure.
898
+ 3. Use "content_take-screenshot" ONLY when you have a concrete visual problem to verify (e.g. design check, visual bug, contrast, layout issue). For understanding page structure, snapshots are sufficient \u2014 screenshot is not needed. If in doubt, use snapshots first; add a screenshot only when snapshots are insufficient for the visual question.
893
899
  *** SCREENSHOT TOOL: includeBase64 ***
894
900
  - The screenshot is always saved to disk; the tool returns the file path. Do NOT set includeBase64 to true unless the assistant cannot read files from that path (e.g. remote MCP server, container). Leave includeBase64 false (default) to avoid large responses; read the image from the returned path when possible.
895
901
 
902
+ - For page structure, always use snapshots (ARIA or AX), not screenshots. Navigation responses already include an ARIA snapshot with refs; use it. Take a screenshot only when you must verify something visual (design, contrast, layout bug).
896
903
  - Prefer Accessibility (AX) and ARIA snapshots over raw DOM dumps when diagnosing UI problems.
897
904
  These snapshots provide higher-signal, semantically meaningful anchors (roles, names, states) that
898
905
  map more reliably to what users perceive and what assistive tech can interact with.
@@ -1059,7 +1066,7 @@ When MCP runs in a container connecting to another container:
1059
1066
 
1060
1067
  Once connected, you can use tracepoints, logpoints, exceptionpoints,
1061
1068
  and watch expressions - the same debugging tools available for browser.
1062
- `}inputSchema(){return{pid:z70.number().int().positive().describe("Process ID to connect to").optional(),processName:z70.string().describe('Process name pattern to search for (e.g., "server.js", "api")').optional(),containerId:z70.string().describe("Docker container ID - process runs inside this container").optional(),containerName:z70.string().describe("Docker container name or pattern (e.g., my-node-app) - resolved via docker ps -f name=").optional(),host:z70.string().describe("Inspector host (default: 127.0.0.1); for containerized MCP use host.docker.internal or container name").optional(),port:z70.number().int().positive().describe("Inspector port (default: 9229); for Docker use host-mapped port").optional(),wsUrl:z70.string().describe("Direct WebSocket URL (e.g., ws://127.0.0.1:9229/abc-123)").optional()}}outputSchema(){return{connected:z70.boolean().describe("Whether connection was successful"),pid:z70.number().describe("Process ID"),command:z70.string().optional().describe("Process command line"),containerId:z70.string().optional().describe("Docker container ID when connected via container"),inspectorHost:z70.string().optional().describe("Inspector host"),inspectorPort:z70.number().optional().describe("Inspector port"),webSocketUrl:z70.string().optional().describe("WebSocket URL"),title:z70.string().describe("Target title")}}async handle(context,args){let result=await context.connect({pid:args.pid,processName:args.processName,containerId:args.containerId,containerName:args.containerName,host:args.host,port:args.port,wsUrl:args.wsUrl,activateIfNeeded:!0});return{connected:!0,pid:result.processInfo.pid,command:result.processInfo.command,containerId:result.processInfo.containerId,inspectorHost:result.processInfo.inspectorHost,inspectorPort:result.processInfo.inspectorPort,webSocketUrl:result.processInfo.webSocketUrl,title:result.target.title}}};import{z as z71}from"zod";var Disconnect=class{name(){return"debug_disconnect"}description(){return"Disconnects from the currently connected Node.js process and cleans up debugging resources."}inputSchema(){return{}}outputSchema(){return{disconnected:z71.boolean().describe("Whether disconnection was successful")}}async handle(context,_args){return await context.disconnect(),{disconnected:!0}}};import{z as z72}from"zod";function _filterSnapshots(storeKey,probeId,fromSequence,limit,kind){let snapshots;if(probeId)snapshots=getSnapshotsByProbe(storeKey,probeId);else if(kind){let probes=listProbes(storeKey),probeIds=new Set(probes.filter(p=>p.kind===kind).map(p=>p.id)),allSnapshots=getSnapshots(storeKey);kind==="tracepoint"?snapshots=allSnapshots.filter(s=>probeIds.has(s.probeId)||s.probeId==="__exception__"):snapshots=allSnapshots.filter(s=>probeIds.has(s.probeId))}else snapshots=getSnapshots(storeKey);return fromSequence!==void 0&&(snapshots=snapshots.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&(snapshots=snapshots.slice(-limit)),snapshots}var GetTracepointSnapshots2=class{name(){return"debug_get-tracepoint-snapshots"}description(){return"Retrieves snapshots captured by tracepoints on the Node.js process. Each snapshot contains call stack with local variables, async stack trace, watch expression results, and traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{probeId:z72.string().describe("Filter by specific tracepoint ID").optional(),fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=_filterSnapshots(context.storeKey,args.probeId,args.fromSequence,args.limit,"tracepoint");return{snapshots,count:snapshots.length}}},GetLogpointSnapshots2=class{name(){return"debug_get-logpoint-snapshots"}description(){return"Retrieves snapshots captured by logpoints on the Node.js process. Each snapshot contains the log expression result and traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{probeId:z72.string().describe("Filter by specific logpoint ID").optional(),fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=_filterSnapshots(context.storeKey,args.probeId,args.fromSequence,args.limit,"logpoint");return{snapshots,count:snapshots.length}}},GetExceptionpointSnapshots2=class{name(){return"debug_get-exceptionpoint-snapshots"}description(){return"Retrieves snapshots captured by exception breakpoints on the Node.js process. Snapshots include traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=getSnapshots(context.storeKey).filter(s=>s.probeId==="__exception__");return args.fromSequence!==void 0&&(snapshots=snapshots.filter(s=>s.sequenceNumber>args.fromSequence)),args.limit!==void 0&&(snapshots=snapshots.slice(-args.limit)),{snapshots,count:snapshots.length}}},ClearTracepointSnapshots2=class{name(){return"debug_clear-tracepoint-snapshots"}description(){return"Clears tracepoint snapshots. Optionally filter by specific tracepoint ID."}inputSchema(){return{probeId:z72.string().describe("Clear only snapshots for this tracepoint ID").optional()}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,args){let{clearSnapshots,clearSnapshotsByProbe:clearSnapshotsByProbe2}=await import("./core-YLK4BRF2.js");return{cleared:args.probeId?clearSnapshotsByProbe2(context.storeKey,args.probeId):clearSnapshots(context.storeKey)}}},ClearLogpointSnapshots2=class{name(){return"debug_clear-logpoint-snapshots"}description(){return"Clears logpoint snapshots. Optionally filter by specific logpoint ID."}inputSchema(){return{probeId:z72.string().describe("Clear only snapshots for this logpoint ID").optional()}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2,clearSnapshots}=await import("./core-YLK4BRF2.js");return{cleared:args.probeId?clearSnapshotsByProbe2(context.storeKey,args.probeId):clearSnapshots(context.storeKey)}}},ClearExceptionpointSnapshots2=class{name(){return"debug_clear-exceptionpoint-snapshots"}description(){return"Clears all exception snapshots."}inputSchema(){return{}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,_args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2}=await import("./core-YLK4BRF2.js");return{cleared:clearSnapshotsByProbe2(context.storeKey,"__exception__")}}};import{z as z73}from"zod";var GetLogs=class{name(){return"debug_get-logs"}description(){return"Retrieves console messages/logs from the Node.js process with filtering options."}inputSchema(){return{type:z73.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).describe(`Type of console messages to retrieve. When specified, messages with equal or higher levels are retrieved. Valid values: ${getEnumKeyTuples(ConsoleMessageLevelName).join(", ")}.`).optional(),search:z73.string().describe("Text to search for in console messages.").optional(),timestamp:z73.number().int().nonnegative().describe("Start time filter as Unix epoch timestamp in milliseconds. If provided, only messages recorded at or after this timestamp will be returned.").optional(),sequenceNumber:z73.number().int().nonnegative().describe("Sequence number for incremental retrieval. If provided, only messages with sequence number greater than this value will be returned.").optional(),limit:z73.object({count:z73.number().int().nonnegative().default(0).describe("Max number of messages. 0 = no limit."),from:z73.enum(["start","end"]).default("end").describe("Which side to keep when truncated.")}).describe("Maximum number of console messages to return.").optional()}}outputSchema(){return{messages:z73.array(z73.object({type:z73.string().describe("Type of the console message."),text:z73.string().describe("Text of the console message."),location:z73.object({url:z73.string().describe("URL of the resource."),lineNumber:z73.number().nonnegative().describe("0-based line number."),columnNumber:z73.number().nonnegative().describe("0-based column number.")}).describe("Location of the console message.").optional(),timestamp:z73.number().int().nonnegative().describe("Unix epoch timestamp (ms)."),sequenceNumber:z73.number().int().nonnegative().describe("Monotonic sequence number.")})).describe("Retrieved console messages.")}}async handle(context,args){let levelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filtered=getConsoleMessages(context.storeKey).filter(msg=>!(levelCodeThreshold!==void 0&&msg.level.code<levelCodeThreshold||args.timestamp&&msg.timestamp<args.timestamp||args.sequenceNumber&&msg.sequenceNumber<=args.sequenceNumber||args.search&&!msg.text.includes(args.search)));return{messages:(args.limit?.count?args.limit.from==="start"?filtered.slice(0,args.limit.count):filtered.slice(-args.limit.count):filtered).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z74}from"zod";var RemoveTracepoint2=class{name(){return"debug_remove-tracepoint"}description(){return"Removes a tracepoint by its ID."}inputSchema(){return{id:z74.string().describe("Tracepoint ID to remove")}}outputSchema(){return{removed:z74.boolean().describe("Whether the tracepoint was removed")}}async handle(context,args){return{removed:await removeProbe(context.storeKey,args.id)}}},ListTracepoints2=class{name(){return"debug_list-tracepoints"}description(){return"Lists all active tracepoints on the Node.js process."}inputSchema(){return{}}outputSchema(){return{tracepoints:z74.array(z74.any()).describe("Active tracepoints")}}async handle(context,_args){return{tracepoints:listProbes(context.storeKey).filter(p=>p.kind==="tracepoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled}))}}},ClearTracepoints2=class{name(){return"debug_clear-tracepoints"}description(){return"Removes all tracepoints from the Node.js process."}inputSchema(){return{}}outputSchema(){return{cleared:z74.number().describe("Number of tracepoints cleared")}}async handle(context,_args){let probes=listProbes(context.storeKey).filter(p=>p.kind==="tracepoint"),count=0;for(let p of probes)await removeProbe(context.storeKey,p.id)&&count++;return{cleared:count}}},RemoveLogpoint2=class{name(){return"debug_remove-logpoint"}description(){return"Removes a logpoint by its ID."}inputSchema(){return{id:z74.string().describe("Logpoint ID to remove")}}outputSchema(){return{removed:z74.boolean().describe("Whether the logpoint was removed")}}async handle(context,args){return{removed:await removeProbe(context.storeKey,args.id)}}},ListLogpoints2=class{name(){return"debug_list-logpoints"}description(){return"Lists all active logpoints on the Node.js process."}inputSchema(){return{}}outputSchema(){return{logpoints:z74.array(z74.any()).describe("Active logpoints")}}async handle(context,_args){return{logpoints:listProbes(context.storeKey).filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled}))}}},ClearLogpoints2=class{name(){return"debug_clear-logpoints"}description(){return"Removes all logpoints from the Node.js process."}inputSchema(){return{}}outputSchema(){return{cleared:z74.number().describe("Number of logpoints cleared")}}async handle(context,_args){let probes=listProbes(context.storeKey).filter(p=>p.kind==="logpoint"),count=0;for(let p of probes)await removeProbe(context.storeKey,p.id)&&count++;return{cleared:count}}};import{z as z75}from"zod";var PutExceptionpoint2=class{name(){return"debug_put-exceptionpoint"}description(){return`
1069
+ `}inputSchema(){return{pid:z70.number().int().positive().describe("Process ID to connect to").optional(),processName:z70.string().describe('Process name pattern to search for (e.g., "server.js", "api")').optional(),containerId:z70.string().describe("Docker container ID - process runs inside this container").optional(),containerName:z70.string().describe("Docker container name or pattern (e.g., my-node-app) - resolved via docker ps -f name=").optional(),host:z70.string().describe("Inspector host (default: 127.0.0.1); for containerized MCP use host.docker.internal or container name").optional(),port:z70.number().int().positive().describe("Inspector port (default: 9229); for Docker use host-mapped port").optional(),wsUrl:z70.string().describe("Direct WebSocket URL (e.g., ws://127.0.0.1:9229/abc-123)").optional()}}outputSchema(){return{connected:z70.boolean().describe("Whether connection was successful"),pid:z70.number().describe("Process ID"),command:z70.string().optional().describe("Process command line"),containerId:z70.string().optional().describe("Docker container ID when connected via container"),inspectorHost:z70.string().optional().describe("Inspector host"),inspectorPort:z70.number().optional().describe("Inspector port"),webSocketUrl:z70.string().optional().describe("WebSocket URL"),title:z70.string().describe("Target title")}}async handle(context,args){let result=await context.connect({pid:args.pid,processName:args.processName,containerId:args.containerId,containerName:args.containerName,host:args.host,port:args.port,wsUrl:args.wsUrl,activateIfNeeded:!0});return{connected:!0,pid:result.processInfo.pid,command:result.processInfo.command,containerId:result.processInfo.containerId,inspectorHost:result.processInfo.inspectorHost,inspectorPort:result.processInfo.inspectorPort,webSocketUrl:result.processInfo.webSocketUrl,title:result.target.title}}};import{z as z71}from"zod";var Disconnect=class{name(){return"debug_disconnect"}description(){return"Disconnects from the currently connected Node.js process and cleans up debugging resources."}inputSchema(){return{}}outputSchema(){return{disconnected:z71.boolean().describe("Whether disconnection was successful")}}async handle(context,_args){return await context.disconnect(),{disconnected:!0}}};import{z as z72}from"zod";function _filterSnapshots(storeKey,probeId,fromSequence,limit,kind){let snapshots;if(probeId)snapshots=getSnapshotsByProbe(storeKey,probeId);else if(kind){let probes=listProbes(storeKey),probeIds=new Set(probes.filter(p=>p.kind===kind).map(p=>p.id)),allSnapshots=getSnapshots(storeKey);kind==="tracepoint"?snapshots=allSnapshots.filter(s=>probeIds.has(s.probeId)||s.probeId==="__exception__"):snapshots=allSnapshots.filter(s=>probeIds.has(s.probeId))}else snapshots=getSnapshots(storeKey);return fromSequence!==void 0&&(snapshots=snapshots.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&(snapshots=snapshots.slice(-limit)),snapshots}var GetTracepointSnapshots2=class{name(){return"debug_get-tracepoint-snapshots"}description(){return"Retrieves snapshots captured by tracepoints on the Node.js process. Each snapshot contains call stack with local variables, async stack trace, watch expression results, and traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{probeId:z72.string().describe("Filter by specific tracepoint ID").optional(),fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=_filterSnapshots(context.storeKey,args.probeId,args.fromSequence,args.limit,"tracepoint");return{snapshots,count:snapshots.length}}},GetLogpointSnapshots2=class{name(){return"debug_get-logpoint-snapshots"}description(){return"Retrieves snapshots captured by logpoints on the Node.js process. Each snapshot contains the log expression result and traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{probeId:z72.string().describe("Filter by specific logpoint ID").optional(),fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=_filterSnapshots(context.storeKey,args.probeId,args.fromSequence,args.limit,"logpoint");return{snapshots,count:snapshots.length}}},GetExceptionpointSnapshots2=class{name(){return"debug_get-exceptionpoint-snapshots"}description(){return"Retrieves snapshots captured by exception breakpoints on the Node.js process. Snapshots include traceContext (traceId, spanId) when the process uses @opentelemetry/api."}inputSchema(){return{fromSequence:z72.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence").optional(),limit:z72.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:z72.array(z72.any()).describe("Captured snapshots"),count:z72.number().describe("Number of snapshots returned")}}async handle(context,args){let snapshots=getSnapshots(context.storeKey).filter(s=>s.probeId==="__exception__");return args.fromSequence!==void 0&&(snapshots=snapshots.filter(s=>s.sequenceNumber>args.fromSequence)),args.limit!==void 0&&(snapshots=snapshots.slice(-args.limit)),{snapshots,count:snapshots.length}}},ClearTracepointSnapshots2=class{name(){return"debug_clear-tracepoint-snapshots"}description(){return"Clears tracepoint snapshots. Optionally filter by specific tracepoint ID."}inputSchema(){return{probeId:z72.string().describe("Clear only snapshots for this tracepoint ID").optional()}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,args){let{clearSnapshots,clearSnapshotsByProbe:clearSnapshotsByProbe2}=await import("./core-FCPLN2O4.js");return{cleared:args.probeId?clearSnapshotsByProbe2(context.storeKey,args.probeId):clearSnapshots(context.storeKey)}}},ClearLogpointSnapshots2=class{name(){return"debug_clear-logpoint-snapshots"}description(){return"Clears logpoint snapshots. Optionally filter by specific logpoint ID."}inputSchema(){return{probeId:z72.string().describe("Clear only snapshots for this logpoint ID").optional()}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2,clearSnapshots}=await import("./core-FCPLN2O4.js");return{cleared:args.probeId?clearSnapshotsByProbe2(context.storeKey,args.probeId):clearSnapshots(context.storeKey)}}},ClearExceptionpointSnapshots2=class{name(){return"debug_clear-exceptionpoint-snapshots"}description(){return"Clears all exception snapshots."}inputSchema(){return{}}outputSchema(){return{cleared:z72.number().describe("Number of snapshots cleared")}}async handle(context,_args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2}=await import("./core-FCPLN2O4.js");return{cleared:clearSnapshotsByProbe2(context.storeKey,"__exception__")}}};import{z as z73}from"zod";var GetLogs=class{name(){return"debug_get-logs"}description(){return"Retrieves console messages/logs from the Node.js process with filtering options."}inputSchema(){return{type:z73.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).describe(`Type of console messages to retrieve. When specified, messages with equal or higher levels are retrieved. Valid values: ${getEnumKeyTuples(ConsoleMessageLevelName).join(", ")}.`).optional(),search:z73.string().describe("Text to search for in console messages.").optional(),timestamp:z73.number().int().nonnegative().describe("Start time filter as Unix epoch timestamp in milliseconds. If provided, only messages recorded at or after this timestamp will be returned.").optional(),sequenceNumber:z73.number().int().nonnegative().describe("Sequence number for incremental retrieval. If provided, only messages with sequence number greater than this value will be returned.").optional(),limit:z73.object({count:z73.number().int().nonnegative().default(0).describe("Max number of messages. 0 = no limit."),from:z73.enum(["start","end"]).default("end").describe("Which side to keep when truncated.")}).describe("Maximum number of console messages to return.").optional()}}outputSchema(){return{messages:z73.array(z73.object({type:z73.string().describe("Type of the console message."),text:z73.string().describe("Text of the console message."),location:z73.object({url:z73.string().describe("URL of the resource."),lineNumber:z73.number().nonnegative().describe("0-based line number."),columnNumber:z73.number().nonnegative().describe("0-based column number.")}).describe("Location of the console message.").optional(),timestamp:z73.number().int().nonnegative().describe("Unix epoch timestamp (ms)."),sequenceNumber:z73.number().int().nonnegative().describe("Monotonic sequence number.")})).describe("Retrieved console messages.")}}async handle(context,args){let levelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filtered=getConsoleMessages(context.storeKey).filter(msg=>!(levelCodeThreshold!==void 0&&msg.level.code<levelCodeThreshold||args.timestamp&&msg.timestamp<args.timestamp||args.sequenceNumber&&msg.sequenceNumber<=args.sequenceNumber||args.search&&!msg.text.includes(args.search)));return{messages:(args.limit?.count?args.limit.from==="start"?filtered.slice(0,args.limit.count):filtered.slice(-args.limit.count):filtered).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z74}from"zod";var RemoveTracepoint2=class{name(){return"debug_remove-tracepoint"}description(){return"Removes a tracepoint by its ID."}inputSchema(){return{id:z74.string().describe("Tracepoint ID to remove")}}outputSchema(){return{removed:z74.boolean().describe("Whether the tracepoint was removed")}}async handle(context,args){return{removed:await removeProbe(context.storeKey,args.id)}}},ListTracepoints2=class{name(){return"debug_list-tracepoints"}description(){return"Lists all active tracepoints on the Node.js process."}inputSchema(){return{}}outputSchema(){return{tracepoints:z74.array(z74.any()).describe("Active tracepoints")}}async handle(context,_args){return{tracepoints:listProbes(context.storeKey).filter(p=>p.kind==="tracepoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled}))}}},ClearTracepoints2=class{name(){return"debug_clear-tracepoints"}description(){return"Removes all tracepoints from the Node.js process."}inputSchema(){return{}}outputSchema(){return{cleared:z74.number().describe("Number of tracepoints cleared")}}async handle(context,_args){let probes=listProbes(context.storeKey).filter(p=>p.kind==="tracepoint"),count=0;for(let p of probes)await removeProbe(context.storeKey,p.id)&&count++;return{cleared:count}}},RemoveLogpoint2=class{name(){return"debug_remove-logpoint"}description(){return"Removes a logpoint by its ID."}inputSchema(){return{id:z74.string().describe("Logpoint ID to remove")}}outputSchema(){return{removed:z74.boolean().describe("Whether the logpoint was removed")}}async handle(context,args){return{removed:await removeProbe(context.storeKey,args.id)}}},ListLogpoints2=class{name(){return"debug_list-logpoints"}description(){return"Lists all active logpoints on the Node.js process."}inputSchema(){return{}}outputSchema(){return{logpoints:z74.array(z74.any()).describe("Active logpoints")}}async handle(context,_args){return{logpoints:listProbes(context.storeKey).filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled}))}}},ClearLogpoints2=class{name(){return"debug_clear-logpoints"}description(){return"Removes all logpoints from the Node.js process."}inputSchema(){return{}}outputSchema(){return{cleared:z74.number().describe("Number of logpoints cleared")}}async handle(context,_args){let probes=listProbes(context.storeKey).filter(p=>p.kind==="logpoint"),count=0;for(let p of probes)await removeProbe(context.storeKey,p.id)&&count++;return{cleared:count}}};import{z as z75}from"zod";var PutExceptionpoint2=class{name(){return"debug_put-exceptionpoint"}description(){return`
1063
1070
  Sets the exception tracepoint state for a Node.js process:
1064
1071
  - "none": Don't capture on exceptions
1065
1072
  - "uncaught": Capture only on uncaught exceptions
@@ -1121,18 +1128,23 @@ This code executes in the NODE PROCESS CONTEXT (real Node.js environment):
1121
1128
  - Can read/modify process state
1122
1129
  - Runs with full Node.js APIs (fs, http, etc.)
1123
1130
 
1131
+ Script format:
1132
+ - To get output you must use \`return\` (e.g. \`return 2+2;\`, \`return process.memoryUsage();\`, \`return JSON.stringify({...})\`). The script is run inside an async function, so \`return\` is required to pass a value back.
1133
+ - Async code is supported: use \`await\` and return a Promise (e.g. \`return await fetch(url).then(r => r.json());\`). The tool waits for the Promise to resolve and returns the resolved value.
1134
+
1124
1135
  Typical use cases:
1125
- - Inspect process state: process.memoryUsage(), process.uptime()
1126
- - Call loaded modules: require('os').loadavg()
1136
+ - Inspect process state: return process.memoryUsage(); return process.uptime();
1137
+ - Call loaded modules: return require('os').loadavg();
1127
1138
  - Read globals or cached data
1128
1139
  - One-off diagnostic commands
1140
+ - Async: return await someAsyncCall();
1129
1141
 
1130
1142
  Notes:
1131
1143
  - Requires debug_connect first - must be connected to a Node.js process
1132
- - Execution blocks the Node event loop until the script completes
1133
- - Long-running scripts will block the process; use short expressions
1144
+ - Execution blocks the Node event loop until the script (and any returned Promise) completes
1145
+ - Long-running scripts will block the process; keep scripts short
1134
1146
  - Return value must be serializable (JSON-compatible)
1135
- `}inputSchema(){return{script:z81.string().describe("JavaScript code to execute in the Node process."),timeoutMs:z81.number().int().min(100).max(3e4).describe("Max evaluation time in milliseconds (default: 5000, max: 30000).").optional()}}outputSchema(){return{result:z81.any().describe("The result of the evaluation. Can be primitives, arrays, or objects. Must be serializable (JSON-compatible).")}}async handle(context,args){return{result:await evaluateInNode(context.storeKey,args.script,args.timeoutMs??5e3)}}};var tools14=[new RunJsInNode];import{EventEmitter}from"node:events";import{WebSocket}from"ws";var NodeCDPSession=class extends EventEmitter{ws=null;reqId=0;pending=new Map;connected=!1;wsUrl;connectOptions;constructor(wsUrl,connectOptions){super(),this.wsUrl=wsUrl,this.connectOptions=connectOptions}async connect(){if(this.connected)return;let wsOptions=this.connectOptions?.headers?{headers:this.connectOptions.headers}:void 0;return new Promise((resolve,reject)=>{this.ws=new WebSocket(this.wsUrl,wsOptions);let timeout=setTimeout(()=>{reject(new Error(`Connection timeout to ${this.wsUrl}`)),this.ws?.close()},1e4);this.ws.on("open",()=>{clearTimeout(timeout),this.connected=!0,resolve()}),this.ws.on("error",err=>{clearTimeout(timeout),this.connected||reject(err)}),this.ws.on("close",()=>{this.connected=!1;for(let[id,handler]of this.pending)handler.reject(new Error("CDP session closed"));this.pending.clear()}),this.ws.on("message",data=>{try{let msg=JSON.parse(data.toString());this._handleMessage(msg)}catch{}})})}async send(method,params){if(!this.ws||!this.connected)throw new Error("CDP session is not connected");let id=++this.reqId;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});let message=JSON.stringify({id,method,params:params||{}});this.ws.send(message,err=>{err&&(this.pending.delete(id),reject(err))}),setTimeout(()=>{this.pending.has(id)&&(this.pending.delete(id),reject(new Error(`CDP command timeout: ${method}`)))},3e4)})}async detach(){if(this.ws){this.connected=!1,this.ws.close(),this.ws=null;for(let[,handler]of this.pending)handler.reject(new Error("CDP session detached"));this.pending.clear()}}isConnected(){return this.connected}_handleMessage(msg){if(msg.id!==void 0){let handler=this.pending.get(msg.id);handler&&(this.pending.delete(msg.id),msg.error?handler.reject(new Error(`CDP error: ${msg.error.message||JSON.stringify(msg.error)}`)):handler.resolve(msg.result||{}))}else msg.method&&this.emit(msg.method,msg.params)}};async function createNodeCDPSession(wsUrl,connectOptions){let session=new NodeCDPSession(wsUrl,connectOptions);return await session.connect(),session}import{execSync}from"node:child_process";import*as http from"node:http";var DEFAULT_HOST="127.0.0.1",DEFAULT_PORT=9229,DEFAULT_TIMEOUT_MS7=1e4,ACTIVATION_POLL_INTERVAL_MS=200,MAX_INSPECTOR_SCAN_PORTS=20;function buildInspectorWebSocketUrl(host,port,targetWebSocketDebuggerUrl){let path4=new URL(targetWebSocketDebuggerUrl).pathname;return`ws://${host}:${port}${path4}`}var DISCOVERY_HOST_HEADER="127.0.0.1:9229";function inspectorWsConnectOptions(wsUrl){try{let hostname=new URL(wsUrl).hostname.toLowerCase();return hostname==="127.0.0.1"||hostname==="localhost"?void 0:{headers:{Host:DISCOVERY_HOST_HEADER}}}catch{return}}async function discoverInspectorTargets(host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=5e3){return new Promise((resolve,reject)=>{let timeout=setTimeout(()=>{reject(new Error(`Inspector discovery timeout on ${host}:${port}`))},timeoutMs),req=http.get({host,port,path:"/json/list",...host!==DEFAULT_HOST&&host!=="localhost"&&{headers:{Host:DISCOVERY_HOST_HEADER}}},res=>{let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>{clearTimeout(timeout);try{let targets=JSON.parse(data);resolve(targets)}catch{reject(new Error(`Invalid response from inspector at ${host}:${port}`))}})});req.on("error",err=>{clearTimeout(timeout),reject(new Error(`Cannot reach inspector at ${host}:${port}: ${err.message}`))}),req.end()})}async function scanForInspector(host=DEFAULT_HOST,startPort=DEFAULT_PORT,maxPorts=MAX_INSPECTOR_SCAN_PORTS){for(let port=startPort;port<startPort+maxPorts;port++)try{let targets=await discoverInspectorTargets(host,port,1e3);if(targets.length>0)return{port,targets}}catch{}return null}function findNodeProcesses(namePattern){try{let platform=process.platform,output;platform==="win32"?output=execSync(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
1147
+ `}inputSchema(){return{script:z81.string().describe("JavaScript code to execute in the Node process. Use return to pass a value back (e.g. return 2+2; or return await fetch(url).then(r => r.json());). Async/await is supported."),timeoutMs:z81.number().int().min(100).max(3e4).describe("Max evaluation time in milliseconds (default: 5000, max: 30000).").optional()}}outputSchema(){return{result:z81.any().describe("The result of the evaluation. Can be primitives, arrays, or objects. Must be serializable (JSON-compatible).")}}async handle(context,args){return{result:await evaluateInNode(context.storeKey,args.script,args.timeoutMs??5e3)}}};var tools14=[new RunJsInNode];import{EventEmitter}from"node:events";import{WebSocket}from"ws";var NodeCDPSession=class extends EventEmitter{ws=null;reqId=0;pending=new Map;connected=!1;wsUrl;connectOptions;constructor(wsUrl,connectOptions){super(),this.wsUrl=wsUrl,this.connectOptions=connectOptions}async connect(){if(this.connected)return;let wsOptions=this.connectOptions?.headers?{headers:this.connectOptions.headers}:void 0;return new Promise((resolve,reject)=>{this.ws=new WebSocket(this.wsUrl,wsOptions);let timeout=setTimeout(()=>{reject(new Error(`Connection timeout to ${this.wsUrl}`)),this.ws?.close()},1e4);this.ws.on("open",()=>{clearTimeout(timeout),this.connected=!0,resolve()}),this.ws.on("error",err=>{clearTimeout(timeout),this.connected||reject(err)}),this.ws.on("close",()=>{this.connected=!1;for(let[id,handler]of this.pending)handler.reject(new Error("CDP session closed"));this.pending.clear()}),this.ws.on("message",data=>{try{let msg=JSON.parse(data.toString());this._handleMessage(msg)}catch{}})})}async send(method,params){if(!this.ws||!this.connected)throw new Error("CDP session is not connected");let id=++this.reqId;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});let message=JSON.stringify({id,method,params:params||{}});this.ws.send(message,err=>{err&&(this.pending.delete(id),reject(err))}),setTimeout(()=>{this.pending.has(id)&&(this.pending.delete(id),reject(new Error(`CDP command timeout: ${method}`)))},3e4)})}async detach(){if(this.ws){this.connected=!1,this.ws.close(),this.ws=null;for(let[,handler]of this.pending)handler.reject(new Error("CDP session detached"));this.pending.clear()}}isConnected(){return this.connected}_handleMessage(msg){if(msg.id!==void 0){let handler=this.pending.get(msg.id);handler&&(this.pending.delete(msg.id),msg.error?handler.reject(new Error(`CDP error: ${msg.error.message||JSON.stringify(msg.error)}`)):handler.resolve(msg.result||{}))}else msg.method&&this.emit(msg.method,msg.params)}};async function createNodeCDPSession(wsUrl,connectOptions){let session=new NodeCDPSession(wsUrl,connectOptions);return await session.connect(),session}import{execSync}from"node:child_process";import*as http from"node:http";var DEFAULT_HOST="127.0.0.1",DEFAULT_PORT=9229,DEFAULT_TIMEOUT_MS7=1e4,ACTIVATION_POLL_INTERVAL_MS=200,MAX_INSPECTOR_SCAN_PORTS=20;function buildInspectorWebSocketUrl(host,port,targetWebSocketDebuggerUrl){let path4=new URL(targetWebSocketDebuggerUrl).pathname;return`ws://${host}:${port}${path4}`}var DISCOVERY_HOST_HEADER="127.0.0.1:9229";function inspectorWsConnectOptions(wsUrl){try{let hostname=new URL(wsUrl).hostname.toLowerCase();return hostname==="127.0.0.1"||hostname==="localhost"?void 0:{headers:{Host:DISCOVERY_HOST_HEADER}}}catch{return}}async function discoverInspectorTargets(host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=5e3){return new Promise((resolve,reject)=>{let timeout=setTimeout(()=>{reject(new Error(`Inspector discovery timeout on ${host}:${port}`))},timeoutMs),req=http.get({host,port,path:"/json/list",...host!==DEFAULT_HOST&&host!=="localhost"&&{headers:{Host:DISCOVERY_HOST_HEADER}}},res=>{let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>{clearTimeout(timeout);try{let targets=JSON.parse(data);resolve(targets)}catch{reject(new Error(`Invalid response from inspector at ${host}:${port}`))}})});req.on("error",err=>{clearTimeout(timeout),reject(new Error(`Cannot reach inspector at ${host}:${port}: ${err.message}`))}),req.end()})}async function scanForInspector(host=DEFAULT_HOST,startPort=DEFAULT_PORT,maxPorts=MAX_INSPECTOR_SCAN_PORTS){for(let port=startPort;port<startPort+maxPorts;port++)try{let targets=await discoverInspectorTargets(host,port,1e3);if(targets.length>0)return{port,targets}}catch{}return null}function findNodeProcesses(namePattern){try{let platform=process.platform,output;platform==="win32"?output=execSync(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
1136
1148
  `)){let trimmed=line.trim();if(trimmed)if(platform==="win32"){let parts=trimmed.split(",");if(parts.length>=3){let pid=parseInt(parts[parts.length-1],10),command=parts.slice(1,-1).join(",");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}else{let parts=trimmed.split(/\s+/);if(parts.length>=11){let pid=parseInt(parts[1],10),command=parts.slice(10).join(" ");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}}if(namePattern){let regex=new RegExp(namePattern,"i");return processes.filter(p=>regex.test(p.command))}return processes}catch{return[]}}async function activateInspector(pid,host=DEFAULT_HOST,timeoutMs=DEFAULT_TIMEOUT_MS7){if(process.platform==="win32")throw new Error("SIGUSR1 activation is not supported on Windows. Start the Node.js process with --inspect flag or NODE_OPTIONS=--inspect instead.");try{process.kill(pid,0)}catch{throw new Error(`Process ${pid} does not exist or is not accessible`)}debug(`Sending SIGUSR1 to process ${pid} to activate V8 Inspector...`),process.kill(pid,"SIGUSR1");let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){let result=await scanForInspector(host,DEFAULT_PORT,10);if(result)return debug(`Inspector activated on port ${result.port} for process ${pid}`),result;await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector to activate on process ${pid}`)}function resolveContainerId(containerName){try{let ids=execSync(`docker ps -q -f name=${containerName}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
1137
1149
  `).filter(Boolean);return ids.length===0?null:(ids.length>1&&debug(`Multiple containers match "${containerName}", using first: ${ids[0]}`),ids[0])}catch(err){if(/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??""))throw new Error(`${err.message} When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.`);return null}}async function activateInspectorInContainer(containerId,host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=DEFAULT_TIMEOUT_MS7){if(process.platform==="win32")throw new Error("Docker container activation may have limitations on Windows. Ensure the container has port 9229 exposed and Node is started with --inspect.");let hostPid="";try{let lines=execSync(`docker top ${containerId}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
1138
1150
  `);if(lines.length<2)throw new Error(`No Node.js process found inside container ${containerId}`);for(let i=1;i<lines.length;i++){let line=lines[i];if(!/node/i.test(line))continue;let parts=line.trim().split(/\s+/);if(parts.length>=2&&/^\d+$/.test(parts[1])){hostPid=parts[1];break}}if(!hostPid)throw new Error(`Could not parse Node PID from container ${containerId}`)}catch(err){if(err.message?.includes("No Node.js process"))throw err;let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Cannot find Node process in container ${containerId}: ${err.message}. Ensure Docker is running and the container has a Node.js process.${dockerHint}`)}debug(`Sending SIGUSR1 to Node process (host PID ${hostPid}) for container ${containerId}...`);try{execSync(`docker run --rm --pid=host alpine kill -USR1 ${hostPid}`,{encoding:"utf-8",timeout:1e4})}catch(err){let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Failed to send SIGUSR1 to process in container ${containerId}: ${err.message}. (Uses docker run --rm --pid=host alpine kill -USR1 <hostPid>; ensure alpine image is available.)${dockerHint}`)}let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){try{let targets=await discoverInspectorTargets(host,port,2e3);if(targets.length>0)return debug(`Inspector active at ${host}:${port} for container ${containerId}`),{port,targets}}catch(err){debug(`Inspector not yet ready at ${host}:${port}: ${err instanceof Error?err.message:String(err)}`)}await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector from container ${containerId} at ${host}:${port}. Ensure: (1) the container has NODE_OPTIONS=--inspect-port=0.0.0.0:9229 so inspector accepts external connections when activated via SIGUSR1; (2) the container exposes port 9229 to the host (e.g. -p 30019:9229); (3) when MCP runs in a container, host is reachable (e.g. NODE_INSPECTOR_HOST=host.docker.internal and on Linux add extra_hosts: host.docker.internal:host-gateway).`)}async function connectToNodeProcess(options){let host=options.host??NODE_INSPECTOR_HOST??DEFAULT_HOST,timeoutMs=options.timeoutMs||DEFAULT_TIMEOUT_MS7,activateIfNeeded=options.activateIfNeeded!==!1;if(options.wsUrl)return debug(`Connecting to Node.js inspector via WebSocket: ${options.wsUrl}`),{session:await createNodeCDPSession(options.wsUrl,inspectorWsConnectOptions(options.wsUrl)),processInfo:{pid:0,inspectorActive:!0,webSocketUrl:options.wsUrl},target:{id:"direct",type:"node",title:"Direct WebSocket connection",url:"",webSocketDebuggerUrl:options.wsUrl}};let containerId=options.containerId??null;if(!containerId&&options.containerName&&(containerId=resolveContainerId(options.containerName),!containerId))throw new Error(`No running container found matching name: ${options.containerName}`);if(containerId){let port2=options.port||DEFAULT_PORT,targets2=[];try{targets2=await discoverInspectorTargets(host,port2,3e3)}catch{}if(targets2.length===0&&activateIfNeeded&&(debug(`Inspector not active. Activating via SIGUSR1 in container ${containerId}...`),targets2=(await activateInspectorInContainer(containerId,host,port2,timeoutMs)).targets),targets2.length===0)throw new Error(`Cannot connect to Node.js in container ${containerId}. Inspector not active. Ensure port 9229 is exposed (e.g. -p 9229:9229) and use activateIfNeeded or start Node with --inspect.`);let target=targets2[0],wsUrl=buildInspectorWebSocketUrl(host,port2,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid:0,inspectorActive:!0,inspectorHost:host,inspectorPort:port2,webSocketUrl:wsUrl,containerId},target}}let pid=options.pid,processCommand;if(!pid&&options.processName){let processes=findNodeProcesses(options.processName);if(processes.length===0)throw new Error(`No Node.js process found matching: ${options.processName}`);if(processes.length>1){let procList=processes.map(p=>` PID ${p.pid}: ${p.command}`).join(`
@@ -1 +1 @@
1
- import{platformInfo}from"./core-M3RUMGR5.js";import{DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,debug,enable,error,info,isDebugEnabled}from"./core-ARVKOWOZ.js";import{createRequire}from"node:module";import{Command,Option,InvalidOptionArgumentError}from"commander";import{serve}from"@hono/node-server";import{Hono}from"hono";import{cors}from"hono/cors";import{z}from"zod";var require2=createRequire(import.meta.url),daemonStartTime=0,daemonPort=0,app=new Hono,sessions=new Map,DEFAULT_SESSION_ID="#default",ERRORS={get sessionNotFound(){return _buildErrorResponse(404,"Session Not Found")},get toolNotFound(){return _buildErrorResponse(404,"Tool Not Found")},get internalServerError(){return _buildErrorResponse(500,"Internal Server Error")}};function _buildErrorResponse(code,message){return{error:{code,message}}}async function _closeSession(session){if(session.closed=!0,session.context)try{await session.context.close(),debug("Closed MCP session context")}catch(err){error("Error occurred while closing MCP session context",err)}sessions.delete(session.id)}function _createSession(ctx,sessionId){let now=Date.now(),session={id:sessionId,toolExecutor:platformInfo.toolsInfo.createToolExecutor(()=>sessionId),closed:!1,createdAt:now,lastActiveAt:now};return debug(`Created session with id ${sessionId}`),session}function _getSessionInfo(session){let now=Date.now();return{id:session.id,createdAt:session.createdAt,lastActiveAt:session.lastActiveAt,idleSeconds:Math.floor((now-session.lastActiveAt)/1e3)}}async function _getSession(ctx){let sessionId=ctx.req.header("session-id")||DEFAULT_SESSION_ID;return sessions.get(sessionId)}async function _getOrCreateSession(ctx){let sessionId=ctx.req.header("session-id")||DEFAULT_SESSION_ID,session=sessions.get(sessionId);return session?debug(`Reusing session with id ${sessionId}`):(debug(`No session could be found with id ${sessionId}`),session=_createSession(ctx,sessionId),sessions.set(sessionId,session)),session}function _scheduleIdleSessionCheck(){let noActiveSession=!1;setInterval(()=>{let currentTime=Date.now();noActiveSession&&sessions.size===0&&(info("No active session found, so terminating daemon server"),process.exit(0));for(let[sessionId,session]of sessions)debug(`Checking whether session with id ${sessionId} is idle or not ...`),currentTime-session.lastActiveAt>DAEMON_SESSION_IDLE_SECONDS*1e3&&(debug(`Session with id ${sessionId} is idle, so it will be closing ...`),_closeSession(session).then(()=>{debug(`Session with id ${sessionId} was idle, so it has been closed`)}).catch(err=>{error(`Unable to delete idle session with id ${sessionId}`,err)}));noActiveSession=sessions.size===0},DAEMON_SESSION_IDLE_CHECK_SECONDS*1e3)}async function _logRequest(ctx){let reqClone=ctx.req.raw.clone();debug(`Got request: ${await reqClone.json()}`)}async function startDaemonHTTPServer(port){let toolMap=Object.fromEntries(platformInfo.toolsInfo.tools.map(tool=>[tool.name(),tool]));app.use("*",cors({origin:"*",allowMethods:["GET","POST","DELETE","OPTIONS"],allowHeaders:["Content-Type","Authorization","session-id"]})),daemonPort=port,daemonStartTime=Date.now();let gracefulShutdown=async signal=>{info(`Received ${signal}, initiating graceful shutdown...`);let closePromises=[];for(let session of sessions.values())closePromises.push(_closeSession(session));await Promise.allSettled(closePromises),info("All sessions closed, exiting..."),process.exit(0)};process.on("SIGTERM",()=>gracefulShutdown("SIGTERM")),process.on("SIGINT",()=>gracefulShutdown("SIGINT")),process.on("uncaughtException",err=>{error("Uncaught exception",err)}),process.on("unhandledRejection",reason=>{error("Unhandled rejection",reason)}),app.get("/health",ctx=>ctx.json({status:"ok"})),app.get("/info",ctx=>{let info2={version:require2("../package.json").version,uptime:Math.floor((Date.now()-daemonStartTime)/1e3),sessionCount:sessions.size,port:daemonPort};return ctx.json(info2)}),app.get("/sessions",ctx=>{let sessionList=[];for(let session of sessions.values())sessionList.push(_getSessionInfo(session));return ctx.json({sessions:sessionList})}),app.get("/session",async ctx=>{let session=await _getSession(ctx);return session?ctx.json(_getSessionInfo(session)):ctx.json(ERRORS.sessionNotFound,404)}),app.post("/shutdown",async ctx=>{info("Shutdown request received, closing all sessions...");let closePromises=[];for(let session of sessions.values())closePromises.push(_closeSession(session));return await Promise.allSettled(closePromises),info("All sessions closed, shutting down daemon server..."),setTimeout(()=>{process.exit(0)},500),ctx.json({status:"shutting_down"},200)}),app.post("/call",async ctx=>{try{isDebugEnabled()&&await _logRequest(ctx);let session=await _getOrCreateSession(ctx);session.lastActiveAt=Date.now();let toolCallRequest=await ctx.req.json(),tool=toolMap[toolCallRequest.toolName];if(!tool)return ctx.json(ERRORS.toolNotFound,404);let toolInput;try{toolInput=z.object(tool.inputSchema()).parse(toolCallRequest.toolInput)}catch(err){let errorMessage=err.errors&&Array.isArray(err.errors)?err.errors.map(e=>`${e.path?.join(".")||"input"}: ${e.message}`).join("; "):"Invalid tool input";return ctx.json(_buildErrorResponse(400,`Invalid Tool Request: ${errorMessage}`),400)}try{let toolCallResponse={toolOutput:await session.toolExecutor.executeTool(tool,toolInput)};return ctx.json(toolCallResponse,200)}catch(err){let toolCallResponse={toolError:{code:err.code,message:err.message}};return ctx.json(toolCallResponse,500)}}catch(err){return error("Error occurred while handling tool call request",err),ctx.json(ERRORS.internalServerError,500)}}),app.delete("/session",async ctx=>{try{let session=await _getSession(ctx);return session?(await _closeSession(session),ctx.json({ok:!0},200)):ctx.json(ERRORS.sessionNotFound,404)}catch(err){return error("Error occurred while deleting session",err),ctx.json(ERRORS.internalServerError,500)}}),app.onError((err,ctx)=>(error("Unhandled error in request handler",err),ctx.json({error:{code:500,message:"Internal Server Error"}},500))),app.notFound(ctx=>ctx.json({error:"Not Found",status:404},404)),serve({fetch:app.fetch,port},()=>info(`Listening on port ${port}`)),_scheduleIdleSessionCheck()}var isMainModule=import.meta.url===`file://${process.argv[1]}`||import.meta.url===`file://${process.argv[1]}.mjs`||process.argv[1]?.endsWith("daemon-server.js")||process.argv[1]?.endsWith("daemon-server.mjs");if(isMainModule){let parsePort=function(value){let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new InvalidOptionArgumentError("port must be an integer between 1 and 65535");return n};parsePort2=parsePort;let options=new Command().addOption(new Option("--port <number>","port for daemon HTTP server").argParser(parsePort).default(DAEMON_PORT)).allowUnknownOption().parse(process.argv).opts();enable(),info("Starting daemon HTTP server..."),startDaemonHTTPServer(options.port).then(()=>{info("Daemon HTTP server started")}).catch(err=>{error("Failed to start daemon HTTP server",err),process.exit(1)})}var parsePort2;export{startDaemonHTTPServer};
1
+ import{platformInfo}from"./core-LM2SLZDP.js";import{AVAILABLE_TOOL_DOMAINS,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,debug,enable,error,info,isDebugEnabled}from"./core-247YVH6X.js";import{createRequire}from"node:module";import{Command,Option,InvalidOptionArgumentError}from"commander";import{serve}from"@hono/node-server";import{Hono}from"hono";import{cors}from"hono/cors";import{z}from"zod";var require2=createRequire(import.meta.url),daemonStartTime=0,daemonPort=0,app=new Hono,sessions=new Map,DEFAULT_SESSION_ID="#default",ERRORS={get sessionNotFound(){return _buildErrorResponse(404,"Session Not Found")},get toolNotFound(){return _buildErrorResponse(404,"Tool Not Found")},get internalServerError(){return _buildErrorResponse(500,"Internal Server Error")}};function _buildErrorResponse(code,message){return{error:{code,message}}}async function _closeSession(session){if(session.closed=!0,session.context)try{await session.context.close(),debug("Closed MCP session context")}catch(err){error("Error occurred while closing MCP session context",err)}sessions.delete(session.id)}function _createSession(ctx,sessionId){let now=Date.now(),session={id:sessionId,toolExecutor:platformInfo.toolsInfo.createToolExecutor(()=>sessionId),closed:!1,createdAt:now,lastActiveAt:now};return debug(`Created session with id ${sessionId}`),session}function _getSessionInfo(session){let now=Date.now();return{id:session.id,createdAt:session.createdAt,lastActiveAt:session.lastActiveAt,idleSeconds:Math.floor((now-session.lastActiveAt)/1e3)}}async function _getSession(ctx){let sessionId=ctx.req.header("session-id")||DEFAULT_SESSION_ID;return sessions.get(sessionId)}async function _getOrCreateSession(ctx){let sessionId=ctx.req.header("session-id")||DEFAULT_SESSION_ID,session=sessions.get(sessionId);return session?debug(`Reusing session with id ${sessionId}`):(debug(`No session could be found with id ${sessionId}`),session=_createSession(ctx,sessionId),sessions.set(sessionId,session)),session}function _scheduleIdleSessionCheck(){let noActiveSession=!1;setInterval(()=>{let currentTime=Date.now();noActiveSession&&sessions.size===0&&(info("No active session found, so terminating daemon server"),process.exit(0));for(let[sessionId,session]of sessions)debug(`Checking whether session with id ${sessionId} is idle or not ...`),currentTime-session.lastActiveAt>DAEMON_SESSION_IDLE_SECONDS*1e3&&(debug(`Session with id ${sessionId} is idle, so it will be closing ...`),_closeSession(session).then(()=>{debug(`Session with id ${sessionId} was idle, so it has been closed`)}).catch(err=>{error(`Unable to delete idle session with id ${sessionId}`,err)}));noActiveSession=sessions.size===0},DAEMON_SESSION_IDLE_CHECK_SECONDS*1e3)}async function _logRequest(ctx){let reqClone=ctx.req.raw.clone();debug(`Got request: ${await reqClone.json()}`)}async function startDaemonHTTPServer(port){let allowedDomains=AVAILABLE_TOOL_DOMAINS,toolsToExpose=allowedDomains===void 0?platformInfo.toolsInfo.tools:platformInfo.toolsInfo.tools.filter(tool=>{let domain=tool.name().split("_")[0]?.toLowerCase()??"";return allowedDomains.has(domain)}),toolMap=Object.fromEntries(toolsToExpose.map(tool=>[tool.name(),tool]));app.use("*",cors({origin:"*",allowMethods:["GET","POST","DELETE","OPTIONS"],allowHeaders:["Content-Type","Authorization","session-id"]})),daemonPort=port,daemonStartTime=Date.now();let gracefulShutdown=async signal=>{info(`Received ${signal}, initiating graceful shutdown...`);let closePromises=[];for(let session of sessions.values())closePromises.push(_closeSession(session));await Promise.allSettled(closePromises),info("All sessions closed, exiting..."),process.exit(0)};process.on("SIGTERM",()=>gracefulShutdown("SIGTERM")),process.on("SIGINT",()=>gracefulShutdown("SIGINT")),process.on("uncaughtException",err=>{error("Uncaught exception",err)}),process.on("unhandledRejection",reason=>{error("Unhandled rejection",reason)}),app.get("/health",ctx=>ctx.json({status:"ok"})),app.get("/info",ctx=>{let info2={version:require2("../package.json").version,uptime:Math.floor((Date.now()-daemonStartTime)/1e3),sessionCount:sessions.size,port:daemonPort};return ctx.json(info2)}),app.get("/sessions",ctx=>{let sessionList=[];for(let session of sessions.values())sessionList.push(_getSessionInfo(session));return ctx.json({sessions:sessionList})}),app.get("/session",async ctx=>{let session=await _getSession(ctx);return session?ctx.json(_getSessionInfo(session)):ctx.json(ERRORS.sessionNotFound,404)}),app.post("/shutdown",async ctx=>{info("Shutdown request received, closing all sessions...");let closePromises=[];for(let session of sessions.values())closePromises.push(_closeSession(session));return await Promise.allSettled(closePromises),info("All sessions closed, shutting down daemon server..."),setTimeout(()=>{process.exit(0)},500),ctx.json({status:"shutting_down"},200)}),app.post("/call",async ctx=>{try{isDebugEnabled()&&await _logRequest(ctx);let session=await _getOrCreateSession(ctx);session.lastActiveAt=Date.now();let toolCallRequest=await ctx.req.json(),tool=toolMap[toolCallRequest.toolName];if(!tool)return ctx.json(ERRORS.toolNotFound,404);let toolInput;try{toolInput=z.object(tool.inputSchema()).parse(toolCallRequest.toolInput)}catch(err){let errorMessage=err.errors&&Array.isArray(err.errors)?err.errors.map(e=>`${e.path?.join(".")||"input"}: ${e.message}`).join("; "):"Invalid tool input";return ctx.json(_buildErrorResponse(400,`Invalid Tool Request: ${errorMessage}`),400)}try{let toolCallResponse={toolOutput:await session.toolExecutor.executeTool(tool,toolInput)};return ctx.json(toolCallResponse,200)}catch(err){let toolCallResponse={toolError:{code:err.code,message:err.message}};return ctx.json(toolCallResponse,500)}}catch(err){return error("Error occurred while handling tool call request",err),ctx.json(ERRORS.internalServerError,500)}}),app.delete("/session",async ctx=>{try{let session=await _getSession(ctx);return session?(await _closeSession(session),ctx.json({ok:!0},200)):ctx.json(ERRORS.sessionNotFound,404)}catch(err){return error("Error occurred while deleting session",err),ctx.json(ERRORS.internalServerError,500)}}),app.onError((err,ctx)=>(error("Unhandled error in request handler",err),ctx.json({error:{code:500,message:"Internal Server Error"}},500))),app.notFound(ctx=>ctx.json({error:"Not Found",status:404},404)),serve({fetch:app.fetch,port},()=>info(`Listening on port ${port}`)),_scheduleIdleSessionCheck()}var isMainModule=import.meta.url===`file://${process.argv[1]}`||import.meta.url===`file://${process.argv[1]}.mjs`||process.argv[1]?.endsWith("daemon-server.js")||process.argv[1]?.endsWith("daemon-server.mjs");if(isMainModule){let parsePort=function(value){let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new InvalidOptionArgumentError("port must be an integer between 1 and 65535");return n};parsePort2=parsePort;let options=new Command().addOption(new Option("--port <number>","port for daemon HTTP server").argParser(parsePort).default(DAEMON_PORT)).allowUnknownOption().parse(process.argv).opts();enable(),info("Starting daemon HTTP server..."),startDaemonHTTPServer(options.port).then(()=>{info("Daemon HTTP server started")}).catch(err=>{error("Failed to start daemon HTTP server",err),process.exit(1)})}var parsePort2;export{startDaemonHTTPServer};
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{platformInfo}from"./core-M3RUMGR5.js";import{PORT,SESSION_CLOSE_ON_SOCKET_CLOSE,SESSION_IDLE_CHECK_SECONDS,SESSION_IDLE_SECONDS,debug,disable,enable,error,info,isDebugEnabled}from"./core-ARVKOWOZ.js";import{createRequire}from"node:module";var require2=createRequire(import.meta.url),SERVER_NAME="browser-devtools-mcp",SERVER_VERSION=require2("../package.json").version;function getServerInstructions(){let parts=[];return parts.push(platformInfo.serverInfo.instructions),parts.join(`
2
+ import{platformInfo}from"./core-LM2SLZDP.js";import{AVAILABLE_TOOL_DOMAINS,PORT,SESSION_CLOSE_ON_SOCKET_CLOSE,SESSION_IDLE_CHECK_SECONDS,SESSION_IDLE_SECONDS,TOOL_OUTPUT_SCHEMA_DISABLE,debug,disable,enable,error,info,isDebugEnabled}from"./core-247YVH6X.js";import{createRequire}from"node:module";var require2=createRequire(import.meta.url),SERVER_NAME="browser-devtools-mcp",SERVER_VERSION=require2("../package.json").version;function getServerInstructions(){let parts=[];return parts.push(platformInfo.serverInfo.instructions),parts.join(`
3
3
 
4
- `).trim()}function getServerPolicies(){return platformInfo.serverInfo.policies}import crypto from"node:crypto";import{StreamableHTTPTransport}from"@hono/mcp";import{serve}from"@hono/node-server";import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";import{Hono}from"hono";import{cors}from"hono/cors";var MCP_TEMPLATE={jsonrpc:"2.0",error:{code:0,message:"N/A"},id:null},MCP_ERRORS={get sessionNotFound(){return _buildMCPErrorResponse(-32001,"Session Not Found")},get unauthorized(){return _buildMCPErrorResponse(-32001,"Unauthorized")},get internalServerError(){return _buildMCPErrorResponse(-32603,"Internal Server Error")}},sessions=new Map;function _buildMCPErrorResponse(code,message){let result={...MCP_TEMPLATE};return result.error.code=code,result.error.message=message,result}function _getImage(response){if("image"in response&&response.image!==null&&typeof response.image=="object"&&"data"in response.image&&"mimeType"in response.image&&Buffer.isBuffer(response.image.data)&&typeof response.image.mimeType=="string"){let image=response.image;return delete response.image,image}}function _toResponse(response){let image=_getImage(response),contents=[];return contents.push({type:"text",text:JSON.stringify(response,null,2)}),image&&(image.mimeType==="image/svg+xml"?contents.push({type:"text",text:image.data.toString(),mimeType:image.mimeType}):contents.push({type:"image",data:image.data.toString("base64"),mimeType:image.mimeType})),{content:contents,structuredContent:response,isError:!1}}function _createServer(opts){let server=new McpServer({name:SERVER_NAME,version:SERVER_VERSION},{capabilities:{resources:{},tools:{}},instructions:getServerInstructions()}),messages=[],policies=getServerPolicies();if(policies)for(let policy of policies)messages.push({role:"user",content:{type:"text",text:policy}});server.registerPrompt("default_system",{title:"Default System Prompt",description:"General behavior for the AI assistant"},async()=>({description:"Defines the assistant's general reasoning and tool usage rules.",messages}));let toolExecutor=platformInfo.toolsInfo.createToolExecutor(()=>opts.sessionIdProvider?opts.sessionIdProvider():""),createToolCallback=tool=>async args=>{try{let response=await toolExecutor.executeTool(tool,args);return _toResponse(response)}catch(error2){return{content:[{type:"text",text:`Error: ${error2.message}`}],isError:!0}}};return platformInfo.toolsInfo.tools.forEach(t=>{debug(`Registering tool ${t.name()} ...`),server.registerTool(t.name(),{description:t.description(),inputSchema:t.inputSchema(),outputSchema:t.outputSchema()},createToolCallback(t))}),server}async function _createAndConnectServer(transport,opts){let server=_createServer({config:opts.config,sessionIdProvider:()=>transport.sessionId});return await server.connect(transport),server}function _getConfig(){return{}}function _createSession(ctx,transport,server){let session={transport,server,closed:!1,lastActiveAt:Date.now()},socket=ctx.env.incoming.socket;return socket._mcpRegistered||(socket._mcpRegistered=!0,socket.on("close",async()=>{debug(`Socket, which is for MCP session with id ${transport.sessionId}, has been closed`),SESSION_CLOSE_ON_SOCKET_CLOSE&&await transport.close()})),_registerMCPSessionClose(transport,session.server),debug(`Created MCP server session with id ${transport.sessionId}`),session}async function _createTransport(ctx){let serverConfig=_getConfig(),holder={},transport=new StreamableHTTPTransport({enableJsonResponse:!0,sessionIdGenerator:()=>crypto.randomUUID(),onsessioninitialized:async sessionId=>{let session=_createSession(ctx,transport,holder.server);sessions.set(sessionId,session),debug(`MCP session initialized with id ${sessionId}`)},onsessionclosed:async sessionId=>{debug(`Closing MCP session closed with id ${sessionId} ...`),await transport.close(),debug(`MCP session closed with id ${sessionId}`)}});return holder.server=await _createAndConnectServer(transport,{config:serverConfig}),transport}async function _getTransport(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);if(session)return debug(`Reusing MCP session with id ${sessionId}`),session.transport}}async function _getOrCreateTransport(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);if(session)return debug(`Reusing MCP session with id ${sessionId}`),session.transport;debug(`No MCP session could be found with id ${sessionId}`);return}return await _createTransport(ctx)}function _registerMCPSessionClose(transport,mcpServer){let closed=!1;transport.onclose=async()=>{if(debug(`Closing MCP session with id ${transport.sessionId} ...`),closed){debug(`MCP session with id ${transport.sessionId} has already been closed`);return}closed=!0;try{await mcpServer.close(),debug("Closed MCP server")}catch(err){error("Error occurred while closing MCP server",err)}if(transport.sessionId){let session=sessions.get(transport.sessionId);if(session&&(session.closed=!0,session.context))try{await session.context.close(),debug("Closed MCP session context")}catch(err){error("Error occurred while closing MCP session context",err)}sessions.delete(transport.sessionId)}debug(`Closing MCP session with id ${transport.sessionId} ...`)}}function _scheduleIdleSessionCheck(){setInterval(()=>{let currentTime=Date.now();for(let[sessionId,session]of sessions)debug(`Checking whether session with id ${sessionId} is idle or not ...`),currentTime-session.lastActiveAt>SESSION_IDLE_SECONDS*1e3&&(debug(`Session with id ${sessionId} is idle, so it will be closing ...`),session.transport.close().then(()=>{debug(`Session with id ${sessionId} was idle, so it has been closed`)}).catch(err=>{error(`Unable to delete idle session with id ${sessionId}`,err)}))},SESSION_IDLE_CHECK_SECONDS*1e3)}async function _logRequest(ctx){let reqClone=ctx.req.raw.clone();debug(`Got request: ${await reqClone.json()}`)}function _markSessionAsActive(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);session&&(session.lastActiveAt=Date.now())}}async function startStdioServer(){let transport=new StdioServerTransport;await _createAndConnectServer(transport,{config:_getConfig()})}var app=new Hono;async function startStreamableHTTPServer(port){app.use("*",cors({origin:"*",allowMethods:["GET","POST","OPTIONS"],allowHeaders:["Content-Type","Authorization","MCP-Protocol-Version"]})),app.get("/health",ctx=>ctx.json({status:"ok"})),app.get("/ping",ctx=>ctx.json({status:"ok",message:"pong"})),app.get("/mcp",ctx=>ctx.json({status:"ok",protocol:"model-context-protocol",version:"1.0"})),app.post("/mcp",async ctx=>{try{isDebugEnabled()&&await _logRequest(ctx);let transport=await _getOrCreateTransport(ctx);return transport?(_markSessionAsActive(ctx),await transport.handleRequest(ctx)):ctx.json(MCP_ERRORS.sessionNotFound,400)}catch(err){return error("Error occurred while handling MCP request",err),ctx.json(MCP_ERRORS.internalServerError,500)}}),app.delete("/mcp",async ctx=>{try{let transport=await _getTransport(ctx);return transport?(await transport.close(),ctx.json({ok:!0},200)):ctx.json(MCP_ERRORS.sessionNotFound,400)}catch(err){return error("Error occurred while deleting MCP session",err),ctx.json(MCP_ERRORS.internalServerError,500)}}),app.notFound(ctx=>ctx.json({error:"Not Found",status:404},404)),serve({fetch:app.fetch,port},()=>info(`Listening on port ${port}`)),_scheduleIdleSessionCheck()}import{Command,Option,InvalidOptionArgumentError}from"commander";function _parsePort(value){let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new InvalidOptionArgumentError("port must be an integer between 1 and 65535");return n}function _getOptions(){return new Command().addOption(new Option("--transport <type>","transport type").choices(["stdio","streamable-http"]).default("stdio")).addOption(new Option("--port <number>","port for Streamable HTTP transport").argParser(_parsePort).default(PORT)).allowUnknownOption().parse(process.argv).opts()}async function main(){let options=_getOptions();options.transport==="stdio"?(disable(),await startStdioServer()):options.transport==="streamable-http"?(info("Starting MCP server..."),await startStreamableHTTPServer(options.port),info("Started MCP Server")):(error(`Invalid transport: ${options.transport}`),process.exit(1))}main().catch(err=>{enable(),error("MCP server error",err),process.exit(1)});
4
+ `).trim()}function getServerPolicies(){return platformInfo.serverInfo.policies}import crypto from"node:crypto";import{StreamableHTTPTransport}from"@hono/mcp";import{serve}from"@hono/node-server";import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";import{Hono}from"hono";import{cors}from"hono/cors";var MCP_TEMPLATE={jsonrpc:"2.0",error:{code:0,message:"N/A"},id:null},MCP_ERRORS={get sessionNotFound(){return _buildMCPErrorResponse(-32001,"Session Not Found")},get unauthorized(){return _buildMCPErrorResponse(-32001,"Unauthorized")},get internalServerError(){return _buildMCPErrorResponse(-32603,"Internal Server Error")}},sessions=new Map;function _buildMCPErrorResponse(code,message){let result={...MCP_TEMPLATE};return result.error.code=code,result.error.message=message,result}function _getImage(response){if("image"in response&&response.image!==null&&typeof response.image=="object"&&"data"in response.image&&"mimeType"in response.image&&Buffer.isBuffer(response.image.data)&&typeof response.image.mimeType=="string"){let image=response.image;return delete response.image,image}}function _toResponse(response){let image=_getImage(response),contents=[];return contents.push({type:"text",text:JSON.stringify(response,null,2)}),image&&(image.mimeType==="image/svg+xml"?contents.push({type:"text",text:image.data.toString(),mimeType:image.mimeType}):contents.push({type:"image",data:image.data.toString("base64"),mimeType:image.mimeType})),{content:contents,structuredContent:response,isError:!1}}function _createServer(opts){let server=new McpServer({name:SERVER_NAME,version:SERVER_VERSION},{capabilities:{resources:{},tools:{}},instructions:getServerInstructions()}),messages=[],policies=getServerPolicies();if(policies)for(let policy of policies)messages.push({role:"user",content:{type:"text",text:policy}});server.registerPrompt("default_system",{title:"Default System Prompt",description:"General behavior for the AI assistant"},async()=>({description:"Defines the assistant's general reasoning and tool usage rules.",messages}));let toolExecutor=platformInfo.toolsInfo.createToolExecutor(()=>opts.sessionIdProvider?opts.sessionIdProvider():""),createToolCallback=tool=>async args=>{try{let response=await toolExecutor.executeTool(tool,args);return _toResponse(response)}catch(error2){return{content:[{type:"text",text:`Error: ${error2.message}`}],isError:!0}}},includeOutputSchema=!TOOL_OUTPUT_SCHEMA_DISABLE,allowedDomains=AVAILABLE_TOOL_DOMAINS;return platformInfo.toolsInfo.tools.forEach(t=>{let domain=t.name().split("_")[0]?.toLowerCase()??"";if(allowedDomains&&!allowedDomains.has(domain)){debug(`Skipping tool ${t.name()} (domain ${domain} not in AVAILABLE_TOOL_DOMAINS)`);return}debug(`Registering tool ${t.name()} ...`);let toolOptions=includeOutputSchema?{description:t.description(),inputSchema:t.inputSchema(),outputSchema:t.outputSchema()}:{description:t.description(),inputSchema:t.inputSchema()};server.registerTool(t.name(),toolOptions,createToolCallback(t))}),server}async function _createAndConnectServer(transport,opts){let server=_createServer({config:opts.config,sessionIdProvider:()=>transport.sessionId});return await server.connect(transport),server}function _getConfig(){return{}}function _createSession(ctx,transport,server){let session={transport,server,closed:!1,lastActiveAt:Date.now()},socket=ctx.env.incoming.socket;return socket._mcpRegistered||(socket._mcpRegistered=!0,socket.on("close",async()=>{debug(`Socket, which is for MCP session with id ${transport.sessionId}, has been closed`),SESSION_CLOSE_ON_SOCKET_CLOSE&&await transport.close()})),_registerMCPSessionClose(transport,session.server),debug(`Created MCP server session with id ${transport.sessionId}`),session}async function _createTransport(ctx){let serverConfig=_getConfig(),holder={},transport=new StreamableHTTPTransport({enableJsonResponse:!0,sessionIdGenerator:()=>crypto.randomUUID(),onsessioninitialized:async sessionId=>{let session=_createSession(ctx,transport,holder.server);sessions.set(sessionId,session),debug(`MCP session initialized with id ${sessionId}`)},onsessionclosed:async sessionId=>{debug(`Closing MCP session closed with id ${sessionId} ...`),await transport.close(),debug(`MCP session closed with id ${sessionId}`)}});return holder.server=await _createAndConnectServer(transport,{config:serverConfig}),transport}async function _getTransport(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);if(session)return debug(`Reusing MCP session with id ${sessionId}`),session.transport}}async function _getOrCreateTransport(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);if(session)return debug(`Reusing MCP session with id ${sessionId}`),session.transport;debug(`No MCP session could be found with id ${sessionId}`);return}return await _createTransport(ctx)}function _registerMCPSessionClose(transport,mcpServer){let closed=!1;transport.onclose=async()=>{if(debug(`Closing MCP session with id ${transport.sessionId} ...`),closed){debug(`MCP session with id ${transport.sessionId} has already been closed`);return}closed=!0;try{await mcpServer.close(),debug("Closed MCP server")}catch(err){error("Error occurred while closing MCP server",err)}if(transport.sessionId){let session=sessions.get(transport.sessionId);if(session&&(session.closed=!0,session.context))try{await session.context.close(),debug("Closed MCP session context")}catch(err){error("Error occurred while closing MCP session context",err)}sessions.delete(transport.sessionId)}debug(`Closing MCP session with id ${transport.sessionId} ...`)}}function _scheduleIdleSessionCheck(){setInterval(()=>{let currentTime=Date.now();for(let[sessionId,session]of sessions)debug(`Checking whether session with id ${sessionId} is idle or not ...`),currentTime-session.lastActiveAt>SESSION_IDLE_SECONDS*1e3&&(debug(`Session with id ${sessionId} is idle, so it will be closing ...`),session.transport.close().then(()=>{debug(`Session with id ${sessionId} was idle, so it has been closed`)}).catch(err=>{error(`Unable to delete idle session with id ${sessionId}`,err)}))},SESSION_IDLE_CHECK_SECONDS*1e3)}async function _logRequest(ctx){let reqClone=ctx.req.raw.clone();debug(`Got request: ${await reqClone.json()}`)}function _markSessionAsActive(ctx){let sessionId=ctx.req.header("mcp-session-id");if(sessionId){let session=sessions.get(sessionId);session&&(session.lastActiveAt=Date.now())}}async function startStdioServer(){let transport=new StdioServerTransport;await _createAndConnectServer(transport,{config:_getConfig()})}var app=new Hono;async function startStreamableHTTPServer(port){app.use("*",cors({origin:"*",allowMethods:["GET","POST","OPTIONS"],allowHeaders:["Content-Type","Authorization","MCP-Protocol-Version"]})),app.get("/health",ctx=>ctx.json({status:"ok"})),app.get("/ping",ctx=>ctx.json({status:"ok",message:"pong"})),app.get("/mcp",ctx=>ctx.json({status:"ok",protocol:"model-context-protocol",version:"1.0"})),app.post("/mcp",async ctx=>{try{isDebugEnabled()&&await _logRequest(ctx);let transport=await _getOrCreateTransport(ctx);return transport?(_markSessionAsActive(ctx),await transport.handleRequest(ctx)):ctx.json(MCP_ERRORS.sessionNotFound,400)}catch(err){return error("Error occurred while handling MCP request",err),ctx.json(MCP_ERRORS.internalServerError,500)}}),app.delete("/mcp",async ctx=>{try{let transport=await _getTransport(ctx);return transport?(await transport.close(),ctx.json({ok:!0},200)):ctx.json(MCP_ERRORS.sessionNotFound,400)}catch(err){return error("Error occurred while deleting MCP session",err),ctx.json(MCP_ERRORS.internalServerError,500)}}),app.notFound(ctx=>ctx.json({error:"Not Found",status:404},404)),serve({fetch:app.fetch,port},()=>info(`Listening on port ${port}`)),_scheduleIdleSessionCheck()}import{Command,Option,InvalidOptionArgumentError}from"commander";function _parsePort(value){let n=Number(value);if(!Number.isInteger(n)||n<1||n>65535)throw new InvalidOptionArgumentError("port must be an integer between 1 and 65535");return n}function _getOptions(){return new Command().addOption(new Option("--transport <type>","transport type").choices(["stdio","streamable-http"]).default("stdio")).addOption(new Option("--port <number>","port for Streamable HTTP transport").argParser(_parsePort).default(PORT)).allowUnknownOption().parse(process.argv).opts()}async function main(){let options=_getOptions();options.transport==="stdio"?(disable(),await startStdioServer()):options.transport==="streamable-http"?(info("Starting MCP server..."),await startStreamableHTTPServer(options.port),info("Started MCP Server")):(error(`Invalid transport: ${options.transport}`),process.exit(1))}main().catch(err=>{enable(),error("MCP server error",err),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-devtools-mcp",
3
- "version": "0.2.24",
3
+ "version": "0.2.26",
4
4
  "description": "MCP Server for Browser Dev Tools",
5
5
  "private": false,
6
6
  "type": "module",