browser-devtools-mcp 0.2.12 → 0.2.14
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/dist/cli/runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{platformInfo}from"../core-CP3EHLBX.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-SWCBNNLF.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-35VQPOBO.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-SWCBNNLF.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(`
|
|
@@ -266,10 +266,10 @@ By default, all <script> tags are removed from the output unless "removeScripts"
|
|
|
266
266
|
<!-- 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+`
|
|
267
267
|
`)}return text.trim()},{selector});return output.length>maxLength&&(output=output.slice(0,maxLength)+`
|
|
268
268
|
[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}}};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 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.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
Do NOT set includeBase64 to true unless the assistant cannot read the file from the returned path (e.g. remote server, container). When in doubt, omit includeBase64 or set it to false.`}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)}}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.')}}_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,options={path:filePath,type:screenshotType,fullPage:!!args.fullPage,quality};if(args.selector){let element=await context.page.$(args.selector);if(!element)throw new Error(`Element not found: ${args.selector}`);options.element=element}let screenshot=await context.page.screenshot(options),result={filePath};return args.includeBase64&&(result.image={data:this._scaleImageToFitMessage(screenshot,screenshotType),mimeType:`image/${screenshotType}`}),result}};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 _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,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||[],hitProbe,hitConditionMet=!0,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))){hitProbe=probe,probe.hitCondition&&(hitConditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1));break}}let shouldCaptureBreakpoint=hitProbe!==void 0&&hitConditionMet,shouldCaptureException=isException&&store.exceptionBreakpoint!=="none",shouldCaptureDOMBreakpoint=hitDOMBreakpoint!==void 0;if(hitProbe&&(hitProbe.hitCount++,hitProbe.lastHitAt=Date.now()),hitDOMBreakpoint&&(hitDOMBreakpoint.hitCount++,hitDOMBreakpoint.lastHitAt=Date.now()),(shouldCaptureBreakpoint||shouldCaptureException||shouldCaptureDOMBreakpoint)&&event.callFrames.length>0){let topFrame=event.callFrames[0],logResult;if(hitProbe&&hitProbe.kind==="logpoint"&&hitProbe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,hitProbe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}let 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,resolved=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.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});let probeId=hitProbe?.id??hitDOMBreakpoint?.id??"__exception__",isLogpoint=hitProbe?.kind==="logpoint",callStack=[];if(!isLogpoint){let 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,snapshotFrame=await _callFrameToSnapshot(store.v8Api,frame,captureScopes,store.sourceMapResolver);callStack.push(snapshotFrame)}}let asyncStackTrace;isLogpoint||(asyncStackTrace=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment));let watchResults;!isLogpoint&&store.watchExpressions.size>0&&(watchResults=await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions));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,domChange:domChangeInfo,callStack,asyncStackTrace,logResult,watchResults,captureTimeMs:Date.now()-startTime};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(),fullCondition;options.condition?fullCondition=`(${options.condition})`:fullCondition="true";let line0based=options.lineNumber-1,column0based=(options.columnNumber??1)-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1;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}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;for(let v8Id of probe.v8BreakpointIds)try{await store.v8Api.removeBreakpoint(v8Id)}catch{}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`
|
|
269
|
+
|
|
270
|
+
**Do NOT use this tool to understand page structure.** Use a11y_take-aria-snapshot first, then a11y_take-ax-tree-snapshot for layout/occlusion. 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.
|
|
271
|
+
|
|
272
|
+
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)}}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.')}}_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,options={path:filePath,type:screenshotType,fullPage:!!args.fullPage,quality};if(args.selector){let element=await context.page.$(args.selector);if(!element)throw new Error(`Element not found: ${args.selector}`);options.element=element}let screenshot=await context.page.screenshot(options),result={filePath};return args.includeBase64&&(result.image={data:this._scaleImageToFitMessage(screenshot,screenshotType),mimeType:`image/${screenshotType}`}),result}};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 _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,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||[],hitProbe,hitConditionMet=!0,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))){hitProbe=probe,probe.hitCondition&&(hitConditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1));break}}let shouldCaptureBreakpoint=hitProbe!==void 0&&hitConditionMet,shouldCaptureException=isException&&store.exceptionBreakpoint!=="none",shouldCaptureDOMBreakpoint=hitDOMBreakpoint!==void 0;if(hitProbe&&(hitProbe.hitCount++,hitProbe.lastHitAt=Date.now()),hitDOMBreakpoint&&(hitDOMBreakpoint.hitCount++,hitDOMBreakpoint.lastHitAt=Date.now()),(shouldCaptureBreakpoint||shouldCaptureException||shouldCaptureDOMBreakpoint)&&event.callFrames.length>0){let topFrame=event.callFrames[0],logResult;if(hitProbe&&hitProbe.kind==="logpoint"&&hitProbe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,hitProbe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}let 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,resolved=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.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});let probeId=hitProbe?.id??hitDOMBreakpoint?.id??"__exception__",isLogpoint=hitProbe?.kind==="logpoint",callStack=[];if(!isLogpoint){let 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,snapshotFrame=await _callFrameToSnapshot(store.v8Api,frame,captureScopes,store.sourceMapResolver);callStack.push(snapshotFrame)}}let asyncStackTrace;isLogpoint||(asyncStackTrace=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment));let watchResults;!isLogpoint&&store.watchExpressions.size>0&&(watchResults=await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions));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,domChange:domChangeInfo,callStack,asyncStackTrace,logResult,watchResults,captureTimeMs:Date.now()-startTime};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(),fullCondition;options.condition?fullCondition=`(${options.condition})`:fullCondition="true";let line0based=options.lineNumber-1,column0based=(options.columnNumber??1)-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1;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}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;for(let v8Id of probe.v8BreakpointIds)try{await store.v8Api.removeBreakpoint(v8Id)}catch{}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`
|
|
273
273
|
Returns the current debugging status including:
|
|
274
274
|
- Whether debugging is enabled
|
|
275
275
|
- Source map status
|
|
@@ -782,6 +782,31 @@ enabling deep, bidirectional debugging and interaction with live web pages.
|
|
|
782
782
|
It supports both visual understanding and code-level inspection of browser state,
|
|
783
783
|
similar to existing Playwright and Chrome DevTools\u2013based MCP servers, with a focus on AI-driven exploration, diagnosis, and action.
|
|
784
784
|
|
|
785
|
+
**Important rules for AI agents:**
|
|
786
|
+
|
|
787
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
788
|
+
*** CRITICAL \u2014 TO UNDERSTAND PAGE STRUCTURE (do not default to screenshot) ***
|
|
789
|
+
Use ARIA snapshot first, then AX tree if needed. Use screenshot only when necessary.
|
|
790
|
+
|
|
791
|
+
DO NOT take a screenshot just to "see the page" or understand what is on it.
|
|
792
|
+
1. Call "a11y_take-aria-snapshot" FIRST for structure, roles, and semantics.
|
|
793
|
+
2. If you need layout, bounding boxes, or occlusion: call "a11y_take-ax-tree-snapshot".
|
|
794
|
+
3. Call "content_take-screenshot" ONLY when you need to verify how something looks visually (e.g. design check, visual bug, or when ARIA/AX are insufficient). Do not use screenshot to understand page structure.
|
|
795
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
796
|
+
|
|
797
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
798
|
+
*** CRITICAL \u2014 BEFORE ANY ELEMENT INTERACTION ***
|
|
799
|
+
(click | fill | hover | select | drag)
|
|
800
|
+
|
|
801
|
+
DO NOT GUESS SELECTORS FROM SCREENSHOTS.
|
|
802
|
+
|
|
803
|
+
Take a snapshot first to get real selectors:
|
|
804
|
+
- Call "a11y_take-aria-snapshot", "a11y_take-ax-tree-snapshot", or "content_get-as-html" (e.g. selector "form") to see actual ids, roles, and markup.
|
|
805
|
+
- Then use the returned selectors for interaction. Guessing (e.g. input[type="email"]) often fails because real markup may use type="text", wrappers, or different attributes.
|
|
806
|
+
|
|
807
|
+
SNAPSHOT FIRST, THEN INTERACT.
|
|
808
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
809
|
+
|
|
785
810
|
Core capabilities include:
|
|
786
811
|
|
|
787
812
|
**Content & Visual Inspection:**
|
|
@@ -852,12 +877,10 @@ Core capabilities include:
|
|
|
852
877
|
|
|
853
878
|
UI debugging guidance for AI agents:
|
|
854
879
|
|
|
855
|
-
*** INTERACTION: SNAPSHOT BEFORE ACTING (do not guess selectors) ***
|
|
856
|
-
- Before click, fill, hover, or any element interaction, obtain reliable selectors from the page. Use "a11y_take-aria-snapshot", "a11y_take-ax-tree-snapshot", or "content_get-as-html" (e.g. selector "form" for forms) to see actual ids, roles, and markup. Do not infer selectors from screenshots alone (e.g. assuming input[type="email"] or input[type="password"])\u2014real markup may use type="text", wrappers, or different attributes, which causes failed interactions. Snapshot first, then interact.
|
|
857
880
|
*** INSPECTION PREFERENCE (follow this order) ***
|
|
858
|
-
1.
|
|
881
|
+
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.
|
|
859
882
|
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.
|
|
860
|
-
3. Use "content_take-screenshot" ONLY when you
|
|
883
|
+
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.
|
|
861
884
|
*** SCREENSHOT TOOL: includeBase64 ***
|
|
862
885
|
- 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.
|
|
863
886
|
|
|
@@ -910,31 +933,34 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
|
|
|
910
933
|
1. **Synchronization**: If the page loads content asynchronously, call "sync_wait-for-network-idle" first
|
|
911
934
|
to ensure the page is stable before inspection.
|
|
912
935
|
|
|
913
|
-
2. **
|
|
914
|
-
-
|
|
915
|
-
- If you need layout/occlusion/visibility:
|
|
916
|
-
- Call "content_take-screenshot" ONLY when you
|
|
917
|
-
|
|
936
|
+
2. **To understand page structure (mandatory order\u2014do not default to screenshot):**
|
|
937
|
+
- Call "a11y_take-aria-snapshot" FIRST for structure, roles, and semantics.
|
|
938
|
+
- If you need layout/occlusion/visibility: call "a11y_take-ax-tree-snapshot" (with "checkOcclusion:true" when relevant).
|
|
939
|
+
- Call "content_take-screenshot" ONLY when you need to verify how something looks visually (e.g. design check, visual bug) or when ARIA/AX are insufficient. Do NOT use screenshot to "see the page" or understand structure.
|
|
940
|
+
|
|
941
|
+
3. **Before any element interaction** (click, fill, hover, select, drag): Take a snapshot first to get real selectors. Call "a11y_take-aria-snapshot", "a11y_take-ax-tree-snapshot", or "content_get-as-html" (e.g. selector "form") to see actual ids, roles, and markup. Do NOT guess selectors from screenshots\u2014real markup often differs (e.g. type="text" instead of type="email"). Snapshot first, then interact.
|
|
942
|
+
|
|
943
|
+
4. **Screenshot usage**: When you do take a screenshot, do NOT set "includeBase64" to true unless the assistant cannot read the returned file path (e.g. remote/container). The image is always saved to disk.
|
|
918
944
|
|
|
919
|
-
|
|
945
|
+
5. **Accessibility Tree Analysis**: Call "a11y_take-ax-tree-snapshot" tool with "checkOcclusion:true"
|
|
920
946
|
- Provides precise bounding boxes, runtime visual data, and occlusion detection
|
|
921
947
|
- Best for detecting overlaps and measuring exact positions
|
|
922
948
|
- Use "onlyVisible:true" or "onlyInViewport:true" to filter results
|
|
923
949
|
- Set "includeStyles:true" to analyze computed CSS properties
|
|
924
950
|
|
|
925
|
-
|
|
951
|
+
6. **ARIA Snapshot**: Call "a11y_take-aria-snapshot" tool (full page or specific selector)
|
|
926
952
|
- Provides semantic structure and accessibility roles
|
|
927
953
|
- Best for understanding page hierarchy and accessibility issues
|
|
928
954
|
- Use in combination with AX tree snapshot for comprehensive analysis
|
|
929
955
|
|
|
930
|
-
|
|
956
|
+
7. **Design Comparison** (if Figma design is available): Call "figma_compare-page-with-design" tool
|
|
931
957
|
- Compares live page UI against Figma design snapshot
|
|
932
958
|
- Returns combined similarity score using multiple signals (MSSIM, image embedding, text embedding)
|
|
933
959
|
- Use "semantic" mode for real data vs design data comparisons
|
|
934
960
|
- Use "raw" mode only when expecting pixel-identical output
|
|
935
961
|
- Notes explain which signals were used or skipped
|
|
936
962
|
|
|
937
|
-
|
|
963
|
+
8. **React Component Inspection** (if page uses React): Use React tools to understand component structure
|
|
938
964
|
- Call "react_get-component-for-element" with selector or (x,y) to find React component for a DOM element
|
|
939
965
|
- Call "react_get-element-for-component" to find DOM elements rendered by a React component
|
|
940
966
|
- **Important:** These tools require persistent browser context (BROWSER_PERSISTENT_ENABLE=true)
|
|
@@ -942,11 +968,11 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
|
|
|
942
968
|
- Without extension, tools use best-effort DOM scanning (less reliable)
|
|
943
969
|
- Component names and debug source info are best-effort and may vary by build (dev/prod)
|
|
944
970
|
|
|
945
|
-
|
|
971
|
+
9. **Performance Check** (optional but recommended): Call "o11y_get-web-vitals" to assess page performance
|
|
946
972
|
- Identifies performance issues that may affect user experience
|
|
947
973
|
- Provides actionable recommendations based on Google's thresholds
|
|
948
974
|
|
|
949
|
-
|
|
975
|
+
10. **Console & Network Inspection**: Check for errors and failed requests
|
|
950
976
|
- Call "o11y_get-console-messages" with "type:ERROR" to find JavaScript errors
|
|
951
977
|
- Call "o11y_get-http-requests" with "ok:false" to find failed network requests
|
|
952
978
|
- If network issues are suspected or testing error scenarios, use stub tools:
|
|
@@ -954,19 +980,19 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
|
|
|
954
980
|
- Use "stub_intercept-http-request" to modify requests (e.g., inject headers) to test different scenarios
|
|
955
981
|
- Use "stub_list" to verify active stubs and "stub_clear" to remove them after testing
|
|
956
982
|
|
|
957
|
-
|
|
983
|
+
11. **Manual Verification**: Calculate bounding box overlaps:
|
|
958
984
|
- Horizontal: (element1.x + element1.width) \u2264 element2.x
|
|
959
985
|
- Vertical: (element1.y + element1.height) \u2264 element2.y
|
|
960
986
|
|
|
961
|
-
|
|
987
|
+
12. **Report ALL findings**: aesthetic issues, overlaps, spacing problems, alignment issues,
|
|
962
988
|
accessibility problems, semantic structure issues, design parity issues (if compared with Figma),
|
|
963
989
|
React component structure issues (if inspected), performance problems, console errors, failed requests
|
|
964
990
|
|
|
965
|
-
|
|
991
|
+
13. **JavaScript Execution** (when needed for advanced debugging):
|
|
966
992
|
- Use "run_js-in-browser" to inspect or mutate DOM state, read client-side variables, or extract computed values directly from the page
|
|
967
993
|
- Use "run_js-in-sandbox" for server-side automation logic that needs access to Playwright Page object or safe built-ins
|
|
968
994
|
|
|
969
|
-
|
|
995
|
+
14. **Non-Blocking JavaScript Debugging** (for deep code investigation):
|
|
970
996
|
- Use "debug_put-tracepoint" to set breakpoints that capture call stack and local variables without pausing
|
|
971
997
|
- Use "debug_put-logpoint" for lightweight logging at specific code locations
|
|
972
998
|
- Use "debug_put-exceptionpoint" with state "uncaught" or "all" to capture exception snapshots
|
|
@@ -979,8 +1005,9 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
|
|
|
979
1005
|
- Use "debug_status" to check current debugging state and probe counts
|
|
980
1006
|
|
|
981
1007
|
**Tool Usage Notes:**
|
|
982
|
-
- ARIA snapshot first
|
|
983
|
-
-
|
|
1008
|
+
- To understand the page: ARIA snapshot first, then AX tree; do NOT use screenshot for structure\u2014use screenshot only for visual verification when needed.
|
|
1009
|
+
- Before any click/fill/hover/select/drag: take a snapshot first to get selectors; do not guess from screenshots.
|
|
1010
|
+
- Screenshot only when you need to verify how the UI looks (e.g. visual bug, design check) or when ARIA/AX are insufficient.
|
|
984
1011
|
- Do not set includeBase64 on screenshot unless the assistant cannot read the file path.
|
|
985
1012
|
- AX tree: Technical measurements, occlusion, precise positioning, visual diagnostics
|
|
986
1013
|
- ARIA snapshot: Semantic understanding, accessibility structure, role hierarchy
|
package/dist/daemon-server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{platformInfo}from"./core-
|
|
1
|
+
import{platformInfo}from"./core-35VQPOBO.js";import{DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,debug,enable,error,info,isDebugEnabled}from"./core-SWCBNNLF.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};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{platformInfo}from"./core-
|
|
2
|
+
import{platformInfo}from"./core-35VQPOBO.js";import{PORT,SESSION_CLOSE_ON_SOCKET_CLOSE,SESSION_IDLE_CHECK_SECONDS,SESSION_IDLE_SECONDS,debug,disable,enable,error,info,isDebugEnabled}from"./core-SWCBNNLF.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
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)});
|