browser-devtools-mcp 0.2.11 → 0.2.13

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{platformInfo}from"../core-S6EFRNAR.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-W6FDJH5U.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(`
@@ -852,6 +852,19 @@ Core capabilities include:
852
852
 
853
853
  UI debugging guidance for AI agents:
854
854
 
855
+ \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
856
+ *** CRITICAL \u2014 BEFORE ANY ELEMENT INTERACTION ***
857
+ (click | fill | hover | select | drag)
858
+
859
+ DO NOT GUESS SELECTORS FROM SCREENSHOTS.
860
+
861
+ Take a snapshot first to get real selectors:
862
+ - 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.
863
+ - 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.
864
+
865
+ SNAPSHOT FIRST, THEN INTERACT.
866
+ \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
867
+
855
868
  *** INSPECTION PREFERENCE (follow this order) ***
856
869
  1. Prefer "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.
857
870
  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.
@@ -908,31 +921,33 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
908
921
  1. **Synchronization**: If the page loads content asynchronously, call "sync_wait-for-network-idle" first
909
922
  to ensure the page is stable before inspection.
910
923
 
911
- 2. **Inspection order (prefer snapshots over screenshots)**:
924
+ 2. **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.
925
+
926
+ 3. **Inspection order (prefer snapshots over screenshots)**:
912
927
  - First try "a11y_take-aria-snapshot" for structure, roles, and semantics.
913
928
  - If you need layout/occlusion/visibility: use "a11y_take-ax-tree-snapshot" (with "checkOcclusion:true" when interactions fail).
914
929
  - Call "content_take-screenshot" ONLY when you truly need to see the visual appearance of the UI (e.g. design check, pixel verification). Do not take screenshots just to see the page\u2014use ARIA or AX snapshot instead.
915
930
  - When taking 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.
916
931
 
917
- 3. **Accessibility Tree Analysis**: Call "a11y_take-ax-tree-snapshot" tool with "checkOcclusion:true"
932
+ 4. **Accessibility Tree Analysis**: Call "a11y_take-ax-tree-snapshot" tool with "checkOcclusion:true"
918
933
  - Provides precise bounding boxes, runtime visual data, and occlusion detection
919
934
  - Best for detecting overlaps and measuring exact positions
920
935
  - Use "onlyVisible:true" or "onlyInViewport:true" to filter results
921
936
  - Set "includeStyles:true" to analyze computed CSS properties
922
937
 
923
- 4. **ARIA Snapshot**: Call "a11y_take-aria-snapshot" tool (full page or specific selector)
938
+ 5. **ARIA Snapshot**: Call "a11y_take-aria-snapshot" tool (full page or specific selector)
924
939
  - Provides semantic structure and accessibility roles
925
940
  - Best for understanding page hierarchy and accessibility issues
926
941
  - Use in combination with AX tree snapshot for comprehensive analysis
927
942
 
928
- 5. **Design Comparison** (if Figma design is available): Call "figma_compare-page-with-design" tool
943
+ 6. **Design Comparison** (if Figma design is available): Call "figma_compare-page-with-design" tool
929
944
  - Compares live page UI against Figma design snapshot
930
945
  - Returns combined similarity score using multiple signals (MSSIM, image embedding, text embedding)
931
946
  - Use "semantic" mode for real data vs design data comparisons
932
947
  - Use "raw" mode only when expecting pixel-identical output
933
948
  - Notes explain which signals were used or skipped
934
949
 
935
- 6. **React Component Inspection** (if page uses React): Use React tools to understand component structure
950
+ 7. **React Component Inspection** (if page uses React): Use React tools to understand component structure
936
951
  - Call "react_get-component-for-element" with selector or (x,y) to find React component for a DOM element
937
952
  - Call "react_get-element-for-component" to find DOM elements rendered by a React component
938
953
  - **Important:** These tools require persistent browser context (BROWSER_PERSISTENT_ENABLE=true)
@@ -940,11 +955,11 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
940
955
  - Without extension, tools use best-effort DOM scanning (less reliable)
941
956
  - Component names and debug source info are best-effort and may vary by build (dev/prod)
942
957
 
943
- 7. **Performance Check** (optional but recommended): Call "o11y_get-web-vitals" to assess page performance
958
+ 8. **Performance Check** (optional but recommended): Call "o11y_get-web-vitals" to assess page performance
944
959
  - Identifies performance issues that may affect user experience
945
960
  - Provides actionable recommendations based on Google's thresholds
946
961
 
947
- 8. **Console & Network Inspection**: Check for errors and failed requests
962
+ 9. **Console & Network Inspection**: Check for errors and failed requests
948
963
  - Call "o11y_get-console-messages" with "type:ERROR" to find JavaScript errors
949
964
  - Call "o11y_get-http-requests" with "ok:false" to find failed network requests
950
965
  - If network issues are suspected or testing error scenarios, use stub tools:
@@ -952,19 +967,19 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
952
967
  - Use "stub_intercept-http-request" to modify requests (e.g., inject headers) to test different scenarios
953
968
  - Use "stub_list" to verify active stubs and "stub_clear" to remove them after testing
954
969
 
955
- 9. **Manual Verification**: Calculate bounding box overlaps:
970
+ 10. **Manual Verification**: Calculate bounding box overlaps:
956
971
  - Horizontal: (element1.x + element1.width) \u2264 element2.x
957
972
  - Vertical: (element1.y + element1.height) \u2264 element2.y
958
973
 
959
- 10. **Report ALL findings**: aesthetic issues, overlaps, spacing problems, alignment issues,
974
+ 11. **Report ALL findings**: aesthetic issues, overlaps, spacing problems, alignment issues,
960
975
  accessibility problems, semantic structure issues, design parity issues (if compared with Figma),
961
976
  React component structure issues (if inspected), performance problems, console errors, failed requests
962
977
 
963
- 11. **JavaScript Execution** (when needed for advanced debugging):
978
+ 12. **JavaScript Execution** (when needed for advanced debugging):
964
979
  - Use "run_js-in-browser" to inspect or mutate DOM state, read client-side variables, or extract computed values directly from the page
965
980
  - Use "run_js-in-sandbox" for server-side automation logic that needs access to Playwright Page object or safe built-ins
966
981
 
967
- 12. **Non-Blocking JavaScript Debugging** (for deep code investigation):
982
+ 13. **Non-Blocking JavaScript Debugging** (for deep code investigation):
968
983
  - Use "debug_put-tracepoint" to set breakpoints that capture call stack and local variables without pausing
969
984
  - Use "debug_put-logpoint" for lightweight logging at specific code locations
970
985
  - Use "debug_put-exceptionpoint" with state "uncaught" or "all" to capture exception snapshots
@@ -977,6 +992,7 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
977
992
  - Use "debug_status" to check current debugging state and probe counts
978
993
 
979
994
  **Tool Usage Notes:**
995
+ - Before any click/fill/hover/select/drag: take a snapshot first to get selectors; do not guess from screenshots.
980
996
  - ARIA snapshot first for structure/roles/semantics; then AX tree for layout/occlusion/positioning.
981
997
  - Screenshot only when you must see the actual pixels (e.g. visual design); otherwise use snapshots.
982
998
  - Do not set includeBase64 on screenshot unless the assistant cannot read the file path.
@@ -1 +1 @@
1
- import{platformInfo}from"./core-S6EFRNAR.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};
1
+ import{platformInfo}from"./core-W6FDJH5U.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-S6EFRNAR.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(`
2
+ import{platformInfo}from"./core-W6FDJH5U.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)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-devtools-mcp",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "MCP Server for Browser Dev Tools",
5
5
  "private": false,
6
6
  "type": "module",