browser-devtools-mcp 0.3.3 → 0.3.4

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,4 +1,4 @@
1
- import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_EXECUTABLE_PATH,BROWSER_HEADLESS_ENABLE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,BROWSER_LOCALE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_USE_INSTALLED_ON_SYSTEM,ConsoleMessageLevel,ConsoleMessageLevelName,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,OTEL_ASSETS_DIR,OTEL_ENABLE,OTEL_EXPORTER_HTTP_HEADERS,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,PLATFORM,SourceMapResolver,V8Api,addWatchExpression,clearWatchExpressions,createProbe,debug,detachDebugging,enableDebugging,evaluateInNode,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-PF26XSPV.js";import fs from"node:fs";import{chromium,firefox,webkit}from"playwright";var DEFAULT_BROWSER_TYPE="chromium",browsers=new Map,persistenceBrowserContexts=new Map;function _browserKey(browserOptions){return JSON.stringify(browserOptions)}function _browserLaunchOptions(browserOptions){let launchOptions={headless:browserOptions.headless,executablePath:browserOptions.executablePath,handleSIGINT:!1,handleSIGTERM:!1};if(browserOptions.useInstalledOnSystem)if(browserOptions.browserType==="chromium")launchOptions.channel="chrome",launchOptions.args=["--disable-blink-features=AutomationControlled"],launchOptions.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${browserOptions.browserType} is not supported to be used from the one installed on the system`);return launchOptions}async function _createBrowser(browserOptions){let browserInstance;switch(browserOptions.browserType){case"firefox":browserInstance=firefox;break;case"webkit":browserInstance=webkit;break;default:browserInstance=chromium;break}return browserInstance.launch(_browserLaunchOptions(browserOptions))}async function _getBrowser(browserOptions){let browserKey=_browserKey(browserOptions),browserInstance=browsers.get(browserKey);if(browserInstance&&!browserInstance.isConnected()){try{await browserInstance.close().catch(()=>{})}catch{}browserInstance=void 0}return browserInstance||(browserInstance=await _createBrowser(browserOptions),browsers.set(browserKey,browserInstance)),browserInstance}function _persistentBrowserContextKey(browserContextOptions){return browserContextOptions.persistent.userDataDir}function _persistentBrowserContextLaunchOptions(browserContextOptions){let browserOptions=browserContextOptions.browserOptions,launchOptions={headless:browserOptions.headless,executablePath:browserOptions.executablePath,bypassCSP:!0,viewport:browserOptions.headless?void 0:null,locale:browserContextOptions.locale};if(browserOptions.useInstalledOnSystem)if(browserOptions.browserType==="chromium")launchOptions.channel="chrome",launchOptions.args=["--disable-blink-features=AutomationControlled"],launchOptions.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${browserOptions.browserType} is not supported to be used from the one installed on the system`);return launchOptions}async function _createPersistentBrowserContext(browserContextOptions){let browserInstance;switch(browserContextOptions.browserOptions.browserType){case"firefox":browserInstance=firefox;break;case"webkit":browserInstance=webkit;break;default:browserInstance=chromium;break}let userDataDir=browserContextOptions.persistent.userDataDir;fs.mkdirSync(userDataDir,{recursive:!0});let browserContext=await browserInstance.launchPersistentContext(userDataDir,_persistentBrowserContextLaunchOptions(browserContextOptions));for(let p of browserContext.pages())try{await p.close()}catch{}return browserContext}async function _getPersistentBrowserContext(browserContextOptions){let persistentBrowserContextKey=_persistentBrowserContextKey(browserContextOptions),browserContext=persistenceBrowserContexts.get(persistentBrowserContextKey);if(browserContext&&!browserContext.browser()?.isConnected()){try{await browserContext.close().catch(()=>{})}catch{}browserContext=void 0}if(!browserContext)browserContext=await _createPersistentBrowserContext(browserContextOptions),persistenceBrowserContexts.set(persistentBrowserContextKey,browserContext);else throw new Error(`There is already active persistent browser context in the user data directory: ${browserContextOptions.persistent?.userDataDir}`);return browserContext}async function newBrowserContext(browserContextOptions={browserOptions:{browserType:DEFAULT_BROWSER_TYPE,headless:BROWSER_HEADLESS_ENABLE,executablePath:BROWSER_EXECUTABLE_PATH,useInstalledOnSystem:BROWSER_USE_INSTALLED_ON_SYSTEM},persistent:BROWSER_PERSISTENT_ENABLE?{userDataDir:BROWSER_PERSISTENT_USER_DATA_DIR}:void 0,locale:BROWSER_LOCALE}){return browserContextOptions.persistent?{browserContext:await _getPersistentBrowserContext(browserContextOptions)}:{browserContext:await(await _getBrowser(browserContextOptions.browserOptions)).newContext({viewport:browserContextOptions.browserOptions.headless?void 0:null,bypassCSP:!0,locale:browserContextOptions.locale})}}async function newPage(browserContext,pageOptions={}){return await browserContext.newPage()}async function closeBrowserContext(browserContext){await browserContext.close();let deleted=!1;for(let[key,val]of persistenceBrowserContexts.entries())browserContext===val&&(persistenceBrowserContexts.delete(key),deleted=!0);return deleted}function _normalizeBasePath(input){let p=input.trim();return p.startsWith("/")||(p="/"+p),p.endsWith("/")||(p=p+"/"),p}function _normalizeUpstreamBaseUrl(input){let u=input.trim();return u&&(u.endsWith("/")?u.slice(0,-1):u)}function _computeSuffixPath(fullUrl,basePath){try{let pathname=new URL(fullUrl).pathname;if(!pathname.startsWith(basePath))return null;let raw=pathname.slice(basePath.length);return raw?raw.startsWith("/")?raw:"/"+raw:null}catch{return null}}function _appendSuffixToUpstream(upstreamBaseUrl,suffixPath,originalUrl){try{let qs=new URL(originalUrl).search??"";return upstreamBaseUrl+suffixPath+qs}catch{return upstreamBaseUrl+suffixPath}}var OTELProxy=class{config;queue;workers;isRunning;isInstalled;metrics;constructor(config){let maxQueueSize=config.maxQueueSize??200,concurrency=config.concurrency??2,respondNoContent=config.respondNoContent??!0,normalizedLocalPath=_normalizeBasePath(config.localPath),normalizedUpstreamUrl=_normalizeUpstreamBaseUrl(config.upstreamUrl);this.config={...config,localPath:normalizedLocalPath,upstreamUrl:normalizedUpstreamUrl,maxQueueSize,concurrency,respondNoContent},this.queue=[],this.workers=[],this.isRunning=!1,this.isInstalled=!1,this.metrics={routedRequests:0,acceptedBatches:0,droppedBatches:0,forwardedBatches:0,failedBatches:0,inFlight:0,queueSize:0,lastError:null}}getMetrics(){return{...this.metrics,queueSize:this.queue.length}}async install(context){if(this.isInstalled)return;let basePath=this.config.localPath;if(!basePath.startsWith("/"))throw new Error('localPath must start with "/" (e.g. "/__mcp_otel/").');let pattern=`**${basePath}**`;await context.route(pattern,async route=>{await this._handleRoute(route)}),this.isInstalled=!0,this.isRunning||await this.start(),debug(`[otel-proxy] installed route pattern: ${pattern} (basePath=${basePath}, upstreamBase=${this.config.upstreamUrl})`)}async uninstall(context){if(!this.isInstalled)return;let pattern=`**${this.config.localPath}**`;try{await context.unroute(pattern)}catch{}this.isInstalled=!1,await this.stop()}async start(){if(this.isRunning)return;this.isRunning=!0;let workerCount=Math.max(1,this.config.concurrency);for(let i=0;i<workerCount;i++){let w=this._workerLoop(i);this.workers.push(w)}debug(`[otel-proxy] started with concurrency=${workerCount}, maxQueueSize=${this.config.maxQueueSize}`)}async stop(){if(this.isRunning){this.isRunning=!1,this.queue.length=0;try{await Promise.allSettled(this.workers)}finally{this.workers.length=0}debug("[otel-proxy] stopped")}}async _handleRoute(route){let req=route.request();if(this.metrics.routedRequests++,req.method().toUpperCase()==="OPTIONS"){await this._fulfillFast(route);return}if(this.config.shouldForward&&!this.config.shouldForward(req)){await this._fulfillFast(route);return}let requestUrl=req.url(),basePath=this.config.localPath,suffixPath=_computeSuffixPath(requestUrl,basePath);if(!suffixPath){await route.fallback();return}let upstreamFullUrl=_appendSuffixToUpstream(this.config.upstreamUrl,suffixPath,requestUrl),body=await req.postDataBuffer()??Buffer.alloc(0),contentType=req.headers()["content-type"]??"application/x-protobuf",method=req.method(),headers={"content-type":contentType};if(this.config.upstreamHeaders)for(let[k,v]of Object.entries(this.config.upstreamHeaders))headers[k]=v;if(this.queue.length>=this.config.maxQueueSize){this.metrics.droppedBatches++,await this._fulfillFast(route),warn(`[otel-proxy] dropped batch (queue full: ${this.queue.length}/${this.config.maxQueueSize}) suffix=${suffixPath}`);return}let item={body,contentType,createdAtMs:Date.now(),upstreamUrl:upstreamFullUrl,method,headers};this.queue.push(item),this.metrics.acceptedBatches++,await this._fulfillFast(route)}async _fulfillFast(route){let status=this.config.respondNoContent?204:200;if(status===204){await route.fulfill({status});return}await route.fulfill({status,headers:{"content-type":"text/plain; charset=utf-8"},body:""})}async _workerLoop(workerIndex){for(;this.isRunning;){let item=this.queue.shift();if(!item){await this._sleep(25);continue}this.metrics.inFlight++;try{await this._forwardUpstream(item),this.metrics.forwardedBatches++}catch(e){this.metrics.failedBatches++;let msg=e instanceof Error?e.message:String(e);this.metrics.lastError=msg,warn(`[otel-proxy] worker=${workerIndex} forward failed: ${msg}`)}finally{this.metrics.inFlight--}}}async _forwardUpstream(item){let res=await fetch(item.upstreamUrl,{method:item.method,headers:item.headers,body:new Uint8Array(item.body)});if(res.status<200||res.status>=300){let text=await this._safeReadText(res);throw new Error(`upstream returned ${res.status} for ${item.upstreamUrl}: ${text}`)}}async _safeReadText(res){try{return(await res.text()).slice(0,500)}catch{return""}}async _sleep(ms){await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}};import*as fs2 from"node:fs";import*as path from"node:path";import{fileURLToPath}from"node:url";var __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),OTEL_PROXY_LOCAL_PATH="/__mcp_otel/",OTEL_BUNDLE_FILE_NAME="otel-initializer.bundle.js";function _getOtelAssetsDir(){return OTEL_ASSETS_DIR?OTEL_ASSETS_DIR:path.join(__dirname,"platform","browser","otel")}function _getOTELExporterConfig(){if(OTEL_EXPORTER_TYPE==="otlp/http"||OTEL_EXPORTER_HTTP_URL){if(!OTEL_EXPORTER_HTTP_URL)throw new Error('OTEL exporter HTTP url must be set when OTEL exporter type is "otlp/http"');return{type:"otlp/http",url:OTEL_PROXY_LOCAL_PATH,upstreamURL:OTEL_EXPORTER_HTTP_URL,headers:OTEL_EXPORTER_HTTP_HEADERS}}else{if(OTEL_EXPORTER_TYPE==="console")return{type:"console"};if(OTEL_EXPORTER_TYPE==="none")return{type:"none"};throw new Error(`Invalid OTEL exporter type ${OTEL_EXPORTER_TYPE}`)}}function _getOTELInstrumentationConfig(){return{userInteractionEvents:OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS}}function _getOTELConfig(){return{serviceName:OTEL_SERVICE_NAME,serviceVersion:OTEL_SERVICE_VERSION,exporter:_getOTELExporterConfig(),instrumentation:_getOTELInstrumentationConfig(),debug:!1}}function _readBundleContent(assetDir,bundleFileName){let assetDirAbs=path.isAbsolute(assetDir)?assetDir:path.join(process.cwd(),assetDir),filePath=path.join(assetDirAbs,bundleFileName);if(!fs2.existsSync(filePath))throw new Error(`OTEL bundle not found at: ${filePath}`);return fs2.readFileSync(filePath,"utf-8")}async function _applyConfigToPage(page,cfg){await page.evaluate(nextCfg=>{let g=globalThis;g.__MCP_DEVTOOLS__||(g.__MCP_DEVTOOLS__={}),g.__MCP_TRACE_ID__=nextCfg.traceId,g.__mcpOtel&&typeof g.__mcpOtel.init=="function"?g.__mcpOtel.init(nextCfg):(g.__MCP_DEVTOOLS__.otelInitialized=!1,g.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available while applying config")},cfg).catch(e=>{let msg=e instanceof Error?e.message:String(e);debug(`[otel-controller] applyConfigToPage failed (ignored): ${msg}`)})}function _installAutoSync(browserContext,getCfg){let perPageHandlers=new WeakMap,attachToPage=page=>{if(perPageHandlers.has(page))return;let onFrameNavigated=async frame=>{frame===page.mainFrame()&&await _applyConfigToPage(page,getCfg())};perPageHandlers.set(page,onFrameNavigated),page.on("framenavigated",onFrameNavigated)};for(let p of browserContext.pages())attachToPage(p);let onNewPage=p=>{attachToPage(p)};browserContext.on("page",onNewPage);let detach=()=>{try{browserContext.off("page",onNewPage)}catch{}for(let p of browserContext.pages()){let h=perPageHandlers.get(p);if(h)try{p.off("framenavigated",h)}catch{}}};return debug("[otel-controller] auto-sync installed (page+framenavigated)"),{detach}}var OTELController=class{browserContext;config;proxy;initialized=!1;autoSyncDetach;constructor(browserContext){this.browserContext=browserContext,this.config=_getOTELConfig()}async init(options){if(this.initialized){debug("[otel-controller] init skipped: BrowserContext already initialized");return}if(!options.traceId||!options.traceId.trim())throw new Error("[otel-controller] init requires a non-empty traceId");this.config.traceId=options.traceId;let assetDir=_getOtelAssetsDir();this.config.exporter.type==="otlp/http"&&(this.proxy=new OTELProxy({localPath:OTEL_PROXY_LOCAL_PATH,upstreamUrl:this.config.exporter.upstreamURL,upstreamHeaders:{...this.config.exporter.headers??{}}}),await this.proxy.install(this.browserContext)),debug(`[otel-controller] exporter=${this.config.exporter.type} localBase=${OTEL_PROXY_LOCAL_PATH}`+(this.config.exporter.type==="otlp/http"?` upstreamBase=${this.config.exporter.upstreamURL}`:""));let bundleContent=_readBundleContent(assetDir,OTEL_BUNDLE_FILE_NAME),sync=_installAutoSync(this.browserContext,()=>this.config);this.autoSyncDetach=sync.detach,await this.browserContext.addInitScript({content:bundleContent}),await this.browserContext.addInitScript(cfg=>{let g=globalThis;g.__MCP_DEVTOOLS__||(g.__MCP_DEVTOOLS__={}),g.__MCP_TRACE_ID__=cfg.traceId,g.__mcpOtel&&typeof g.__mcpOtel.init=="function"?g.__mcpOtel.init(cfg):(g.__MCP_DEVTOOLS__.otelInitialized=!1,g.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available (initializer bundle did not install)")},this.config),this.initialized=!0,debug("[otel-controller] init installed: bundle + config init scripts + auto-sync")}isOTELRequest(request){return new URL(request.url()).pathname.startsWith(OTEL_PROXY_LOCAL_PATH)}async isInitialized(page){return await page.evaluate(()=>globalThis.__MCP_DEVTOOLS__?.otelInitialized===!0)}async getInitError(page){return await page.evaluate(()=>{let v=globalThis.__MCP_DEVTOOLS__?.otelInitError;if(typeof v=="string"&&v.trim())return v})}async getTraceId(page){return await page.evaluate(()=>{let g=globalThis;if(g.__mcpOtel&&typeof g.__mcpOtel.getTraceId=="function"){let tid=g.__mcpOtel.getTraceId();if(typeof tid=="string"&&tid.trim())return tid}let fallback=g.__MCP_TRACE_ID__;if(typeof fallback=="string"&&fallback.trim())return fallback})}async setTraceId(page,traceId){this.config.traceId=traceId,await page.evaluate(tid=>{let g=globalThis;g.__mcpOtel&&typeof g.__mcpOtel.setTraceId=="function"?g.__mcpOtel.setTraceId(tid):g.__MCP_TRACE_ID__=tid},traceId)}async close(){if(this.autoSyncDetach){try{this.autoSyncDetach()}catch{}this.autoSyncDetach=void 0}this.proxy&&(await this.proxy.uninstall(this.browserContext),this.proxy=void 0)}};var HttpMethod=(HttpMethod3=>(HttpMethod3.GET="GET",HttpMethod3.POST="POST",HttpMethod3.PUT="PUT",HttpMethod3.PATCH="PATCH",HttpMethod3.DELETE="DELETE",HttpMethod3.HEAD="HEAD",HttpMethod3.OPTIONS="OPTIONS",HttpMethod3))(HttpMethod||{}),HttpResourceType=(HttpResourceType2=>(HttpResourceType2.DOCUMENT="document",HttpResourceType2.STYLESHEET="stylesheet",HttpResourceType2.IMAGE="image",HttpResourceType2.MEDIA="media",HttpResourceType2.FONT="font",HttpResourceType2.SCRIPT="script",HttpResourceType2.TEXTTRACK="texttrack",HttpResourceType2.XHR="xhr",HttpResourceType2.FETCH="fetch",HttpResourceType2.EVENTSOURCE="eventsource",HttpResourceType2.WEBSOCKET="websocket",HttpResourceType2.MANIFEST="manifest",HttpResourceType2.OTHER="other",HttpResourceType2))(HttpResourceType||{});import crypto from"node:crypto";function newTraceId(){return crypto.randomBytes(16).toString("hex")}var BrowserToolSessionContext=class _BrowserToolSessionContext{static STATIC_RESOURCE_TYPES=new Set(["image","stylesheet","font","media","script","texttrack","manifest"]);_sessionId;options;otelController;consoleMessages=[];httpRequests=[];initialized=!1;closed=!1;traceId;_numOfInFlightRequests=0;_lastNetworkActivityTimestamp=0;_refMap={};browserContext;page;constructor(sessionId,browserContext,page,options){this._sessionId=sessionId,this.browserContext=browserContext,this.page=page,this.options=options,this.otelController=new OTELController(this.browserContext)}async init(){if(this.closed)throw new Error("Session context is already closed");if(this.initialized)throw new Error("Session context is already initialized");let me=this,consoleMessageSequenceNumber=0;this.page.on("console",msg=>{me.consoleMessages.push(me._toConsoleMessage(msg,++consoleMessageSequenceNumber)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),this.page.on("pageerror",err=>{me.consoleMessages.push(me._errorToConsoleMessage(err,++consoleMessageSequenceNumber)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)});let httpRequestSequenceNumber=0;this.page.on("request",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests++,me._lastNetworkActivityTimestamp=Date.now())}),this.page.on("requestfinished",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++httpRequestSequenceNumber)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),this.page.on("requestfailed",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++httpRequestSequenceNumber)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),this.options.otelEnable&&(this.traceId=newTraceId(),await this.otelController.init({traceId:this.traceId})),this.initialized=!0}_toConsoleMessageLevelName(type){switch(type){case"assert":case"error":return"error";case"warning":return"warning";case"count":case"dir":case"dirxml":case"info":case"log":case"table":case"time":case"timeEnd":return"info";case"clear":case"debug":case"endGroup":case"profile":case"profileEnd":case"startGroup":case"startGroupCollapsed":case"trace":return"debug";default:return"info"}}_toConsoleMessage(message,sequenceNumber){let timestamp=Date.now(),levelName=this._toConsoleMessageLevelName(message.type()),levelCode=ConsoleMessageLevel[levelName].code;return{type:message.type(),text:message.text(),level:{name:levelName,code:levelCode},location:{url:message.location().url,lineNumber:message.location().lineNumber,columnNumber:message.location().columnNumber},timestamp,sequenceNumber}}_errorToConsoleMessage(error,sequenceNumber){let timestamp=Date.now();return error instanceof Error?{type:"error",text:error.message,level:{name:"error",code:3},timestamp,sequenceNumber}:{type:"error",text:String(error),level:{name:"error",code:3},timestamp,sequenceNumber}}_isBodyLikelyPresent(status,method){return!(method==="HEAD"||method==="OPTIONS"||status===204||status===304||status>=300&&status<400)}async _safeReadResponseBody(res){try{let method=res.request().method(),status=res.status();return this._isBodyLikelyPresent(status,method)?(await res.body()).toString("utf-8"):void 0}catch{return}}async _toHttpRequest(req,sequenceNumber){let res=await req.response(),resourceType=req.resourceType(),skipResponseBody=_BrowserToolSessionContext.STATIC_RESOURCE_TYPES.has(resourceType);return{url:req.url(),method:req.method(),headers:req.headers(),body:req.postData()||void 0,resourceType,failure:req.failure()?.errorText,duration:req.timing().responseEnd,response:res?{status:res.status(),statusText:res.statusText(),headers:res.headers(),body:skipResponseBody?void 0:await this._safeReadResponseBody(res)}:void 0,ok:res?res.ok():!1,timestamp:Math.floor(req.timing().startTime),sequenceNumber}}numOfInFlightRequests(){return this._numOfInFlightRequests}lastNetworkActivityTimestamp(){return this._lastNetworkActivityTimestamp}sessionId(){return this._sessionId}async getTraceId(){return this.traceId}async setTraceId(traceId){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");this.traceId=traceId,await this.otelController.setTraceId(this.page,traceId)}getConsoleMessages(){return this.consoleMessages}getHttpRequests(){return this.httpRequests}getRefMap(){return this._refMap}setRefMap(refs){this._refMap=refs}async close(){if(this.closed)return!1;debug(`Closing OTEL controller of the session with id ${this._sessionId} ...`),await this.otelController.close();try{debug(`Closing browser context of the session with id ${this._sessionId} ...`),await closeBrowserContext(this.browserContext)}catch(err){debug(`Error occurred while closing browser context of the session with id ${this._sessionId} ...`,err)}return this.consoleMessages.length=0,this.httpRequests.length=0,this._refMap={},this.closed=!0,!0}};var BrowserToolExecutor=class{async executeTool(context,tool,args){debug(`Executing tool ${tool.name()} with input: ${toJson(args)}`);try{let result=await tool.handle(context,args);return debug(`Executed tool ${tool.name()} and got output: ${toJson(result)}`),result}catch(err){throw debug(`Error occurred while executing ${tool.name()}: ${err}`),err}}};var INTERACTIVE_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]),CONTENT_ROLES=new Set(["heading","cell","gridcell","columnheader","rowheader","listitem","article","region","main","navigation"]),STRUCTURAL_ROLES=new Set(["generic","group","list","table","row","rowgroup","grid","treegrid","menu","menubar","toolbar","tablist","tree","directory","document","application","presentation","none"]),refCounter=0;function nextRef(){return`e${++refCounter}`}function buildSelectorDescriptor(role,name){if(name!==void 0&&name!==""){let escaped=JSON.stringify(name);return`getByRole('${role}', { name: ${escaped}, exact: true })`}return`getByRole('${role}')`}function createRoleNameTracker(){let counts=new Map,refsByKey=new Map;return{getKey(role,name){return`${role}:${name??""}`},getNextIndex(role,name){let key=this.getKey(role,name),current=counts.get(key)??0;return counts.set(key,current+1),current},trackRef(role,name,ref){let key=this.getKey(role,name),refs=refsByKey.get(key)??[];refs.push(ref),refsByKey.set(key,refs)},getDuplicateKeys(){let duplicates=new Set;for(let[key,refs]of refsByKey)refs.length>1&&duplicates.add(key);return duplicates}}}function removeNthFromNonDuplicates(refs,tracker){let duplicateKeys=tracker.getDuplicateKeys();for(let entry of Object.values(refs)){let key=tracker.getKey(entry.role,entry.name);duplicateKeys.has(key)||delete entry.nth}}function getIndentLevel(line){let m=line.match(/^(\s*)/);return m?Math.floor(m[1].length/2):0}function processLine(line,refs,options,tracker){let depth=getIndentLevel(line);if(options.maxDepth!==void 0&&depth>options.maxDepth)return null;let match=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!match)return options.interactiveOnly?null:line;let[,prefix,role,name,suffix]=match,roleLower=role.toLowerCase();if(role.startsWith("/"))return line;let isInteractive=INTERACTIVE_ROLES.has(roleLower),isContent=CONTENT_ROLES.has(roleLower),isStructural=STRUCTURAL_ROLES.has(roleLower);if(options.interactiveOnly&&!isInteractive||options.compact&&isStructural&&!name)return null;if(!(isInteractive||isContent&&name))return line;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref);let entry={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};refs[ref]=entry;let enhanced=`${prefix}${role}`;return name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.trim()&&(enhanced+=suffix),enhanced}function compactTree(tree){let lines=tree.split(`
1
+ import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE,BROWSER_EXECUTABLE_PATH,BROWSER_HEADLESS_ENABLE,BROWSER_HTTP_REQUESTS_BUFFER_SIZE,BROWSER_LOCALE,BROWSER_PERSISTENT_ENABLE,BROWSER_PERSISTENT_USER_DATA_DIR,BROWSER_POLICY_UI_DEBUGGING_ENABLE,BROWSER_SERVER_INSTRUCTIONS_ENABLE,BROWSER_USE_INSTALLED_ON_SYSTEM,ConsoleMessageLevel,ConsoleMessageLevelName,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,NODE_CONSOLE_MESSAGES_BUFFER_SIZE,NODE_INSPECTOR_HOST,NODE_POLICY_DEBUGGING_ENABLE,NODE_SERVER_INSTRUCTIONS_ENABLE,OTEL_ASSETS_DIR,OTEL_ENABLE,OTEL_EXPORTER_HTTP_HEADERS,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION,PLATFORM,SourceMapResolver,V8Api,addWatchExpression,clearWatchExpressions,createProbe,debug,detachDebugging,enableDebugging,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-3M2NFQVF.js";import fs from"node:fs";import{chromium,firefox,webkit}from"playwright";var DEFAULT_BROWSER_TYPE="chromium",browsers=new Map,persistenceBrowserContexts=new Map;function _browserKey(browserOptions){return JSON.stringify(browserOptions)}function _browserLaunchOptions(browserOptions){let launchOptions={headless:browserOptions.headless,executablePath:browserOptions.executablePath,handleSIGINT:!1,handleSIGTERM:!1};if(browserOptions.useInstalledOnSystem)if(browserOptions.browserType==="chromium")launchOptions.channel="chrome",launchOptions.args=["--disable-blink-features=AutomationControlled"],launchOptions.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${browserOptions.browserType} is not supported to be used from the one installed on the system`);return launchOptions}async function _createBrowser(browserOptions){let browserInstance;switch(browserOptions.browserType){case"firefox":browserInstance=firefox;break;case"webkit":browserInstance=webkit;break;default:browserInstance=chromium;break}return browserInstance.launch(_browserLaunchOptions(browserOptions))}async function _getBrowser(browserOptions){let browserKey=_browserKey(browserOptions),browserInstance=browsers.get(browserKey);if(browserInstance&&!browserInstance.isConnected()){try{await browserInstance.close().catch(()=>{})}catch{}browserInstance=void 0}return browserInstance||(browserInstance=await _createBrowser(browserOptions),browsers.set(browserKey,browserInstance)),browserInstance}function _persistentBrowserContextKey(browserContextOptions){return browserContextOptions.persistent.userDataDir}function _persistentBrowserContextLaunchOptions(browserContextOptions){let browserOptions=browserContextOptions.browserOptions,launchOptions={headless:browserOptions.headless,executablePath:browserOptions.executablePath,bypassCSP:!0,viewport:browserOptions.headless?void 0:null,locale:browserContextOptions.locale};if(browserOptions.useInstalledOnSystem)if(browserOptions.browserType==="chromium")launchOptions.channel="chrome",launchOptions.args=["--disable-blink-features=AutomationControlled"],launchOptions.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${browserOptions.browserType} is not supported to be used from the one installed on the system`);return launchOptions}async function _createPersistentBrowserContext(browserContextOptions){let browserInstance;switch(browserContextOptions.browserOptions.browserType){case"firefox":browserInstance=firefox;break;case"webkit":browserInstance=webkit;break;default:browserInstance=chromium;break}let userDataDir=browserContextOptions.persistent.userDataDir;fs.mkdirSync(userDataDir,{recursive:!0});let browserContext=await browserInstance.launchPersistentContext(userDataDir,_persistentBrowserContextLaunchOptions(browserContextOptions));for(let p of browserContext.pages())try{await p.close()}catch{}return browserContext}async function _getPersistentBrowserContext(browserContextOptions){let persistentBrowserContextKey=_persistentBrowserContextKey(browserContextOptions),browserContext=persistenceBrowserContexts.get(persistentBrowserContextKey);if(browserContext&&!browserContext.browser()?.isConnected()){try{await browserContext.close().catch(()=>{})}catch{}browserContext=void 0}if(!browserContext)browserContext=await _createPersistentBrowserContext(browserContextOptions),persistenceBrowserContexts.set(persistentBrowserContextKey,browserContext);else throw new Error(`There is already active persistent browser context in the user data directory: ${browserContextOptions.persistent?.userDataDir}`);return browserContext}async function newBrowserContext(browserContextOptions={browserOptions:{browserType:DEFAULT_BROWSER_TYPE,headless:BROWSER_HEADLESS_ENABLE,executablePath:BROWSER_EXECUTABLE_PATH,useInstalledOnSystem:BROWSER_USE_INSTALLED_ON_SYSTEM},persistent:BROWSER_PERSISTENT_ENABLE?{userDataDir:BROWSER_PERSISTENT_USER_DATA_DIR}:void 0,locale:BROWSER_LOCALE}){return browserContextOptions.persistent?{browserContext:await _getPersistentBrowserContext(browserContextOptions)}:{browserContext:await(await _getBrowser(browserContextOptions.browserOptions)).newContext({viewport:browserContextOptions.browserOptions.headless?void 0:null,bypassCSP:!0,locale:browserContextOptions.locale})}}async function newPage(browserContext,pageOptions={}){return await browserContext.newPage()}async function closeBrowserContext(browserContext){await browserContext.close();let deleted=!1;for(let[key,val]of persistenceBrowserContexts.entries())browserContext===val&&(persistenceBrowserContexts.delete(key),deleted=!0);return deleted}function _normalizeBasePath(input){let p=input.trim();return p.startsWith("/")||(p="/"+p),p.endsWith("/")||(p=p+"/"),p}function _normalizeUpstreamBaseUrl(input){let u=input.trim();return u&&(u.endsWith("/")?u.slice(0,-1):u)}function _computeSuffixPath(fullUrl,basePath){try{let pathname=new URL(fullUrl).pathname;if(!pathname.startsWith(basePath))return null;let raw=pathname.slice(basePath.length);return raw?raw.startsWith("/")?raw:"/"+raw:null}catch{return null}}function _appendSuffixToUpstream(upstreamBaseUrl,suffixPath,originalUrl){try{let qs=new URL(originalUrl).search??"";return upstreamBaseUrl+suffixPath+qs}catch{return upstreamBaseUrl+suffixPath}}var OTELProxy=class{config;queue;workers;isRunning;isInstalled;metrics;constructor(config){let maxQueueSize=config.maxQueueSize??200,concurrency=config.concurrency??2,respondNoContent=config.respondNoContent??!0,normalizedLocalPath=_normalizeBasePath(config.localPath),normalizedUpstreamUrl=_normalizeUpstreamBaseUrl(config.upstreamUrl);this.config={...config,localPath:normalizedLocalPath,upstreamUrl:normalizedUpstreamUrl,maxQueueSize,concurrency,respondNoContent},this.queue=[],this.workers=[],this.isRunning=!1,this.isInstalled=!1,this.metrics={routedRequests:0,acceptedBatches:0,droppedBatches:0,forwardedBatches:0,failedBatches:0,inFlight:0,queueSize:0,lastError:null}}getMetrics(){return{...this.metrics,queueSize:this.queue.length}}async install(context){if(this.isInstalled)return;let basePath=this.config.localPath;if(!basePath.startsWith("/"))throw new Error('localPath must start with "/" (e.g. "/__mcp_otel/").');let pattern=`**${basePath}**`;await context.route(pattern,async route=>{await this._handleRoute(route)}),this.isInstalled=!0,this.isRunning||await this.start(),debug(`[otel-proxy] installed route pattern: ${pattern} (basePath=${basePath}, upstreamBase=${this.config.upstreamUrl})`)}async uninstall(context){if(!this.isInstalled)return;let pattern=`**${this.config.localPath}**`;try{await context.unroute(pattern)}catch{}this.isInstalled=!1,await this.stop()}async start(){if(this.isRunning)return;this.isRunning=!0;let workerCount=Math.max(1,this.config.concurrency);for(let i=0;i<workerCount;i++){let w=this._workerLoop(i);this.workers.push(w)}debug(`[otel-proxy] started with concurrency=${workerCount}, maxQueueSize=${this.config.maxQueueSize}`)}async stop(){if(this.isRunning){this.isRunning=!1,this.queue.length=0;try{await Promise.allSettled(this.workers)}finally{this.workers.length=0}debug("[otel-proxy] stopped")}}async _handleRoute(route){let req=route.request();if(this.metrics.routedRequests++,req.method().toUpperCase()==="OPTIONS"){await this._fulfillFast(route);return}if(this.config.shouldForward&&!this.config.shouldForward(req)){await this._fulfillFast(route);return}let requestUrl=req.url(),basePath=this.config.localPath,suffixPath=_computeSuffixPath(requestUrl,basePath);if(!suffixPath){await route.fallback();return}let upstreamFullUrl=_appendSuffixToUpstream(this.config.upstreamUrl,suffixPath,requestUrl),body=await req.postDataBuffer()??Buffer.alloc(0),contentType=req.headers()["content-type"]??"application/x-protobuf",method=req.method(),headers={"content-type":contentType};if(this.config.upstreamHeaders)for(let[k,v]of Object.entries(this.config.upstreamHeaders))headers[k]=v;if(this.queue.length>=this.config.maxQueueSize){this.metrics.droppedBatches++,await this._fulfillFast(route),warn(`[otel-proxy] dropped batch (queue full: ${this.queue.length}/${this.config.maxQueueSize}) suffix=${suffixPath}`);return}let item={body,contentType,createdAtMs:Date.now(),upstreamUrl:upstreamFullUrl,method,headers};this.queue.push(item),this.metrics.acceptedBatches++,await this._fulfillFast(route)}async _fulfillFast(route){let status=this.config.respondNoContent?204:200;if(status===204){await route.fulfill({status});return}await route.fulfill({status,headers:{"content-type":"text/plain; charset=utf-8"},body:""})}async _workerLoop(workerIndex){for(;this.isRunning;){let item=this.queue.shift();if(!item){await this._sleep(25);continue}this.metrics.inFlight++;try{await this._forwardUpstream(item),this.metrics.forwardedBatches++}catch(e){this.metrics.failedBatches++;let msg=e instanceof Error?e.message:String(e);this.metrics.lastError=msg,warn(`[otel-proxy] worker=${workerIndex} forward failed: ${msg}`)}finally{this.metrics.inFlight--}}}async _forwardUpstream(item){let res=await fetch(item.upstreamUrl,{method:item.method,headers:item.headers,body:new Uint8Array(item.body)});if(res.status<200||res.status>=300){let text=await this._safeReadText(res);throw new Error(`upstream returned ${res.status} for ${item.upstreamUrl}: ${text}`)}}async _safeReadText(res){try{return(await res.text()).slice(0,500)}catch{return""}}async _sleep(ms){await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}};import*as fs2 from"node:fs";import*as path from"node:path";import{fileURLToPath}from"node:url";var __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),OTEL_PROXY_LOCAL_PATH="/__mcp_otel/",OTEL_BUNDLE_FILE_NAME="otel-initializer.bundle.js";function _getOtelAssetsDir(){return OTEL_ASSETS_DIR?OTEL_ASSETS_DIR:path.join(__dirname,"platform","browser","otel")}function _getOTELExporterConfig(){if(OTEL_EXPORTER_TYPE==="otlp/http"||OTEL_EXPORTER_HTTP_URL){if(!OTEL_EXPORTER_HTTP_URL)throw new Error('OTEL exporter HTTP url must be set when OTEL exporter type is "otlp/http"');return{type:"otlp/http",url:OTEL_PROXY_LOCAL_PATH,upstreamURL:OTEL_EXPORTER_HTTP_URL,headers:OTEL_EXPORTER_HTTP_HEADERS}}else{if(OTEL_EXPORTER_TYPE==="console")return{type:"console"};if(OTEL_EXPORTER_TYPE==="none")return{type:"none"};throw new Error(`Invalid OTEL exporter type ${OTEL_EXPORTER_TYPE}`)}}function _getOTELInstrumentationConfig(){return{userInteractionEvents:OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS}}function _getOTELConfig(){return{serviceName:OTEL_SERVICE_NAME,serviceVersion:OTEL_SERVICE_VERSION,exporter:_getOTELExporterConfig(),instrumentation:_getOTELInstrumentationConfig(),debug:!1}}function _readBundleContent(assetDir,bundleFileName){let assetDirAbs=path.isAbsolute(assetDir)?assetDir:path.join(process.cwd(),assetDir),filePath=path.join(assetDirAbs,bundleFileName);if(!fs2.existsSync(filePath))throw new Error(`OTEL bundle not found at: ${filePath}`);return fs2.readFileSync(filePath,"utf-8")}async function _applyConfigToPage(page,cfg){await page.evaluate(nextCfg=>{let g=globalThis;g.__MCP_DEVTOOLS__||(g.__MCP_DEVTOOLS__={}),g.__MCP_TRACE_ID__=nextCfg.traceId,g.__mcpOtel&&typeof g.__mcpOtel.init=="function"?g.__mcpOtel.init(nextCfg):(g.__MCP_DEVTOOLS__.otelInitialized=!1,g.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available while applying config")},cfg).catch(e=>{let msg=e instanceof Error?e.message:String(e);debug(`[otel-controller] applyConfigToPage failed (ignored): ${msg}`)})}function _installAutoSync(browserContext,getCfg){let perPageHandlers=new WeakMap,attachToPage=page=>{if(perPageHandlers.has(page))return;let onFrameNavigated=async frame=>{frame===page.mainFrame()&&await _applyConfigToPage(page,getCfg())};perPageHandlers.set(page,onFrameNavigated),page.on("framenavigated",onFrameNavigated)};for(let p of browserContext.pages())attachToPage(p);let onNewPage=p=>{attachToPage(p)};browserContext.on("page",onNewPage);let detach=()=>{try{browserContext.off("page",onNewPage)}catch{}for(let p of browserContext.pages()){let h=perPageHandlers.get(p);if(h)try{p.off("framenavigated",h)}catch{}}};return debug("[otel-controller] auto-sync installed (page+framenavigated)"),{detach}}var OTELController=class{browserContext;config;proxy;initialized=!1;autoSyncDetach;constructor(browserContext){this.browserContext=browserContext,this.config=_getOTELConfig()}async init(options){if(this.initialized){debug("[otel-controller] init skipped: BrowserContext already initialized");return}if(!options.traceId||!options.traceId.trim())throw new Error("[otel-controller] init requires a non-empty traceId");this.config.traceId=options.traceId;let assetDir=_getOtelAssetsDir();this.config.exporter.type==="otlp/http"&&(this.proxy=new OTELProxy({localPath:OTEL_PROXY_LOCAL_PATH,upstreamUrl:this.config.exporter.upstreamURL,upstreamHeaders:{...this.config.exporter.headers??{}}}),await this.proxy.install(this.browserContext)),debug(`[otel-controller] exporter=${this.config.exporter.type} localBase=${OTEL_PROXY_LOCAL_PATH}`+(this.config.exporter.type==="otlp/http"?` upstreamBase=${this.config.exporter.upstreamURL}`:""));let bundleContent=_readBundleContent(assetDir,OTEL_BUNDLE_FILE_NAME),sync=_installAutoSync(this.browserContext,()=>this.config);this.autoSyncDetach=sync.detach,await this.browserContext.addInitScript({content:bundleContent}),await this.browserContext.addInitScript(cfg=>{let g=globalThis;g.__MCP_DEVTOOLS__||(g.__MCP_DEVTOOLS__={}),g.__MCP_TRACE_ID__=cfg.traceId,g.__mcpOtel&&typeof g.__mcpOtel.init=="function"?g.__mcpOtel.init(cfg):(g.__MCP_DEVTOOLS__.otelInitialized=!1,g.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available (initializer bundle did not install)")},this.config),this.initialized=!0,debug("[otel-controller] init installed: bundle + config init scripts + auto-sync")}isOTELRequest(request){return new URL(request.url()).pathname.startsWith(OTEL_PROXY_LOCAL_PATH)}async isInitialized(page){return await page.evaluate(()=>globalThis.__MCP_DEVTOOLS__?.otelInitialized===!0)}async getInitError(page){return await page.evaluate(()=>{let v=globalThis.__MCP_DEVTOOLS__?.otelInitError;if(typeof v=="string"&&v.trim())return v})}async getTraceId(page){return await page.evaluate(()=>{let g=globalThis;if(g.__mcpOtel&&typeof g.__mcpOtel.getTraceId=="function"){let tid=g.__mcpOtel.getTraceId();if(typeof tid=="string"&&tid.trim())return tid}let fallback=g.__MCP_TRACE_ID__;if(typeof fallback=="string"&&fallback.trim())return fallback})}async setTraceId(page,traceId){this.config.traceId=traceId,await page.evaluate(tid=>{let g=globalThis;g.__mcpOtel&&typeof g.__mcpOtel.setTraceId=="function"?g.__mcpOtel.setTraceId(tid):g.__MCP_TRACE_ID__=tid},traceId)}async close(){if(this.autoSyncDetach){try{this.autoSyncDetach()}catch{}this.autoSyncDetach=void 0}this.proxy&&(await this.proxy.uninstall(this.browserContext),this.proxy=void 0)}};var HttpMethod=(HttpMethod3=>(HttpMethod3.GET="GET",HttpMethod3.POST="POST",HttpMethod3.PUT="PUT",HttpMethod3.PATCH="PATCH",HttpMethod3.DELETE="DELETE",HttpMethod3.HEAD="HEAD",HttpMethod3.OPTIONS="OPTIONS",HttpMethod3))(HttpMethod||{}),HttpResourceType=(HttpResourceType2=>(HttpResourceType2.DOCUMENT="document",HttpResourceType2.STYLESHEET="stylesheet",HttpResourceType2.IMAGE="image",HttpResourceType2.MEDIA="media",HttpResourceType2.FONT="font",HttpResourceType2.SCRIPT="script",HttpResourceType2.TEXTTRACK="texttrack",HttpResourceType2.XHR="xhr",HttpResourceType2.FETCH="fetch",HttpResourceType2.EVENTSOURCE="eventsource",HttpResourceType2.WEBSOCKET="websocket",HttpResourceType2.MANIFEST="manifest",HttpResourceType2.OTHER="other",HttpResourceType2))(HttpResourceType||{});import crypto from"node:crypto";function newTraceId(){return crypto.randomBytes(16).toString("hex")}var BrowserToolSessionContext=class _BrowserToolSessionContext{static STATIC_RESOURCE_TYPES=new Set(["image","stylesheet","font","media","script","texttrack","manifest"]);static STATIC_ASSET_EXT=/\.(js|mjs|cjs|map|css|woff2?|ttf|otf|eot|png|jpe?g|gif|webp|svg|ico|mp4|webm|mp3|wav|pdf)(\?|$)/i;_sessionId;options;otelController;consoleMessages=[];httpRequests=[];initialized=!1;closed=!1;traceId;_numOfInFlightRequests=0;_lastNetworkActivityTimestamp=0;_refMap={};browserContext;page;constructor(sessionId,browserContext,page,options){this._sessionId=sessionId,this.browserContext=browserContext,this.page=page,this.options=options,this.otelController=new OTELController(this.browserContext)}async init(){if(this.closed)throw new Error("Session context is already closed");if(this.initialized)throw new Error("Session context is already initialized");let me=this,consoleMessageSequenceNumber=0;this.page.on("console",msg=>{me.consoleMessages.push(me._toConsoleMessage(msg,++consoleMessageSequenceNumber)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),this.page.on("pageerror",err=>{me.consoleMessages.push(me._errorToConsoleMessage(err,++consoleMessageSequenceNumber)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)});let httpRequestSequenceNumber=0;this.page.on("request",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests++,me._lastNetworkActivityTimestamp=Date.now())}),this.page.on("requestfinished",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++httpRequestSequenceNumber)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),this.page.on("requestfailed",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++httpRequestSequenceNumber)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),this.options.otelEnable&&(this.traceId=newTraceId(),await this.otelController.init({traceId:this.traceId})),this.initialized=!0}_toConsoleMessageLevelName(type){switch(type){case"assert":case"error":return"error";case"warning":return"warning";case"count":case"dir":case"dirxml":case"info":case"log":case"table":case"time":case"timeEnd":return"info";case"clear":case"debug":case"endGroup":case"profile":case"profileEnd":case"startGroup":case"startGroupCollapsed":case"trace":return"debug";default:return"info"}}_toConsoleMessage(message,sequenceNumber){let timestamp=Date.now(),levelName=this._toConsoleMessageLevelName(message.type()),levelCode=ConsoleMessageLevel[levelName].code;return{type:message.type(),text:message.text(),level:{name:levelName,code:levelCode},location:{url:message.location().url,lineNumber:message.location().lineNumber,columnNumber:message.location().columnNumber},timestamp,sequenceNumber}}_errorToConsoleMessage(error,sequenceNumber){let timestamp=Date.now();return error instanceof Error?{type:"error",text:error.message,level:{name:"error",code:3},timestamp,sequenceNumber}:{type:"error",text:String(error),level:{name:"error",code:3},timestamp,sequenceNumber}}_isStaticResourceUrl(url){try{let pathname=new URL(url).pathname;return _BrowserToolSessionContext.STATIC_ASSET_EXT.test(pathname)}catch{return!1}}_isBodyLikelyPresent(status,method){return!(method==="HEAD"||method==="OPTIONS"||status===204||status===304||status>=300&&status<400)}async _safeReadResponseBody(res){try{let method=res.request().method(),status=res.status();return this._isBodyLikelyPresent(status,method)?(await res.body()).toString("utf-8"):void 0}catch{return}}async _toHttpRequest(req,sequenceNumber){let res=await req.response(),resourceType=req.resourceType(),skipResponseBody=_BrowserToolSessionContext.STATIC_RESOURCE_TYPES.has(resourceType)||this._isStaticResourceUrl(req.url());return{url:req.url(),method:req.method(),headers:req.headers(),body:req.postData()||void 0,resourceType,failure:req.failure()?.errorText,duration:req.timing().responseEnd,response:res?{status:res.status(),statusText:res.statusText(),headers:res.headers(),body:skipResponseBody?void 0:await this._safeReadResponseBody(res)}:void 0,ok:res?res.ok():!1,timestamp:Math.floor(req.timing().startTime),sequenceNumber}}numOfInFlightRequests(){return this._numOfInFlightRequests}lastNetworkActivityTimestamp(){return this._lastNetworkActivityTimestamp}sessionId(){return this._sessionId}async getTraceId(){return this.traceId}async setTraceId(traceId){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");this.traceId=traceId,await this.otelController.setTraceId(this.page,traceId)}getConsoleMessages(){return this.consoleMessages}getHttpRequests(){return this.httpRequests}getRefMap(){return this._refMap}setRefMap(refs){this._refMap=refs}executionContext(){return{page:this.page}}async close(){if(this.closed)return!1;debug(`Closing OTEL controller of the session with id ${this._sessionId} ...`),await this.otelController.close();try{debug(`Closing browser context of the session with id ${this._sessionId} ...`),await closeBrowserContext(this.browserContext)}catch(err){debug(`Error occurred while closing browser context of the session with id ${this._sessionId} ...`,err)}return this.consoleMessages.length=0,this.httpRequests.length=0,this._refMap={},this.closed=!0,!0}};var BrowserToolExecutor=class{async executeTool(context,tool,args){debug(`Executing tool ${tool.name()} with input: ${toJson(args)}`);try{let result=await tool.handle(context,args);return debug(`Executed tool ${tool.name()} and got output: ${toJson(result)}`),result}catch(err){throw debug(`Error occurred while executing ${tool.name()}: ${err}`),err}}};var INTERACTIVE_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]),CONTENT_ROLES=new Set(["heading","cell","gridcell","columnheader","rowheader","listitem","article","region","main","navigation"]),STRUCTURAL_ROLES=new Set(["generic","group","list","table","row","rowgroup","grid","treegrid","menu","menubar","toolbar","tablist","tree","directory","document","application","presentation","none"]),refCounter=0;function nextRef(){return`e${++refCounter}`}function buildSelectorDescriptor(role,name){if(name!==void 0&&name!==""){let escaped=JSON.stringify(name);return`getByRole('${role}', { name: ${escaped}, exact: true })`}return`getByRole('${role}')`}function createRoleNameTracker(){let counts=new Map,refsByKey=new Map;return{getKey(role,name){return`${role}:${name??""}`},getNextIndex(role,name){let key=this.getKey(role,name),current=counts.get(key)??0;return counts.set(key,current+1),current},trackRef(role,name,ref){let key=this.getKey(role,name),refs=refsByKey.get(key)??[];refs.push(ref),refsByKey.set(key,refs)},getDuplicateKeys(){let duplicates=new Set;for(let[key,refs]of refsByKey)refs.length>1&&duplicates.add(key);return duplicates}}}function removeNthFromNonDuplicates(refs,tracker){let duplicateKeys=tracker.getDuplicateKeys();for(let entry of Object.values(refs)){let key=tracker.getKey(entry.role,entry.name);duplicateKeys.has(key)||delete entry.nth}}function getIndentLevel(line){let m=line.match(/^(\s*)/);return m?Math.floor(m[1].length/2):0}function processLine(line,refs,options,tracker){let depth=getIndentLevel(line);if(options.maxDepth!==void 0&&depth>options.maxDepth)return null;let match=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!match)return options.interactiveOnly?null:line;let[,prefix,role,name,suffix]=match,roleLower=role.toLowerCase();if(role.startsWith("/"))return line;let isInteractive=INTERACTIVE_ROLES.has(roleLower),isContent=CONTENT_ROLES.has(roleLower),isStructural=STRUCTURAL_ROLES.has(roleLower);if(options.interactiveOnly&&!isInteractive||options.compact&&isStructural&&!name)return null;if(!(isInteractive||isContent&&name))return line;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref);let entry={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};refs[ref]=entry;let enhanced=`${prefix}${role}`;return name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.trim()&&(enhanced+=suffix),enhanced}function compactTree(tree){let lines=tree.split(`
2
2
  `),result=[];for(let i=0;i<lines.length;i++){let line=lines[i];if(line.includes("[ref=")){result.push(line);continue}if(line.includes(":")&&!line.endsWith(":")){result.push(line);continue}let currentIndent=getIndentLevel(line),hasRelevantChildren=!1;for(let j=i+1;j<lines.length&&!(getIndentLevel(lines[j])<=currentIndent);j++)if(lines[j].includes("[ref=")){hasRelevantChildren=!0;break}hasRelevantChildren&&result.push(line)}return result.join(`
3
3
  `)}function processAriaTreeWithRefs(ariaTree,options={}){refCounter=0;let refs={};if(!ariaTree||!ariaTree.trim())return{tree:"(empty)",refs:{}};let lines=ariaTree.split(`
4
4
  `),result=[],tracker=createRoleNameTracker();if(options.interactiveOnly){for(let line of lines){let m=line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);if(!m)continue;let[,,role,name,suffix]=m,roleLower=role.toLowerCase();if(!INTERACTIVE_ROLES.has(roleLower))continue;let ref=nextRef(),nth=tracker.getNextIndex(roleLower,name);tracker.trackRef(roleLower,name,ref),refs[ref]={role:roleLower,name:name??void 0,selector:buildSelectorDescriptor(roleLower,name),nth};let enhanced=`- ${role}`;name&&(enhanced+=` "${name}"`),enhanced+=` [ref=${ref}]`,nth>0&&(enhanced+=` [nth=${nth}]`),suffix&&suffix.includes("[")&&(enhanced+=suffix),result.push(enhanced)}return removeNthFromNonDuplicates(refs,tracker),{tree:result.length?result.join(`
@@ -259,7 +259,9 @@ Takes a screenshot of the current page or a specific element.
259
259
  Do NOT use for page structure\u2014use ARIA/AX snapshots instead.
260
260
  Use only for visual verification (design check, visual bug, contrast, layout).
261
261
  Screenshot is saved to disk; use includeBase64 only when the file cannot be read from the returned path (e.g. remote, container).
262
- `.trim()}inputSchema(){return{outputPath:z6.string().optional().default(os2.tmpdir()),name:z6.string().optional().default(DEFAULT_SCREENSHOT_NAME),selector:z6.string().optional().describe("Selector/ref; omit=viewport."),fullPage:z6.boolean().optional().default(!1),type:z6.enum(getEnumKeyTuples(ScreenshotType)).transform(createEnumTransformer(ScreenshotType)).optional().default(DEFAULT_SCREENSHOT_TYPE),quality:z6.number().int().min(0).max(DEFAULT_SCREENSHOT_QUALITY).optional().describe("JPEG quality 0\u2013100."),includeBase64:z6.boolean().optional().default(!1).describe("Base64 fallback."),annotate:z6.boolean().optional().default(!1).describe("Overlay ARIA ref labels."),annotateContent:z6.boolean().optional().default(!1).describe("Annotate headings."),annotateCursorInteractive:z6.boolean().optional().default(!1).describe("Annotate cursor:pointer elements.")}}outputSchema(){return{filePath:z6.string().describe("Full path of the saved screenshot file."),image:z6.object({data:z6.any().describe("Base64-encoded image data."),mimeType:z6.string().describe("MIME type of the image.")}).optional().describe('Image data included only when "includeBase64" input parameter is set to true.'),annotations:z6.array(z6.object({ref:z6.string(),number:z6.number(),role:z6.string(),name:z6.string().optional(),box:z6.object({x:z6.number(),y:z6.number(),width:z6.number(),height:z6.number()})})).optional().describe("When annotate is true, list of refs and bounding boxes. When selector is set: only annotations overlapping the element, box relative to that element. When fullPage is true: box is document-relative (matches the full-page image). Otherwise: viewport-relative.")}}async handle(context,args){let screenshotType=args.type??DEFAULT_SCREENSHOT_TYPE,filename=`${args.name??DEFAULT_SCREENSHOT_NAME}-${formattedTimeForFilename()}.${screenshotType}`,filePath=path3.resolve(args.outputPath,filename),result=await captureScreenshot(context,{fullPage:args.fullPage,type:screenshotType,annotate:args.annotate,annotateContent:args.annotateContent,annotateCursorInteractive:args.annotateCursorInteractive,selector:args.selector,quality:args.quality});await fs3.writeFile(filePath,result.rawBuffer);let output={filePath,...result.annotations&&result.annotations.length>0?{annotations:result.annotations}:{}};return args.includeBase64&&(output.image=result.image),output}};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},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 _columnForLocationKey(column){return column==null||column===0?1:column}function _locationKey(urlPattern,lineNumber,columnNumber){return`${urlPattern}:${lineNumber}:${columnNumber}`}function _evaluateHitCondition(hitCondition,hitCount){try{let condition=hitCondition.trim();return/^[=<>!%]/.test(condition)&&(condition=`hitCount ${condition}`),!!new Function("hitCount",`return (${condition});`)(hitCount)}catch{return!1}}async function _evaluateWatchExpressionsOnFrame(v8Api,callFrameId,watchExpressions){let results={};for(let watch of watchExpressions.values())try{let result=await v8Api.evaluateOnCallFrame(callFrameId,watch.expression);if(result.exceptionDetails)results[watch.expression]=`[Error: ${result.exceptionDetails.text||"Evaluation failed"}]`;else{let value=await v8Api.extractValueDeep(result.result,2);results[watch.expression]=value}}catch(e){results[watch.expression]=`[Error: ${e.message||"Unknown error"}]`}return results}function _ensureStore(ctx,page,v8Options,config){let existing=STORE_BY_CONTEXT.get(ctx);if(existing)return existing;let v8Api=new V8Api(page,v8Options),sourceMapResolver=new SourceMapResolver(page),mergedConfig={...DEFAULT_DEBUG_CONFIG,...config},store={v8Api,sourceMapResolver,probes:new Map,locationIndex:new Map,watchExpressions:new Map,snapshots:[],snapshotSequence:0,config:mergedConfig,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none"};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",hitBreakpointIds=event.hitBreakpoints||[],hitProbes=[];for(let probe of store.probes.values()){if(!probe.enabled)continue;if(probe.v8BreakpointIds.some(id=>hitBreakpointIds.includes(id))){let conditionMet=!0;probe.hitCondition&&(conditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1)),hitProbes.push({probe,conditionMet})}}let shouldCaptureBreakpoint=hitProbes.some(p=>p.conditionMet),shouldCaptureException=isException&&store.exceptionBreakpoint!=="none";for(let{probe}of hitProbes)probe.hitCount++,probe.lastHitAt=Date.now();if((shouldCaptureBreakpoint||shouldCaptureException)&&event.callFrames.length>0){let topFrame=event.callFrames[0],exceptionInfo;if(isException&&event.data){let excData=event.data;exceptionInfo={type:event.reason==="promiseRejection"?"promiseRejection":"exception",message:excData.description||excData.value||String(excData),name:excData.className,stack:excData.description}}let originalLocation,resolvedLoc=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.location.columnNumber??0);resolvedLoc&&(originalLocation={source:resolvedLoc.source,line:resolvedLoc.line+1,column:resolvedLoc.column!==void 0?resolvedLoc.column+1:void 0,name:resolvedLoc.name});let baseSnapshot={url:topFrame.url||"",lineNumber:topFrame.location.lineNumber+1,columnNumber:topFrame.location.columnNumber!==void 0?topFrame.location.columnNumber+1:void 0,originalLocation,exception:exceptionInfo,captureTimeMs:Date.now()-startTime},callStackFull=[],framesToProcess=event.callFrames.slice(0,store.config.maxCallStackDepth);for(let i=0;i<framesToProcess.length;i++){let frame=framesToProcess[i],captureScopes=i<store.config.maxFramesWithScopes;callStackFull.push(await _callFrameToSnapshot(store.v8Api,frame,captureScopes,store.sourceMapResolver))}let asyncStackTraceFull=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment),probesToCapture=hitProbes.filter(p=>p.conditionMet),hasException=shouldCaptureException,snapshotCount=probesToCapture.length+(hasException?1:0);for(let s=0;s<snapshotCount;s++){let probeId,isLogpoint,logResult,callStack,asyncStackTrace,watchResults;if(s<probesToCapture.length){let{probe}=probesToCapture[s];if(probeId=probe.id,isLogpoint=probe.kind==="logpoint",isLogpoint&&probe.logExpression)try{let evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,probe.logExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}else logResult=void 0;isLogpoint?(callStack=[],asyncStackTrace=void 0,watchResults=void 0):(callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0)}else probeId="__exception__",isLogpoint=!1,logResult=void 0,callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0;let snapshot={id:_generateId(),probeId,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,...baseSnapshot,callStack,asyncStackTrace,logResult,watchResults};store.snapshots.push(snapshot)}store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots)}}finally{await store.v8Api.resume()}}),store.enabled=!0,store.sourceMapResolver.loadAllSourceMaps().then(()=>{store.sourceMapsLoaded=!0}).catch(()=>{})}}function isDebuggingEnabled2(ctx){return _getStore(ctx)?.enabled??!1}async function resolveSourceLocation2(ctx,page,url,line,column=1){let store=_ensureStore(ctx,page);store.enabled||await enableDebugging2(ctx,page);let resolved=await store.sourceMapResolver.resolveLocationByUrl(url,line,column);return resolved?{source:resolved.source,line:resolved.line+1,column:resolved.column+1,name:resolved.name}:null}async function setExceptionBreakpoint2(ctx,state){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");await store.v8Api.setPauseOnExceptions(state),store.exceptionBreakpoint=state}function getExceptionBreakpoint2(ctx){return _getStore(ctx)?.exceptionBreakpoint??"none"}function hasSourceMaps2(ctx){return _getStore(ctx)?.sourceMapResolver.hasSourceMaps()??!1}async function createProbe2(ctx,options){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");let probeId=_generateId(),columnForKey=_columnForLocationKey(options.columnNumber),locationKey=_locationKey(options.urlPattern,options.lineNumber,columnForKey),existingEntry=store.locationIndex.get(locationKey);if(existingEntry){existingEntry.refCount++;let probe2={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[existingEntry.breakpointId],resolvedLocations:existingEntry.resolvedLocations,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe2),probe2}let fullCondition;options.condition?fullCondition=`(${options.condition})`:fullCondition="true";let line0based=options.lineNumber-1,column0based=columnForKey-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)try{breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1}catch{let scriptUrl=store.sourceMapResolver.getScriptUrl(resolved.scriptId);if(scriptUrl){let result=await store.v8Api.setBreakpointByUrl({url:scriptUrl,lineNumber:resolved.location.line,columnNumber:resolved.location.column,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}else throw new Error("Failed to set breakpoint at resolved location and could not fall back (script URL unknown). A probe may already exist at this line; remove it first or use a different line.")}else{let urlRegex=options.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),result=await store.v8Api.setBreakpointByUrl({urlRegex,lineNumber:options.lineNumber-1,columnNumber:options.columnNumber!=null?Math.max(0,options.columnNumber-1):void 0,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}store.locationIndex.set(locationKey,{breakpointId,resolvedLocations:resolvedLocationsCount,refCount:1});let probe={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[breakpointId],resolvedLocations:resolvedLocationsCount,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe),probe}async function removeProbe2(ctx,probeId){let store=_getStore(ctx);if(!store)return!1;let probe=store.probes.get(probeId);if(!probe)return!1;let locationKey=_locationKey(probe.urlPattern,probe.lineNumber,_columnForLocationKey(probe.columnNumber)),entry=store.locationIndex.get(locationKey);if(entry&&(entry.refCount--,entry.refCount===0)){try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.delete(locationKey)}return store.probes.delete(probeId),!0}function listProbes2(ctx){let store=_getStore(ctx);return store?Array.from(store.probes.values()):[]}function getProbe(ctx,probeId){let store=_getStore(ctx);if(store)return store.probes.get(probeId)}function getSnapshots2(ctx){let store=_getStore(ctx);return store?[...store.snapshots]:[]}function getSnapshotsByProbe2(ctx,probeId){let store=_getStore(ctx);return store?store.snapshots.filter(s=>s.probeId===probeId):[]}function clearSnapshotsByProbe(ctx,probeId){let store=_getStore(ctx);if(!store)return 0;let before=store.snapshots.length;return store.snapshots=store.snapshots.filter(s=>s.probeId!==probeId),before-store.snapshots.length}function getSnapshotStats2(ctx){let store=_getStore(ctx);if(!store||store.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let snapshotsByProbe={},totalCaptureTime=0;for(let snapshot of store.snapshots)snapshotsByProbe[snapshot.probeId]=(snapshotsByProbe[snapshot.probeId]||0)+1,totalCaptureTime+=snapshot.captureTimeMs;return{totalSnapshots:store.snapshots.length,snapshotsByProbe,oldestTimestamp:store.snapshots[0].timestamp,newestTimestamp:store.snapshots[store.snapshots.length-1].timestamp,averageCaptureTimeMs:totalCaptureTime/store.snapshots.length}}function addWatchExpression2(ctx,expression){let store=_getStore(ctx);if(!store)throw new Error("Debug store not initialized");let id=_generateId(),watchExpr={id,expression,createdAt:Date.now()};return store.watchExpressions.set(id,watchExpr),watchExpr}function removeWatchExpression2(ctx,watchExpressionId){let store=_getStore(ctx);return store?store.watchExpressions.delete(watchExpressionId):!1}function listWatchExpressions2(ctx){let store=_getStore(ctx);return store?Array.from(store.watchExpressions.values()):[]}function clearWatchExpressions2(ctx){let store=_getStore(ctx);if(!store)return 0;let count=store.watchExpressions.size;return store.watchExpressions.clear(),count}var Status=class{name(){return"debug_status"}description(){return`
262
+ `.trim()}inputSchema(){return{outputPath:z6.string().optional().default(os2.tmpdir()),name:z6.string().optional().default(DEFAULT_SCREENSHOT_NAME),selector:z6.string().optional().describe("Selector/ref; omit=viewport."),fullPage:z6.boolean().optional().default(!1),type:z6.enum(getEnumKeyTuples(ScreenshotType)).transform(createEnumTransformer(ScreenshotType)).optional().default(DEFAULT_SCREENSHOT_TYPE),quality:z6.number().int().min(0).max(DEFAULT_SCREENSHOT_QUALITY).optional().describe("JPEG quality 0\u2013100."),includeBase64:z6.boolean().optional().default(!1).describe("Base64 fallback."),annotate:z6.boolean().optional().default(!1).describe("Overlay ARIA ref labels."),annotateContent:z6.boolean().optional().default(!1).describe("Annotate headings."),annotateCursorInteractive:z6.boolean().optional().default(!1).describe("Annotate cursor:pointer elements.")}}outputSchema(){return{filePath:z6.string().describe("Full path of the saved screenshot file."),image:z6.object({data:z6.any().describe("Base64-encoded image data."),mimeType:z6.string().describe("MIME type of the image.")}).optional().describe('Image data included only when "includeBase64" input parameter is set to true.'),annotations:z6.array(z6.object({ref:z6.string(),number:z6.number(),role:z6.string(),name:z6.string().optional(),box:z6.object({x:z6.number(),y:z6.number(),width:z6.number(),height:z6.number()})})).optional().describe("When annotate is true, list of refs and bounding boxes. When selector is set: only annotations overlapping the element, box relative to that element. When fullPage is true: box is document-relative (matches the full-page image). Otherwise: viewport-relative.")}}async handle(context,args){let screenshotType=args.type??DEFAULT_SCREENSHOT_TYPE,filename=`${args.name??DEFAULT_SCREENSHOT_NAME}-${formattedTimeForFilename()}.${screenshotType}`,filePath=path3.resolve(args.outputPath,filename),result=await captureScreenshot(context,{fullPage:args.fullPage,type:screenshotType,annotate:args.annotate,annotateContent:args.annotateContent,annotateCursorInteractive:args.annotateCursorInteractive,selector:args.selector,quality:args.quality});await fs3.writeFile(filePath,result.rawBuffer);let output={filePath,...result.annotations&&result.annotations.length>0?{annotations:result.annotations}:{}};return args.includeBase64&&(output.image=result.image),output}};var tools2=[new GetAsHtml,new GetAsText,new SaveAsPdf,new TakeScreenshot];import{z as z7}from"zod";var DEFAULT_DEBUG_CONFIG={maxSnapshots:1e3,maxCallStackDepth:1,maxFramesWithScopes:1,maxAsyncStackSegments:10,maxFramesPerAsyncSegment:10},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 _columnForLocationKey(column){return column==null||column===0?1:column}function _locationKey(urlPattern,lineNumber,columnNumber){return`${urlPattern}:${lineNumber}:${columnNumber}`}function _evaluateHitCondition(hitCondition,hitCount){try{let condition=hitCondition.trim();return/^[=<>!%]/.test(condition)&&(condition=`hitCount ${condition}`),!!new Function("hitCount",`return (${condition});`)(hitCount)}catch{return!1}}async function _evaluateWatchExpressionsOnFrame(v8Api,callFrameId,watchExpressions){let results={};for(let watch of watchExpressions.values())try{let result=await v8Api.evaluateOnCallFrame(callFrameId,watch.expression);if(result.exceptionDetails)results[watch.expression]=`[Error: ${result.exceptionDetails.text||"Evaluation failed"}]`;else{let value=await v8Api.extractValueDeep(result.result,2);results[watch.expression]=value}}catch(e){results[watch.expression]=`[Error: ${e.message||"Unknown error"}]`}return results}function _ensureStore(ctx,page,v8Options,config){let existing=STORE_BY_CONTEXT.get(ctx);if(existing)return existing;let v8Api=new V8Api(page,v8Options),sourceMapResolver=new SourceMapResolver(page),mergedConfig={...DEFAULT_DEBUG_CONFIG,...config},store={v8Api,sourceMapResolver,probes:new Map,locationIndex:new Map,watchExpressions:new Map,snapshots:[],snapshotSequence:0,config:mergedConfig,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none"};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",hitBreakpointIds=event.hitBreakpoints||[],hitProbes=[];for(let probe of store.probes.values()){if(!probe.enabled)continue;if(probe.v8BreakpointIds.some(id=>hitBreakpointIds.includes(id))){let conditionMet=!0;probe.hitCondition&&(conditionMet=_evaluateHitCondition(probe.hitCondition,probe.hitCount+1)),hitProbes.push({probe,conditionMet})}}let shouldCaptureBreakpoint=hitProbes.some(p=>p.conditionMet),shouldCaptureException=isException&&store.exceptionBreakpoint!=="none";for(let{probe}of hitProbes)probe.hitCount++,probe.lastHitAt=Date.now();if((shouldCaptureBreakpoint||shouldCaptureException)&&event.callFrames.length>0){let topFrame=event.callFrames[0],exceptionInfo;if(isException&&event.data){let excData=event.data;exceptionInfo={type:event.reason==="promiseRejection"?"promiseRejection":"exception",message:excData.description||excData.value||String(excData),name:excData.className,stack:excData.description}}let originalLocation,resolvedLoc=store.sourceMapResolver.generatedToOriginal(topFrame.location.scriptId,topFrame.location.lineNumber,topFrame.location.columnNumber??0);resolvedLoc&&(originalLocation={source:resolvedLoc.source,line:resolvedLoc.line+1,column:resolvedLoc.column!==void 0?resolvedLoc.column+1:void 0,name:resolvedLoc.name});let baseSnapshot={url:topFrame.url||"",lineNumber:topFrame.location.lineNumber+1,columnNumber:topFrame.location.columnNumber!==void 0?topFrame.location.columnNumber+1:void 0,originalLocation,exception:exceptionInfo,captureTimeMs:Date.now()-startTime},callStackFull=[],framesToProcess=event.callFrames.slice(0,store.config.maxCallStackDepth);for(let i=0;i<framesToProcess.length;i++){let frame=framesToProcess[i],captureScopes=i<store.config.maxFramesWithScopes;callStackFull.push(await _callFrameToSnapshot(store.v8Api,frame,captureScopes,store.sourceMapResolver))}let asyncStackTraceFull=_convertAsyncStackTrace(event.asyncStackTrace,store.sourceMapResolver,store.config.maxAsyncStackSegments,store.config.maxFramesPerAsyncSegment),probesToCapture=hitProbes.filter(p=>p.conditionMet),hasException=shouldCaptureException,snapshotCount=probesToCapture.length+(hasException?1:0);for(let s=0;s<snapshotCount;s++){let probeId,isLogpoint,logResult,callStack,asyncStackTrace,watchResults;if(s<probesToCapture.length){let{probe}=probesToCapture[s];if(probeId=probe.id,isLogpoint=probe.kind==="logpoint",isLogpoint&&probe.logExpression)try{let wrappedExpression=`(
263
+ ${probe.logExpression}
264
+ )`,evalResult=await store.v8Api.evaluateOnCallFrame(topFrame.callFrameId,wrappedExpression,{returnByValue:!0,generatePreview:!0});logResult=store.v8Api.extractValue(evalResult.result)}catch{logResult="[evaluation error]"}else logResult=void 0;isLogpoint?(callStack=[],asyncStackTrace=void 0,watchResults=void 0):(callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0)}else probeId="__exception__",isLogpoint=!1,logResult=void 0,callStack=callStackFull,asyncStackTrace=asyncStackTraceFull,watchResults=store.watchExpressions.size>0?await _evaluateWatchExpressionsOnFrame(store.v8Api,topFrame.callFrameId,store.watchExpressions):void 0;let snapshot={id:_generateId(),probeId,timestamp:Date.now(),sequenceNumber:++store.snapshotSequence,...baseSnapshot,callStack,asyncStackTrace,logResult,watchResults};store.snapshots.push(snapshot)}store.snapshots.length>store.config.maxSnapshots&&store.snapshots.splice(0,store.snapshots.length-store.config.maxSnapshots)}}finally{await store.v8Api.resume()}}),store.enabled=!0,store.sourceMapResolver.loadAllSourceMaps().then(()=>{store.sourceMapsLoaded=!0}).catch(()=>{})}}function isDebuggingEnabled2(ctx){return _getStore(ctx)?.enabled??!1}async function resolveSourceLocation2(ctx,page,url,line,column=1){let store=_ensureStore(ctx,page);store.enabled||await enableDebugging2(ctx,page);let resolved=await store.sourceMapResolver.resolveLocationByUrl(url,line,column);return resolved?{source:resolved.source,line:resolved.line+1,column:resolved.column+1,name:resolved.name}:null}async function setExceptionBreakpoint2(ctx,state){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");await store.v8Api.setPauseOnExceptions(state),store.exceptionBreakpoint=state}function getExceptionBreakpoint2(ctx){return _getStore(ctx)?.exceptionBreakpoint??"none"}function hasSourceMaps2(ctx){return _getStore(ctx)?.sourceMapResolver.hasSourceMaps()??!1}async function createProbe2(ctx,options){let store=_getStore(ctx);if(!store||!store.enabled)throw new Error("Debugging is not enabled");let probeId=_generateId(),columnForKey=_columnForLocationKey(options.columnNumber),locationKey=_locationKey(options.urlPattern,options.lineNumber,columnForKey),existingEntry=store.locationIndex.get(locationKey);if(existingEntry){existingEntry.refCount++;let probe2={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[existingEntry.breakpointId],resolvedLocations:existingEntry.resolvedLocations,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe2),probe2}let fullCondition;options.condition?fullCondition=`(${options.condition})`:fullCondition="true";let line0based=options.lineNumber-1,column0based=columnForKey-1,resolved=store.sourceMapResolver.originalToGenerated(options.urlPattern,line0based,column0based),breakpointId,resolvedLocationsCount=0;if(resolved)try{breakpointId=(await store.v8Api.setBreakpoint({scriptId:resolved.scriptId,lineNumber:resolved.location.line,columnNumber:resolved.location.column},fullCondition)).breakpointId,resolvedLocationsCount=1}catch{let scriptUrl=store.sourceMapResolver.getScriptUrl(resolved.scriptId);if(scriptUrl){let result=await store.v8Api.setBreakpointByUrl({url:scriptUrl,lineNumber:resolved.location.line,columnNumber:resolved.location.column,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}else throw new Error("Failed to set breakpoint at resolved location and could not fall back (script URL unknown). A probe may already exist at this line; remove it first or use a different line.")}else{let urlRegex=options.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),result=await store.v8Api.setBreakpointByUrl({urlRegex,lineNumber:options.lineNumber-1,columnNumber:options.columnNumber!=null?Math.max(0,options.columnNumber-1):void 0,condition:fullCondition});breakpointId=result.breakpointId,resolvedLocationsCount=result.locations.length}store.locationIndex.set(locationKey,{breakpointId,resolvedLocations:resolvedLocationsCount,refCount:1});let probe={id:probeId,kind:options.kind,enabled:!0,urlPattern:options.urlPattern,lineNumber:options.lineNumber,columnNumber:options.columnNumber,condition:options.condition,logExpression:options.logExpression,hitCondition:options.hitCondition,v8BreakpointIds:[breakpointId],resolvedLocations:resolvedLocationsCount,hitCount:0,createdAt:Date.now()};return store.probes.set(probeId,probe),probe}async function removeProbe2(ctx,probeId){let store=_getStore(ctx);if(!store)return!1;let probe=store.probes.get(probeId);if(!probe)return!1;let locationKey=_locationKey(probe.urlPattern,probe.lineNumber,_columnForLocationKey(probe.columnNumber)),entry=store.locationIndex.get(locationKey);if(entry&&(entry.refCount--,entry.refCount===0)){try{await store.v8Api.removeBreakpoint(entry.breakpointId)}catch{}store.locationIndex.delete(locationKey)}return store.probes.delete(probeId),!0}function listProbes2(ctx){let store=_getStore(ctx);return store?Array.from(store.probes.values()):[]}function getProbe(ctx,probeId){let store=_getStore(ctx);if(store)return store.probes.get(probeId)}function getSnapshots2(ctx){let store=_getStore(ctx);return store?[...store.snapshots]:[]}function getSnapshotsByProbe2(ctx,probeId){let store=_getStore(ctx);return store?store.snapshots.filter(s=>s.probeId===probeId):[]}function clearSnapshotsByProbe(ctx,probeId){let store=_getStore(ctx);if(!store)return 0;let before=store.snapshots.length;return store.snapshots=store.snapshots.filter(s=>s.probeId!==probeId),before-store.snapshots.length}function getSnapshotStats2(ctx){let store=_getStore(ctx);if(!store||store.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let snapshotsByProbe={},totalCaptureTime=0;for(let snapshot of store.snapshots)snapshotsByProbe[snapshot.probeId]=(snapshotsByProbe[snapshot.probeId]||0)+1,totalCaptureTime+=snapshot.captureTimeMs;return{totalSnapshots:store.snapshots.length,snapshotsByProbe,oldestTimestamp:store.snapshots[0].timestamp,newestTimestamp:store.snapshots[store.snapshots.length-1].timestamp,averageCaptureTimeMs:totalCaptureTime/store.snapshots.length}}function addWatchExpression2(ctx,expression){let store=_getStore(ctx);if(!store)throw new Error("Debug store not initialized");let id=_generateId(),watchExpr={id,expression,createdAt:Date.now()};return store.watchExpressions.set(id,watchExpr),watchExpr}function removeWatchExpression2(ctx,watchExpressionId){let store=_getStore(ctx);return store?store.watchExpressions.delete(watchExpressionId):!1}function listWatchExpressions2(ctx){let store=_getStore(ctx);return store?Array.from(store.watchExpressions.values()):[]}function clearWatchExpressions2(ctx){let store=_getStore(ctx);if(!store)return 0;let count=store.watchExpressions.size;return store.watchExpressions.clear(),count}var Status=class{name(){return"debug_status"}description(){return`
263
265
  Returns the current debugging status including:
264
266
  - Whether debugging is enabled
265
267
  - Source map status
@@ -297,20 +299,23 @@ NOT call stack or watch expressions. Use tracepoints for full debug context.
297
299
 
298
300
  urlPattern matches script URLs (e.g., "app.js"). Auto-escaped, do not add backslashes.
299
301
 
300
- logExpression examples:
301
- - Simple value: "user.name"
302
- - Template: "\`User: \${user.name}, Age: \${user.age}\`"
303
- - Object: "{ user, timestamp: Date.now() }"
302
+ logExpression: a single JavaScript expression (e.g. "user.name", "JSON.stringify({ a, b })", or "{ discountAmount, finalAmount, n }"). Object literals are supported; for maximum compatibility prefer a single variable or JSON.stringify(...).
304
303
 
305
304
  Returns resolvedLocations: 0 means pattern didn't match any loaded scripts.
306
- `}inputSchema(){return{urlPattern:z13.string().describe("Glob pattern for script URL (e.g. **/app.js)."),lineNumber:z13.number().int().positive().describe("1-based line in script."),logExpression:z13.string().describe("Expression to evaluate and log (e.g. user.name)."),columnNumber:z13.number().int().positive().optional().describe("1-based column."),condition:z13.string().optional().describe("Only log when this expression is true."),hitCondition:z13.string().optional().describe("Hit-count condition (e.g. > 5).")}}outputSchema(){return{id:z13.string().describe("Debug point ID"),urlPattern:z13.string().describe("URL pattern"),lineNumber:z13.number().describe("Line number"),logExpression:z13.string().describe("Log expression"),columnNumber:z13.number().optional().describe("Column number"),condition:z13.string().optional().describe("Condition expression"),hitCondition:z13.string().optional().describe("Hit count condition"),resolvedLocations:z13.number().describe("Number of locations where logpoint was resolved")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let probe=await createProbe2(context.browserContext,{kind:"logpoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,logExpression:args.logExpression,columnNumber:args.columnNumber,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,logExpression:probe.logExpression||args.logExpression,columnNumber:probe.columnNumber,condition:probe.condition,hitCondition:probe.hitCondition,resolvedLocations:probe.resolvedLocations}}};import{z as z14}from"zod";var PutExceptionpoint=class{name(){return"debug_put-exceptionpoint"}description(){return`
305
+ `}inputSchema(){return{urlPattern:z13.string().describe("Glob pattern for script URL (e.g. **/app.js)."),lineNumber:z13.number().int().positive().describe("1-based line in script."),logExpression:z13.string().describe("JS expression to evaluate and log (e.g. user.name, JSON.stringify({ a, b }), or { x, y })."),columnNumber:z13.number().int().positive().optional().describe("1-based column."),condition:z13.string().optional().describe("Only log when this expression is true."),hitCondition:z13.string().optional().describe("Hit-count condition (e.g. > 5).")}}outputSchema(){return{id:z13.string().describe("Debug point ID"),urlPattern:z13.string().describe("URL pattern"),lineNumber:z13.number().describe("Line number"),logExpression:z13.string().describe("Log expression"),columnNumber:z13.number().optional().describe("Column number"),condition:z13.string().optional().describe("Condition expression"),hitCondition:z13.string().optional().describe("Hit count condition"),resolvedLocations:z13.number().describe("Number of locations where logpoint was resolved")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let probe=await createProbe2(context.browserContext,{kind:"logpoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,logExpression:args.logExpression,columnNumber:args.columnNumber,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,logExpression:probe.logExpression||args.logExpression,columnNumber:probe.columnNumber,condition:probe.condition,hitCondition:probe.hitCondition,resolvedLocations:probe.resolvedLocations}}};import{z as z14}from"zod";var PutExceptionpoint=class{name(){return"debug_put-exceptionpoint"}description(){return`
307
306
  Sets the exception tracepoint state:
308
307
  - "none": Don't capture on exceptions
309
308
  - "uncaught": Capture only on uncaught exceptions
310
309
  - "all": Capture on all exceptions (caught and uncaught)
311
310
 
312
311
  When an exception occurs, a snapshot is captured with exception details.
313
- `}inputSchema(){return{state:z14.enum(["none","uncaught","all"])}}outputSchema(){return{previousState:z14.string().describe("Previous state"),currentState:z14.string().describe("Current state"),message:z14.string().describe("Status message")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let previousState=getExceptionBreakpoint2(context.browserContext);await setExceptionBreakpoint2(context.browserContext,args.state);let currentState=getExceptionBreakpoint2(context.browserContext);return{previousState,currentState,message:`Exception tracepoint set to: ${currentState}`}}};import{z as z16}from"zod";import{z as z15}from"zod";var OriginalLocationSchema=z15.object({source:z15.string().describe("Original source file path"),line:z15.number().describe("1-based line number"),column:z15.number().optional().describe("1-based column number"),name:z15.string().optional().describe("Original identifier name")}),ScopeVariableSchema=z15.object({name:z15.string().describe("Variable name"),value:z15.any().describe("Variable value"),type:z15.string().describe("Variable type")}),ScopeSnapshotSchema=z15.object({type:z15.string().describe("Scope type (global, local, closure, etc.)"),name:z15.string().optional().describe("Scope name"),variables:z15.array(ScopeVariableSchema).describe("Variables in this scope")}),CallFrameSchema=z15.object({functionName:z15.string().describe("Function name"),url:z15.string().describe("Script URL"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),scriptId:z15.string().describe("V8 script ID"),scopes:z15.array(ScopeSnapshotSchema).describe("Variable scopes"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncCallFrameSchema=z15.object({functionName:z15.string().describe("Function name"),url:z15.string().describe("Script URL"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncStackSegmentSchema=z15.object({description:z15.string().optional().describe("Async boundary (Promise.then, setTimeout, etc.)"),callFrames:z15.array(AsyncCallFrameSchema).describe("Frames in this segment")}),AsyncStackTraceSchema=z15.object({segments:z15.array(AsyncStackSegmentSchema).describe("Chain of async segments")}),SnapshotBaseSchema=z15.object({id:z15.string().describe("Snapshot ID"),probeId:z15.string().describe("Probe ID that triggered this snapshot"),timestamp:z15.number().describe("Unix timestamp (ms)"),sequenceNumber:z15.number().describe("Monotonic sequence number for ordering"),url:z15.string().describe("Script URL where snapshot was taken"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location"),captureTimeMs:z15.number().describe("Time taken to capture snapshot (ms)")}),TracepointSnapshotSchema=SnapshotBaseSchema.extend({callStack:z15.array(CallFrameSchema).describe("Call stack with local variables"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z15.record(z15.any()).optional().describe("Watch expression results")}),LogpointSnapshotSchema=SnapshotBaseSchema.extend({logResult:z15.any().optional().describe("Result of log expression evaluation")}),ExceptionInfoSchema=z15.object({type:z15.enum(["exception","promiseRejection"]).describe("Exception type"),message:z15.string().describe("Exception message"),name:z15.string().optional().describe("Exception name/class"),stack:z15.string().optional().describe("Stack trace string")}),ExceptionpointSnapshotSchema=SnapshotBaseSchema.extend({exception:ExceptionInfoSchema.optional().describe("Exception information"),callStack:z15.array(CallFrameSchema).describe("Call stack at exception"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z15.record(z15.any()).optional().describe("Watch expression results")});var SNAPSHOT_TYPES=["tracepoint","logpoint","exceptionpoint"];function applyFilters(snapshots,fromSequence,limit){let out=snapshots;return fromSequence!==void 0&&(out=out.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&out.length>limit&&(out=out.slice(-limit)),out}var GetProbeSnapshots=class{name(){return"debug_get-probe-snapshots"}description(){return"\nRetrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, returns all.\nResponse fields: `tracepointSnapshots`, `logpointSnapshots`, `exceptionpointSnapshots`.\nOptional `probeId` filters tracepoint or logpoint snapshots; `fromSequence` and `limit` apply per type.\n ".trim()}inputSchema(){return{types:z16.array(z16.enum(SNAPSHOT_TYPES)).optional().describe("Return only these types; omit for all."),probeId:z16.string().optional().describe("Filter by this probe ID (tracepoint/logpoint)."),fromSequence:z16.number().int().nonnegative().optional().describe("Snapshots with sequence > this (polling)."),limit:z16.number().int().positive().optional()}}outputSchema(){return{tracepointSnapshots:z16.array(TracepointSnapshotSchema).describe("Tracepoint snapshots"),logpointSnapshots:z16.array(LogpointSnapshotSchema).describe("Logpoint snapshots"),exceptionpointSnapshots:z16.array(ExceptionpointSnapshotSchema).describe("Exceptionpoint snapshots")}}async handle(context,args){let getTracepoint=!args.types?.length||args.types.includes("tracepoint"),getLogpoint=!args.types?.length||args.types.includes("logpoint"),getExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointSnapshots=[],logpointSnapshots=[],exceptionpointSnapshots=[];if(!isDebuggingEnabled2(context.browserContext))return{tracepointSnapshots,logpointSnapshots,exceptionpointSnapshots};let allSnapshots=getSnapshots2(context.browserContext),probes=listProbes2(context.browserContext);if(args.probeId&&(getTracepoint||getLogpoint)){let raw=getSnapshotsByProbe2(context.browserContext,args.probeId),filtered=applyFilters(raw,args.fromSequence,args.limit),probe=probes.find(p=>p.id===args.probeId);probe?.kind==="tracepoint"&&getTracepoint?tracepointSnapshots=filtered:probe?.kind==="logpoint"&&getLogpoint&&(logpointSnapshots=filtered.map(s=>({id:s.id,probeId:s.probeId,timestamp:s.timestamp,sequenceNumber:s.sequenceNumber,url:s.url,lineNumber:s.lineNumber,columnNumber:s.columnNumber,originalLocation:s.originalLocation,captureTimeMs:s.captureTimeMs,logResult:s.logResult})))}else{if(getTracepoint){let tracepointIds=new Set(probes.filter(p=>p.kind==="tracepoint").map(p=>p.id)),raw=allSnapshots.filter(s=>tracepointIds.has(s.probeId));tracepointSnapshots=applyFilters(raw,args.fromSequence,args.limit)}if(getLogpoint){let logpointIds=new Set(probes.filter(p=>p.kind==="logpoint").map(p=>p.id)),raw=allSnapshots.filter(s=>logpointIds.has(s.probeId));logpointSnapshots=applyFilters(raw,args.fromSequence,args.limit).map(s=>({id:s.id,probeId:s.probeId,timestamp:s.timestamp,sequenceNumber:s.sequenceNumber,url:s.url,lineNumber:s.lineNumber,columnNumber:s.columnNumber,originalLocation:s.originalLocation,captureTimeMs:s.captureTimeMs,logResult:s.logResult}))}}if(getExceptionpoint){let raw=getSnapshotsByProbe2(context.browserContext,"__exception__");exceptionpointSnapshots=applyFilters(raw,args.fromSequence,args.limit)}return{tracepointSnapshots,logpointSnapshots,exceptionpointSnapshots}}};import{z as z17}from"zod";var SNAPSHOT_TYPES2=["tracepoint","logpoint","exceptionpoint"],ClearProbeSnapshots=class{name(){return"debug_clear-probe-snapshots"}description(){return"\nClears snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, clears all.\nOptional `probeId`: clear only snapshots for this probe (for tracepoint/logpoint).\n ".trim()}inputSchema(){return{types:z17.array(z17.enum(SNAPSHOT_TYPES2)).optional().describe("Clear only these types; omit for all."),probeId:z17.string().optional().describe("Clear only this probe (tracepoint/logpoint).")}}outputSchema(){return{tracepointCleared:z17.number().describe("Tracepoint snapshots cleared"),logpointCleared:z17.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z17.number().describe("Exceptionpoint snapshots cleared"),message:z17.string().describe("Status message")}}async handle(context,args){let clearTracepoint=!args.types?.length||args.types.includes("tracepoint"),clearLogpoint=!args.types?.length||args.types.includes("logpoint"),clearExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointCleared=0,logpointCleared=0,exceptionpointCleared=0;if(!isDebuggingEnabled2(context.browserContext))return{tracepointCleared:0,logpointCleared:0,exceptionpointCleared:0,message:"No snapshots to clear"};if(args.probeId&&(clearTracepoint||clearLogpoint)){let n=clearSnapshotsByProbe(context.browserContext,args.probeId);if(clearTracepoint&&clearLogpoint){let p=listProbes2(context.browserContext).find(x=>x.id===args.probeId);p?.kind==="tracepoint"?tracepointCleared=n:p?.kind==="logpoint"&&(logpointCleared=n)}else clearTracepoint?tracepointCleared=n:logpointCleared=n}else{if(clearTracepoint){let ids=listProbes2(context.browserContext).filter(p=>p.kind==="tracepoint").map(p=>p.id);for(let id of ids)tracepointCleared+=clearSnapshotsByProbe(context.browserContext,id)}if(clearLogpoint){let ids=listProbes2(context.browserContext).filter(p=>p.kind==="logpoint").map(p=>p.id);for(let id of ids)logpointCleared+=clearSnapshotsByProbe(context.browserContext,id)}clearExceptionpoint&&(exceptionpointCleared=clearSnapshotsByProbe(context.browserContext,"__exception__"))}let parts=[tracepointCleared&&`${tracepointCleared} tracepoint snapshot(s)`,logpointCleared&&`${logpointCleared} logpoint snapshot(s)`,exceptionpointCleared&&`${exceptionpointCleared} exceptionpoint snapshot(s)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointCleared,logpointCleared,exceptionpointCleared,message}}};import{z as z18}from"zod";var AddWatch=class{name(){return"debug_add-watch"}description(){return`
312
+ `}inputSchema(){return{state:z14.enum(["none","uncaught","all"])}}outputSchema(){return{previousState:z14.string().describe("Previous state"),currentState:z14.string().describe("Current state"),message:z14.string().describe("Status message")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let previousState=getExceptionBreakpoint2(context.browserContext);await setExceptionBreakpoint2(context.browserContext,args.state);let currentState=getExceptionBreakpoint2(context.browserContext);return{previousState,currentState,message:`Exception tracepoint set to: ${currentState}`}}};import{z as z16}from"zod";import{z as z15}from"zod";var OriginalLocationSchema=z15.object({source:z15.string().describe("Original source file path"),line:z15.number().describe("1-based line number"),column:z15.number().optional().describe("1-based column number"),name:z15.string().optional().describe("Original identifier name")}),ScopeVariableSchema=z15.object({name:z15.string().describe("Variable name"),value:z15.any().describe("Variable value"),type:z15.string().describe("Variable type")}),ScopeSnapshotSchema=z15.object({type:z15.string().describe("Scope type (global, local, closure, etc.)"),name:z15.string().optional().describe("Scope name"),variables:z15.array(ScopeVariableSchema).describe("Variables in this scope")}),CallFrameSchema=z15.object({functionName:z15.string().describe("Function name"),url:z15.string().describe("Script URL"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),scriptId:z15.string().describe("V8 script ID"),scopes:z15.array(ScopeSnapshotSchema).describe("Variable scopes"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncCallFrameSchema=z15.object({functionName:z15.string().describe("Function name"),url:z15.string().describe("Script URL"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncStackSegmentSchema=z15.object({description:z15.string().optional().describe("Async boundary (Promise.then, setTimeout, etc.)"),callFrames:z15.array(AsyncCallFrameSchema).describe("Frames in this segment")}),AsyncStackTraceSchema=z15.object({segments:z15.array(AsyncStackSegmentSchema).describe("Chain of async segments")}),SnapshotBaseSchema=z15.object({id:z15.string().describe("Snapshot ID"),probeId:z15.string().describe("Probe ID that triggered this snapshot"),timestamp:z15.number().describe("Unix timestamp (ms)"),sequenceNumber:z15.number().describe("Monotonic sequence number for ordering"),url:z15.string().describe("Script URL where snapshot was taken"),lineNumber:z15.number().describe("1-based line number"),columnNumber:z15.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location"),captureTimeMs:z15.number().describe("Time taken to capture snapshot (ms)")}),TracepointSnapshotSchema=SnapshotBaseSchema.extend({callStack:z15.array(CallFrameSchema).describe("Call stack with local variables"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z15.record(z15.any()).optional().describe("Watch expression results")}),LogpointSnapshotSchema=SnapshotBaseSchema.extend({logResult:z15.any().optional().describe("Result of log expression evaluation")}),ExceptionInfoSchema=z15.object({type:z15.enum(["exception","promiseRejection"]).describe("Exception type"),message:z15.string().describe("Exception message"),name:z15.string().optional().describe("Exception name/class"),stack:z15.string().optional().describe("Stack trace string")}),ExceptionpointSnapshotSchema=SnapshotBaseSchema.extend({exception:ExceptionInfoSchema.optional().describe("Exception information"),callStack:z15.array(CallFrameSchema).describe("Call stack at exception"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z15.record(z15.any()).optional().describe("Watch expression results")});var DEFAULT_TRIM_OPTIONS={maxCallStackDepth:5,includeScopes:["local","closure"],maxVariablesPerScope:20};function _trimCallStack(callStack,opts){return callStack.slice(0,opts.maxCallStackDepth).map(frame=>{let scopes=frame.scopes.filter(s=>opts.includeScopes.includes(s.type)).map(s=>{let vars=s.variables.slice(0,opts.maxVariablesPerScope);return{type:s.type,name:s.name,variables:vars}});return{...frame,scopes}})}function trimSnapshot(snapshot,opts){if(!snapshot.callStack?.length)return snapshot;let merged={...DEFAULT_TRIM_OPTIONS,...opts};return{...snapshot,callStack:_trimCallStack(snapshot.callStack,merged)}}function trimSnapshots(snapshots,opts){let merged={...DEFAULT_TRIM_OPTIONS,...opts};return snapshots.map(s=>trimSnapshot(s,merged))}var SNAPSHOT_TYPES=["tracepoint","logpoint","exceptionpoint"],SCOPE_TYPE_VALUES=["global","local","with","closure","catch","block","script","eval","module","wasm-expression-stack"],BROWSER_DEFAULT_INCLUDE_SCOPES=["local"];function applyFilters(snapshots,fromSequence,limit){let out=snapshots;return fromSequence!==void 0&&(out=out.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&out.length>limit&&(out=out.slice(-limit)),out}var GetProbeSnapshots=class{name(){return"debug_get-probe-snapshots"}description(){return`
313
+ Retrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.
314
+ Optional \`types\`: array of \`tracepoint\`, \`logpoint\`, \`exceptionpoint\`. If omitted or empty, returns all.
315
+ Response fields: \`tracepointSnapshots\`, \`logpointSnapshots\`, \`exceptionpointSnapshots\`.
316
+ Optional \`probeId\` filters tracepoint or logpoint snapshots; \`fromSequence\` and \`limit\` apply per type.
317
+ Output trimming: by default only the top ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth} call stack frames are returned, only \`${BROWSER_DEFAULT_INCLUDE_SCOPES.join(", ")}\` scope(s) are included, and variables per scope are capped at ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}. Override with maxCallStackDepth, includeScopes, maxVariablesPerScope.
318
+ `.trim()}inputSchema(){return{types:z16.array(z16.enum(SNAPSHOT_TYPES)).optional().describe("Return only these types; omit for all."),probeId:z16.string().optional().describe("Filter by this probe ID (tracepoint/logpoint)."),fromSequence:z16.number().int().nonnegative().optional().describe("Snapshots with sequence > this (polling)."),limit:z16.number().int().positive().optional(),maxCallStackDepth:z16.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxCallStackDepth).describe(`Max call stack frames per snapshot. Default ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth}.`),includeScopes:z16.array(z16.enum(SCOPE_TYPE_VALUES)).optional().default([...BROWSER_DEFAULT_INCLUDE_SCOPES]).describe(`Scope types to include. Default [${BROWSER_DEFAULT_INCLUDE_SCOPES.join(", ")}] (local only to keep payload small).`),maxVariablesPerScope:z16.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxVariablesPerScope).describe(`Max variables per scope. Default ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}.`)}}outputSchema(){return{tracepointSnapshots:z16.array(TracepointSnapshotSchema).describe("Tracepoint snapshots"),logpointSnapshots:z16.array(LogpointSnapshotSchema).describe("Logpoint snapshots"),exceptionpointSnapshots:z16.array(ExceptionpointSnapshotSchema).describe("Exceptionpoint snapshots")}}async handle(context,args){let getTracepoint=!args.types?.length||args.types.includes("tracepoint"),getLogpoint=!args.types?.length||args.types.includes("logpoint"),getExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),trimOpts={maxCallStackDepth:args.maxCallStackDepth,includeScopes:args.includeScopes,maxVariablesPerScope:args.maxVariablesPerScope},tracepointSnapshots=[],logpointSnapshots=[],exceptionpointSnapshots=[];if(!isDebuggingEnabled2(context.browserContext))return{tracepointSnapshots,logpointSnapshots,exceptionpointSnapshots};let allSnapshots=getSnapshots2(context.browserContext),probes=listProbes2(context.browserContext);if(args.probeId&&(getTracepoint||getLogpoint)){let raw=getSnapshotsByProbe2(context.browserContext,args.probeId),filtered=applyFilters(raw,args.fromSequence,args.limit),probe=probes.find(p=>p.id===args.probeId);probe?.kind==="tracepoint"&&getTracepoint?tracepointSnapshots=trimSnapshots(filtered,trimOpts):probe?.kind==="logpoint"&&getLogpoint&&(logpointSnapshots=filtered.map(s=>({id:s.id,probeId:s.probeId,timestamp:s.timestamp,sequenceNumber:s.sequenceNumber,url:s.url,lineNumber:s.lineNumber,columnNumber:s.columnNumber,originalLocation:s.originalLocation,captureTimeMs:s.captureTimeMs,logResult:s.logResult})))}else{if(getTracepoint){let tracepointIds=new Set(probes.filter(p=>p.kind==="tracepoint").map(p=>p.id)),raw=allSnapshots.filter(s=>tracepointIds.has(s.probeId)),filtered=applyFilters(raw,args.fromSequence,args.limit);tracepointSnapshots=trimSnapshots(filtered,trimOpts)}if(getLogpoint){let logpointIds=new Set(probes.filter(p=>p.kind==="logpoint").map(p=>p.id)),raw=allSnapshots.filter(s=>logpointIds.has(s.probeId));logpointSnapshots=applyFilters(raw,args.fromSequence,args.limit).map(s=>({id:s.id,probeId:s.probeId,timestamp:s.timestamp,sequenceNumber:s.sequenceNumber,url:s.url,lineNumber:s.lineNumber,columnNumber:s.columnNumber,originalLocation:s.originalLocation,captureTimeMs:s.captureTimeMs,logResult:s.logResult}))}}if(getExceptionpoint){let raw=getSnapshotsByProbe2(context.browserContext,"__exception__"),filtered=applyFilters(raw,args.fromSequence,args.limit);exceptionpointSnapshots=trimSnapshots(filtered,trimOpts)}return{tracepointSnapshots,logpointSnapshots,exceptionpointSnapshots}}};import{z as z17}from"zod";var SNAPSHOT_TYPES2=["tracepoint","logpoint","exceptionpoint"],ClearProbeSnapshots=class{name(){return"debug_clear-probe-snapshots"}description(){return"\nClears snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, clears all.\nOptional `probeId`: clear only snapshots for this probe (for tracepoint/logpoint).\n ".trim()}inputSchema(){return{types:z17.array(z17.enum(SNAPSHOT_TYPES2)).optional().describe("Clear only these types; omit for all."),probeId:z17.string().optional().describe("Clear only this probe (tracepoint/logpoint).")}}outputSchema(){return{tracepointCleared:z17.number().describe("Tracepoint snapshots cleared"),logpointCleared:z17.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z17.number().describe("Exceptionpoint snapshots cleared"),message:z17.string().describe("Status message")}}async handle(context,args){let clearTracepoint=!args.types?.length||args.types.includes("tracepoint"),clearLogpoint=!args.types?.length||args.types.includes("logpoint"),clearExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointCleared=0,logpointCleared=0,exceptionpointCleared=0;if(!isDebuggingEnabled2(context.browserContext))return{tracepointCleared:0,logpointCleared:0,exceptionpointCleared:0,message:"No snapshots to clear"};if(args.probeId&&(clearTracepoint||clearLogpoint)){let n=clearSnapshotsByProbe(context.browserContext,args.probeId);if(clearTracepoint&&clearLogpoint){let p=listProbes2(context.browserContext).find(x=>x.id===args.probeId);p?.kind==="tracepoint"?tracepointCleared=n:p?.kind==="logpoint"&&(logpointCleared=n)}else clearTracepoint?tracepointCleared=n:logpointCleared=n}else{if(clearTracepoint){let ids=listProbes2(context.browserContext).filter(p=>p.kind==="tracepoint").map(p=>p.id);for(let id of ids)tracepointCleared+=clearSnapshotsByProbe(context.browserContext,id)}if(clearLogpoint){let ids=listProbes2(context.browserContext).filter(p=>p.kind==="logpoint").map(p=>p.id);for(let id of ids)logpointCleared+=clearSnapshotsByProbe(context.browserContext,id)}clearExceptionpoint&&(exceptionpointCleared=clearSnapshotsByProbe(context.browserContext,"__exception__"))}let parts=[tracepointCleared&&`${tracepointCleared} tracepoint snapshot(s)`,logpointCleared&&`${logpointCleared} logpoint snapshot(s)`,exceptionpointCleared&&`${exceptionpointCleared} exceptionpoint snapshot(s)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointCleared,logpointCleared,exceptionpointCleared,message}}};import{z as z18}from"zod";var AddWatch=class{name(){return"debug_add-watch"}description(){return`
314
319
  Adds a watch expression to be evaluated at every breakpoint hit.
315
320
  Watch expression results are included in the snapshot's watchResults field.
316
321
 
@@ -401,7 +406,7 @@ How to use it effectively:
401
406
  - Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration (e.g. AWS_REGION, inference profile, etc).
402
407
 
403
408
  This tool is designed for UI regression checks, design parity checks, and "does this page still match the intended layout?" validation.
404
- `.trim()}isEnabled(){return!!FIGMA_ACCESS_TOKEN?.trim()}inputSchema(){return{figmaFileKey:z19.string().min(1).describe("Figma file key (from URL: part after /file/)."),figmaNodeId:z19.string().min(1).describe("Figma node id (frame/component, e.g. 12:34)."),selector:z19.string().optional().describe("Compare only this region; omit for full page."),fullPage:z19.boolean().optional().default(DEFAULT_FULL_PAGE).describe("Full scrollable page; ignored when selector set."),figmaScale:z19.number().int().positive().optional().describe("Scale for Figma raster (e.g. 1, 2)."),figmaFormat:z19.enum(["png","jpg"]).optional(),weights:z19.object({mssim:z19.number().positive().optional(),imageEmbedding:z19.number().positive().optional(),textEmbedding:z19.number().positive().optional()}).optional().describe("Weights for combining signals."),mssimMode:z19.enum(["raw","semantic"]).optional().default(DEFAULT_MSSIM_MODE).describe("semantic = more robust for real data vs design."),maxDim:z19.number().int().positive().optional().describe("Max dimension for comparison."),jpegQuality:z19.number().int().min(50).max(100).optional().describe("JPEG quality 50\u2013100.")}}outputSchema(){return{score:z19.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:z19.array(z19.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:z19.object({pageUrl:z19.string().describe("URL of the page that was compared."),pageTitle:z19.string().describe("Title of the page that was compared."),figmaFileKey:z19.string().describe("Figma file key used for the design snapshot."),figmaNodeId:z19.string().describe("Figma node id used for the design snapshot."),selector:z19.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:z19.boolean().describe("Whether the page screenshot was full-page."),pageImageType:z19.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:z19.enum(["png","jpeg"]).describe("Image type of the captured Figma snapshot.")}).describe("Metadata about what was compared.")}}async handle(context,args){let pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),figmaFormat=args.figmaFormat??"png",figmaScale=typeof args.figmaScale=="number"?args.figmaScale:void 0,figmaSnapshot=await getFigmaDesignScreenshot({fileKey:args.figmaFileKey,nodeId:args.figmaNodeId,format:figmaFormat,scale:figmaScale}),pagePng;if(typeof args.selector=="string"&&args.selector.trim()){let selector=args.selector.trim(),locator=context.page.locator(selector);if(await locator.count()===0)throw new Error(`Element not found for selector: ${selector}`);pagePng=await locator.first().screenshot({type:DEFAULT_SCREENSHOT_TYPE2})}else{let fullPage=args.fullPage!==!1;pagePng=await context.page.screenshot({type:DEFAULT_SCREENSHOT_TYPE2,fullPage})}let pageSs={image:pagePng,type:"png",name:"page"},figmaSs={image:figmaSnapshot.image,type:figmaSnapshot.type==="jpeg"?"jpeg":"png",name:"figma"},result=await compareWithNotes(pageSs,figmaSs,{weights:args.weights?{mssim:args.weights.mssim,vectorEmbedding:args.weights.imageEmbedding,textEmbedding:args.weights.textEmbedding}:void 0,mssim:{mode:args.mssimMode??DEFAULT_MSSIM_MODE},imageEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0},textEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0}});return{score:result.score,notes:result.notes,meta:{pageUrl,pageTitle,figmaFileKey:args.figmaFileKey,figmaNodeId:args.figmaNodeId,selector:typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():null,fullPage:typeof args.selector=="string"&&args.selector.trim()?!1:args.fullPage!==!1,pageImageType:"png",figmaImageType:figmaSnapshot.type}}}};var tools4=[new ComparePageWithDesign];import{z as z20}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS=1e4,Click=class{name(){return"interaction_click"}description(){return"Clicks an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z20.string().describe("CSS selector or ref from a11y snapshot (e.g. e1, @e1)."),timeoutMs:z20.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"click","Use a ref for an interactive element (button, link, etc.) from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS;return await resolveSelectorOrRef(context,args.selector).click({timeout}),{}}};import{z as z21}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS2=1e4,Drag=class{name(){return"interaction_drag"}description(){return"Drags an element to a target location. Accepts CSS selectors or refs (e.g. e1, @e1) from the last ARIA snapshot."}inputSchema(){return{sourceSelector:z21.string().describe("CSS selector or ref for the element to drag."),targetSelector:z21.string().describe("CSS selector or ref for the drop target."),timeoutMs:z21.number().int().positive().optional().describe("Wait for elements, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.sourceSelector,REF_ROLES_INTERACTIVE,"drag (source)","Use a ref for a draggable element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS2,sourceLocator=resolveSelectorOrRef(context,args.sourceSelector),targetLocator=resolveSelectorOrRef(context,args.targetSelector),sourceBound=await sourceLocator.boundingBox({timeout}),targetBound=await targetLocator.boundingBox({timeout});if(!sourceBound||!targetBound)throw new Error("Could not get element positions for drag operation");return await context.page.mouse.move(sourceBound.x+sourceBound.width/2,sourceBound.y+sourceBound.height/2),await context.page.mouse.down(),await context.page.mouse.move(targetBound.x+targetBound.width/2,targetBound.y+targetBound.height/2),await context.page.mouse.up(),{}}};import{z as z22}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS3=1e4,FILLABLE_ROLES=new Set(["textbox","searchbox","spinbutton","combobox"]),Fill=class{name(){return"interaction_fill"}description(){return"Fills out an input field. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z22.string().describe("CSS selector or ref from a11y snapshot for the input."),value:z22.string(),timeoutMs:z22.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,FILLABLE_ROLES,"fill","Use a ref for a textbox, searchbox, or input (e.g. from the latest a11y_take-aria-snapshot on this page). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS3;return await resolveSelectorOrRef(context,args.selector).fill(args.value,{timeout}),{}}};import{z as z23}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS4=1e4,Hover=class{name(){return"interaction_hover"}description(){return"Hovers an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z23.string().describe("CSS selector or ref from a11y snapshot."),timeoutMs:z23.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"hover","Use a ref for an interactive element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS4;return await resolveSelectorOrRef(context,args.selector).hover({timeout}),{}}};import{z as z24}from"zod";var DEFAULT_REPEAT_INTERVAL_MS=50,MIN_REPEAT_INTERVAL_MS=10,DEFAULT_SELECTOR_TIMEOUT_MS5=1e4,PressKey=class{name(){return"interaction_press-key"}description(){return`
409
+ `.trim()}isEnabled(){return!!FIGMA_ACCESS_TOKEN?.trim()}inputSchema(){return{figmaFileKey:z19.string().min(1).describe("Figma file key (from URL: part after /file/)."),figmaNodeId:z19.string().min(1).describe("Figma node id (frame/component, e.g. 12:34)."),selector:z19.string().optional().describe("Compare only this region; omit for full page."),fullPage:z19.boolean().optional().default(DEFAULT_FULL_PAGE).describe("Full scrollable page; ignored when selector set."),figmaScale:z19.number().int().positive().optional().describe("Scale for Figma raster (e.g. 1, 2)."),figmaFormat:z19.enum(["png","jpg"]).optional(),weights:z19.object({mssim:z19.number().positive().optional(),imageEmbedding:z19.number().positive().optional(),textEmbedding:z19.number().positive().optional()}).optional().describe("Weights for combining signals."),mssimMode:z19.enum(["raw","semantic"]).optional().default(DEFAULT_MSSIM_MODE).describe("semantic = more robust for real data vs design."),maxDim:z19.number().int().positive().optional().describe("Max dimension for comparison."),jpegQuality:z19.number().int().min(50).max(100).optional().describe("JPEG quality 50\u2013100.")}}outputSchema(){return{score:z19.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:z19.array(z19.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:z19.object({pageUrl:z19.string().describe("URL of the page that was compared."),pageTitle:z19.string().describe("Title of the page that was compared."),figmaFileKey:z19.string().describe("Figma file key used for the design snapshot."),figmaNodeId:z19.string().describe("Figma node id used for the design snapshot."),selector:z19.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:z19.boolean().describe("Whether the page screenshot was full-page."),pageImageType:z19.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:z19.enum(["png","jpeg"]).describe("Image type of the captured Figma snapshot.")}).describe("Metadata about what was compared.")}}async handle(context,args){let pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),figmaFormat=args.figmaFormat??"png",figmaScale=typeof args.figmaScale=="number"?args.figmaScale:void 0,figmaSnapshot=await getFigmaDesignScreenshot({fileKey:args.figmaFileKey,nodeId:args.figmaNodeId,format:figmaFormat,scale:figmaScale}),pagePng;if(typeof args.selector=="string"&&args.selector.trim()){let selector=args.selector.trim(),locator=context.page.locator(selector);if(await locator.count()===0)throw new Error(`Element not found for selector: ${selector}`);pagePng=await locator.first().screenshot({type:DEFAULT_SCREENSHOT_TYPE2})}else{let fullPage=args.fullPage!==!1;pagePng=await context.page.screenshot({type:DEFAULT_SCREENSHOT_TYPE2,fullPage})}let pageSs={image:pagePng,type:"png",name:"page"},figmaSs={image:figmaSnapshot.image,type:figmaSnapshot.type==="jpeg"?"jpeg":"png",name:"figma"},result=await compareWithNotes(pageSs,figmaSs,{weights:args.weights?{mssim:args.weights.mssim,vectorEmbedding:args.weights.imageEmbedding,textEmbedding:args.weights.textEmbedding}:void 0,mssim:{mode:args.mssimMode??DEFAULT_MSSIM_MODE},imageEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0},textEmbedding:{maxDim:args.maxDim,jpegQuality:typeof args.jpegQuality=="number"?args.jpegQuality:void 0}});return{score:result.score,notes:result.notes,meta:{pageUrl,pageTitle,figmaFileKey:args.figmaFileKey,figmaNodeId:args.figmaNodeId,selector:typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():null,fullPage:typeof args.selector=="string"&&args.selector.trim()?!1:args.fullPage!==!1,pageImageType:"png",figmaImageType:figmaSnapshot.type}}}};var tools4=[new ComparePageWithDesign];async function waitForNavigationAndNetworkIdle(context,navPromise,triggerPromise,options){let timeoutMs=options.timeoutMs,deadlineMs=Date.now()+timeoutMs;try{await Promise.all([navPromise,triggerPromise])}catch(err){let message=err instanceof Error?err.message:String(err);if(/timeout|Timeout|TIMEOUT/.test(message)||message.includes("Navigation")){let hint=options.timeoutHint??"Ensure the action triggers a page load (e.g. link or submit).";throw new Error(`Navigation timed out (${timeoutMs}ms). ${hint} Original: ${message}`)}throw err}let stabilizationMs=Math.min(150,Math.max(0,deadlineMs-Date.now()));stabilizationMs>0&&await new Promise(resolve=>{setTimeout(resolve,stabilizationMs)});let lastNotIdleMs=Date.now();for(;;){let nowMs=Date.now();if(context.numOfInFlightRequests()>0&&(lastNotIdleMs=nowMs),nowMs-lastNotIdleMs>=500||nowMs>=deadlineMs)break;await new Promise(resolve=>{setTimeout(resolve,50)})}}async function waitForNetworkIdle(context,options){let timeoutMs=options.timeoutMs,deadlineMs=Date.now()+timeoutMs,stabilizationMs=Math.min(150,Math.max(0,deadlineMs-Date.now()));stabilizationMs>0&&await new Promise(resolve=>{setTimeout(resolve,stabilizationMs)});let lastNotIdleMs=Date.now();for(;;){let nowMs=Date.now();if(context.numOfInFlightRequests()>0&&(lastNotIdleMs=nowMs),nowMs-lastNotIdleMs>=500||nowMs>=deadlineMs)break;await new Promise(resolve=>{setTimeout(resolve,50)})}}import{z as z20}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS=1e4,DEFAULT_WAIT_FOR_TIMEOUT_MS=3e4,Click=class{name(){return"interaction_click"}description(){return"Clicks an element. Accepts selector or ref (e.g. e1, @e1). Set waitForNavigation: true when the click opens a new page \u2014 waits for navigation then for network idle so snapshot/screenshot see full content."}inputSchema(){return{selector:z20.string().describe("CSS selector or ref from a11y snapshot (e.g. e1, @e1)."),timeoutMs:z20.number().int().positive().optional().describe("Wait for element, ms. Default 10000."),waitForNavigation:z20.boolean().optional().default(!1).describe("Wait for navigation triggered by click (parallel with click). Use when click opens a new page."),waitForTimeoutMs:z20.number().int().min(0).optional().default(DEFAULT_WAIT_FOR_TIMEOUT_MS).describe("Timeout for navigation and for network idle wait (ms). Only when waitForNavigation is true. Default 30000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"click","Use a ref for an interactive element (button, link, etc.) from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS,locator=resolveSelectorOrRef(context,args.selector);if(args.waitForNavigation){let waitForTimeoutMs=args.waitForTimeoutMs??DEFAULT_WAIT_FOR_TIMEOUT_MS,navPromise=context.page.waitForNavigation({waitUntil:"load",timeout:waitForTimeoutMs}),triggerPromise=locator.click({timeout});await waitForNavigationAndNetworkIdle(context,navPromise,triggerPromise,{timeoutMs:waitForTimeoutMs,timeoutHint:"Ensure the click triggers a page load (e.g. link or submit)."})}else await locator.click({timeout});return{}}};import{z as z21}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS2=1e4,Drag=class{name(){return"interaction_drag"}description(){return"Drags an element to a target location. Accepts CSS selectors or refs (e.g. e1, @e1) from the last ARIA snapshot."}inputSchema(){return{sourceSelector:z21.string().describe("CSS selector or ref for the element to drag."),targetSelector:z21.string().describe("CSS selector or ref for the drop target."),timeoutMs:z21.number().int().positive().optional().describe("Wait for elements, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.sourceSelector,REF_ROLES_INTERACTIVE,"drag (source)","Use a ref for a draggable element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS2,sourceLocator=resolveSelectorOrRef(context,args.sourceSelector),targetLocator=resolveSelectorOrRef(context,args.targetSelector),sourceBound=await sourceLocator.boundingBox({timeout}),targetBound=await targetLocator.boundingBox({timeout});if(!sourceBound||!targetBound)throw new Error("Could not get element positions for drag operation");return await context.page.mouse.move(sourceBound.x+sourceBound.width/2,sourceBound.y+sourceBound.height/2),await context.page.mouse.down(),await context.page.mouse.move(targetBound.x+targetBound.width/2,targetBound.y+targetBound.height/2),await context.page.mouse.up(),{}}};import{z as z22}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS3=1e4,FILLABLE_ROLES=new Set(["textbox","searchbox","spinbutton","combobox"]),Fill=class{name(){return"interaction_fill"}description(){return"Fills out an input field. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z22.string().describe("CSS selector or ref from a11y snapshot for the input."),value:z22.string(),timeoutMs:z22.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,FILLABLE_ROLES,"fill","Use a ref for a textbox, searchbox, or input (e.g. from the latest a11y_take-aria-snapshot on this page). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS3;return await resolveSelectorOrRef(context,args.selector).fill(args.value,{timeout}),{}}};import{z as z23}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS4=1e4,Hover=class{name(){return"interaction_hover"}description(){return"Hovers an element on the page. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z23.string().describe("CSS selector or ref from a11y snapshot."),timeoutMs:z23.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,REF_ROLES_INTERACTIVE,"hover","Use a ref for an interactive element from the latest a11y_take-aria-snapshot. Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS4;return await resolveSelectorOrRef(context,args.selector).hover({timeout}),{}}};import{z as z24}from"zod";var DEFAULT_REPEAT_INTERVAL_MS=50,MIN_REPEAT_INTERVAL_MS=10,DEFAULT_SELECTOR_TIMEOUT_MS5=1e4,PressKey=class{name(){return"interaction_press-key"}description(){return`
405
410
  Presses a keyboard key with optional "hold" and auto-repeat behavior.
406
411
 
407
412
  Key facts:
@@ -454,20 +459,20 @@ Use this tool to:
454
459
  - Jump to the top/bottom without knowing exact positions
455
460
  - Bring elements into view before clicking
456
461
  - Inspect lazy-loaded content that appears on scroll
457
- `.trim()}inputSchema(){return{mode:z28.enum(["by","to","top","bottom","left","right"]).optional().default(DEFAULT_MODE).describe("by=dx,dy; to=x,y; or edge (top/bottom/left/right)."),selector:z28.string().optional().describe("Scroll this container; omit for viewport."),dx:z28.number().optional(),dy:z28.number().optional(),x:z28.number().optional(),y:z28.number().optional(),behavior:z28.enum(["auto","smooth"]).optional().default(DEFAULT_BEHAVIOR)}}outputSchema(){return{mode:z28.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:z28.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:z28.enum(["auto","smooth"]).describe("The scroll behavior used."),before:z28.object({x:z28.number().describe("ScrollLeft before scrolling."),y:z28.number().describe("ScrollTop before scrolling."),scrollWidth:z28.number().describe("Total scrollable width before scrolling."),scrollHeight:z28.number().describe("Total scrollable height before scrolling."),clientWidth:z28.number().describe("Viewport/container client width before scrolling."),clientHeight:z28.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:z28.object({x:z28.number().describe("ScrollLeft after scrolling."),y:z28.number().describe("ScrollTop after scrolling."),scrollWidth:z28.number().describe("Total scrollable width after scrolling."),scrollHeight:z28.number().describe("Total scrollable height after scrolling."),clientWidth:z28.number().describe("Viewport/container client width after scrolling."),clientHeight:z28.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:z28.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:z28.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:z28.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:z28.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:z28.boolean().describe("Whether the scroll position is at the far left."),isAtRight:z28.boolean().describe("Whether the scroll position is at the far right."),isAtTop:z28.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:z28.boolean().describe("Whether the scroll position is at the very bottom.")}}async handle(context,args){let mode=args.mode??DEFAULT_MODE,selector=args.selector,behavior=args.behavior??DEFAULT_BEHAVIOR,dx=args.dx??0,dy=args.dy??0,x=args.x,y=args.y;if(mode==="to"&&typeof x!="number"&&typeof y!="number")throw new Error('mode="to" requires at least one of x or y.');if(mode==="by"&&dx===0&&dy===0)throw new Error('mode="by" requires dx and/or dy to be non-zero.');let params={modeEval:mode,selectorEval:selector,dxEval:dx,dyEval:dy,xEval:x,yEval:y,behaviorEval:behavior},result;return selector?result=await resolveSelectorOrRef(context,selector).evaluate((el,p)=>{let before={x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight},readMetrics=elem2=>({x:elem2.scrollLeft,y:elem2.scrollTop,scrollWidth:elem2.scrollWidth,scrollHeight:elem2.scrollHeight,clientWidth:elem2.clientWidth,clientHeight:elem2.clientHeight}),clamp=(v,min,max)=>Math.max(min,Math.min(max,v)),elem=el,maxX=Math.max(0,elem.scrollWidth-elem.clientWidth),maxY=Math.max(0,elem.scrollHeight-elem.clientHeight);p.modeEval==="by"?elem.scrollTo({left:clamp(elem.scrollLeft+p.dxEval,0,maxX),top:clamp(elem.scrollTop+p.dyEval,0,maxY),behavior:p.behaviorEval}):p.modeEval==="to"?elem.scrollTo({left:typeof p.xEval=="number"?clamp(p.xEval,0,maxX):elem.scrollLeft,top:typeof p.yEval=="number"?clamp(p.yEval,0,maxY):elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="top"?elem.scrollTo({top:0,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="bottom"?elem.scrollTo({top:maxY,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="left"?elem.scrollTo({left:0,top:elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="right"&&elem.scrollTo({left:maxX,top:elem.scrollTop,behavior:p.behaviorEval});let after=readMetrics(elem);return{before,after,canScrollX:after.scrollWidth>after.clientWidth,canScrollY:after.scrollHeight>after.clientHeight,maxScrollX:Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY:Math.max(0,after.scrollHeight-after.clientHeight),isAtLeft:after.x<=1,isAtRight:after.x>=Math.max(0,after.scrollWidth-after.clientWidth)-1,isAtTop:after.y<=1,isAtBottom:after.y>=Math.max(0,after.scrollHeight-after.clientHeight)-1}},params):result=await context.page.evaluate(params2=>{let modeEval=params2.modeEval,selectorEval=params2.selectorEval,dxEval=params2.dxEval,dyEval=params2.dyEval,xEval=params2.xEval,yEval=params2.yEval,behaviorEval=params2.behaviorEval,getTarget=()=>{if(selectorEval){let el=document.querySelector(selectorEval);if(!el)throw new Error(`Element with selector "${selectorEval}" not found`);return el}let scrolling=document.scrollingElement||document.documentElement||document.body;if(!scrolling)throw new Error("No scrolling element available.");return scrolling},readMetrics=el=>({x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight}),clamp=(v,min,max)=>v<min?min:v>max?max:v,doScroll=el=>{let maxX=Math.max(0,el.scrollWidth-el.clientWidth),maxY=Math.max(0,el.scrollHeight-el.clientHeight);if(modeEval==="by"){let nextX=clamp(el.scrollLeft+dxEval,0,maxX),nextY=clamp(el.scrollTop+dyEval,0,maxY);el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="to"){let nextX=typeof xEval=="number"?clamp(xEval,0,maxX):el.scrollLeft,nextY=typeof yEval=="number"?clamp(yEval,0,maxY):el.scrollTop;el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="top"){el.scrollTo({top:0,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="bottom"){el.scrollTo({top:maxY,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="left"){el.scrollTo({left:0,top:el.scrollTop,behavior:behaviorEval});return}if(modeEval==="right"){el.scrollTo({left:maxX,top:el.scrollTop,behavior:behaviorEval});return}},target=getTarget(),before=readMetrics(target);doScroll(target);let after=readMetrics(target),maxScrollX=Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY=Math.max(0,after.scrollHeight-after.clientHeight),canScrollX=after.scrollWidth>after.clientWidth,canScrollY=after.scrollHeight>after.clientHeight,eps=1,isAtLeft=after.x<=eps,isAtRight=after.x>=maxScrollX-eps,isAtTop=after.y<=eps,isAtBottom=after.y>=maxScrollY-eps;return{before,after,canScrollX,canScrollY,maxScrollX,maxScrollY,isAtLeft,isAtRight,isAtTop,isAtBottom}},params),{mode,selector:selector??null,behavior,before:result.before,after:result.after,canScrollX:result.canScrollX,canScrollY:result.canScrollY,maxScrollX:result.maxScrollX,maxScrollY:result.maxScrollY,isAtLeft:result.isAtLeft,isAtRight:result.isAtRight,isAtTop:result.isAtTop,isAtBottom:result.isAtBottom}}};var tools5=[new Click,new Drag,new Fill,new Hover,new PressKey,new ResizeViewport,new ResizeWindow,new Select,new Scroll];import fs4 from"node:fs/promises";import os3 from"node:os";import path4 from"node:path";import{z as z29}from"zod";var DEFAULT_SCREENSHOT_NAME2="screenshot",DEFAULT_TIMEOUT_MS=0,DEFAULT_WAIT_UNTIL="load",DIRECTION_VALUES=["back","forward"],refMapEntrySchema2=z29.object({role:z29.string(),name:z29.string().optional(),selector:z29.string(),nth:z29.number().optional()}),GoBackOrForward=class{name(){return"navigation_go-back-or-forward"}description(){return'\nNavigates to the previous or next page in history.\n- `direction: "back"` \u2014 previous page in history.\n- `direction: "forward"` \u2014 next page in history.\n\nIn case of multiple redirects, the navigation will resolve with the response of the last redirect.\nIf cannot go back/forward, returns empty response.\n\nBy default (includeSnapshot: true), an ARIA snapshot with refs is returned. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false), same as a11y_take-aria-snapshot.\n\nWhen `includeScreenshot: true`, the screenshot is always saved to disk; `screenshotFilePath` is returned. By default `outputPath` is the OS temp dir and `name` is "screenshot" (same as `content_take-screenshot`). Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the returned path (e.g. remote, container).\n '}inputSchema(){return{direction:z29.enum(DIRECTION_VALUES),timeout:z29.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS).describe("Max wait ms. 0=no timeout."),waitUntil:z29.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL).describe("Resolve when this load event fires."),includeSnapshot:z29.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z29.object({interactiveOnly:z29.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z29.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z29.boolean().optional().default(!1).describe("Take a screenshot after navigation; saved to disk (default: OS temp dir). Use includeBase64 only when file cannot be read from path."),screenshotOptions:z29.object({outputPath:z29.string().optional().default(os3.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z29.string().optional().default(DEFAULT_SCREENSHOT_NAME2).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z29.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z29.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z29.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z29.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z29.string().describe("Contains the URL of the navigated page.").optional(),status:z29.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z29.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z29.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z29.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z29.string().optional(),refs:z29.record(z29.string(),refMapEntrySchema2).optional(),image:z29.object({data:z29.any().describe("Base64-encoded image data."),mimeType:z29.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=args.direction==="back"?await context.page.goBack({timeout:args.timeout,waitUntil:args.waitUntil}):await context.page.goForward({timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os3.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME2;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path4.resolve(outputPath,filename);await fs4.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs5 from"node:fs/promises";import os4 from"node:os";import path5 from"node:path";import{z as z30}from"zod";var DEFAULT_SCREENSHOT_NAME3="screenshot",DEFAULT_TIMEOUT_MS2=0,DEFAULT_WAIT_UNTIL2="load",refMapEntrySchema3=z30.object({role:z30.string(),name:z30.string().optional(),selector:z30.string(),nth:z30.number().optional()}),GoTo=class{name(){return"navigation_go-to"}description(){return'\nNavigates to the given URL.\n**NOTE**: The tool either throws an error or returns a main resource response. \nThe only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, \nwhich would succeed and return empty response.\n\n**By default** (`includeSnapshot: true`), an ARIA snapshot with refs is taken after navigation and returned in `output` and `refs`; you can use refs (e1, e2, ...) in interaction tools without calling `a11y_take-aria-snapshot` separately. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false). Set `includeSnapshot: false` to get only url/status/ok.\n\nWhen `includeScreenshot: true`, the screenshot is always saved to disk; `screenshotFilePath` is returned. By default `outputPath` is the OS temp dir and `name` is "screenshot" (same as `content_take-screenshot`). Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the returned path (e.g. remote, container).\n '}inputSchema(){return{url:z30.string(),timeout:z30.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS2).describe("Max wait ms. 0=no timeout."),waitUntil:z30.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL2).describe("Resolve when this load event fires."),includeSnapshot:z30.boolean().optional().default(!0).describe("Return ARIA snapshot with refs after nav."),snapshotOptions:z30.object({interactiveOnly:z30.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z30.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z30.boolean().optional().default(!1).describe("Take a screenshot after navigation; saved to disk (default: OS temp dir). Use includeBase64 only when file cannot be read from path."),screenshotOptions:z30.object({outputPath:z30.string().optional().default(os4.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z30.string().optional().default(DEFAULT_SCREENSHOT_NAME3).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z30.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z30.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z30.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z30.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z30.string().describe("Contains the URL of the navigated page.").optional(),status:z30.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z30.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z30.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z30.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z30.string().optional().describe("When includeSnapshot is true: page URL, title, and ARIA tree with refs."),refs:z30.record(z30.string(),refMapEntrySchema3).optional().describe("When includeSnapshot is true: map of ref id (e1, e2, ...) to role/name/selector for use in interaction tools."),image:z30.object({data:z30.any().describe("Base64-encoded image data."),mimeType:z30.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=await context.page.goto(args.url,{timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os4.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME3;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path5.resolve(outputPath,filename);await fs5.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs6 from"node:fs/promises";import os5 from"node:os";import path6 from"node:path";import{z as z31}from"zod";var DEFAULT_SCREENSHOT_NAME4="screenshot",DEFAULT_TIMEOUT_MS3=0,DEFAULT_WAIT_UNTIL3="load",refMapEntrySchema4=z31.object({role:z31.string(),name:z31.string().optional(),selector:z31.string(),nth:z31.number().optional()}),Reload=class{name(){return"navigation_reload"}description(){return'\nReloads the current page.\nIn case of multiple redirects, the navigation resolves with the response of the last redirect.\nIf the reload does not produce a response, returns empty response.\n\nBy default (includeSnapshot: true), an ARIA snapshot with refs is returned. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false), same as a11y_take-aria-snapshot.\n\nWhen `includeScreenshot: true`, the screenshot is saved to disk; `screenshotFilePath` is returned. Default path/name: OS temp dir and "screenshot". Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the path.\n '}inputSchema(){return{timeout:z31.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS3).describe("Max wait ms."),waitUntil:z31.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL3).describe("Resolve when this load event fires."),includeSnapshot:z31.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z31.object({interactiveOnly:z31.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z31.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z31.boolean().optional().default(!1).describe("Take a screenshot after reload; saved to disk (default: OS temp dir)."),screenshotOptions:z31.object({outputPath:z31.string().optional().default(os5.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z31.string().optional().default(DEFAULT_SCREENSHOT_NAME4).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z31.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z31.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z31.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z31.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z31.string().describe("Contains the URL of the reloaded page.").optional(),status:z31.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:z31.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:z31.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z31.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z31.string().optional(),refs:z31.record(z31.string(),refMapEntrySchema4).optional(),image:z31.object({data:z31.any().describe("Base64-encoded image data."),mimeType:z31.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=await context.page.reload({timeout:args.timeout,waitUntil:args.waitUntil}),base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os5.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME4;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path6.resolve(outputPath,filename);await fs6.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};var tools6=[new GoBackOrForward,new GoTo,new Reload];import{z as z32}from"zod";var GetConsoleMessages=class{name(){return"o11y_get-console-messages"}description(){return"Retrieves console messages/logs from the browser with filtering options."}inputSchema(){return{type:z32.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z32.string().optional().describe("Filter by message text."),timestamp:z32.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z32.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z32.object({count:z32.number().int().nonnegative().default(100),from:z32.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional()}}outputSchema(){return{messages:z32.array(z32.object({type:z32.string().describe("Type of the console message."),text:z32.string().describe("Text of the console message."),location:z32.object({url:z32.string().describe("URL of the resource."),lineNumber:z32.number().nonnegative().describe("0-based line number in the resource."),columnNumber:z32.number().nonnegative().describe("0-based column number in the resource.")}).describe("Location of the console message in the resource.").optional(),timestamp:z32.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the console message."),sequenceNumber:z32.number().int().nonnegative().describe(`
462
+ `.trim()}inputSchema(){return{mode:z28.enum(["by","to","top","bottom","left","right"]).optional().default(DEFAULT_MODE).describe("by=dx,dy; to=x,y; or edge (top/bottom/left/right)."),selector:z28.string().optional().describe("Scroll this container; omit for viewport."),dx:z28.number().optional(),dy:z28.number().optional(),x:z28.number().optional(),y:z28.number().optional(),behavior:z28.enum(["auto","smooth"]).optional().default(DEFAULT_BEHAVIOR)}}outputSchema(){return{mode:z28.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:z28.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:z28.enum(["auto","smooth"]).describe("The scroll behavior used."),before:z28.object({x:z28.number().describe("ScrollLeft before scrolling."),y:z28.number().describe("ScrollTop before scrolling."),scrollWidth:z28.number().describe("Total scrollable width before scrolling."),scrollHeight:z28.number().describe("Total scrollable height before scrolling."),clientWidth:z28.number().describe("Viewport/container client width before scrolling."),clientHeight:z28.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:z28.object({x:z28.number().describe("ScrollLeft after scrolling."),y:z28.number().describe("ScrollTop after scrolling."),scrollWidth:z28.number().describe("Total scrollable width after scrolling."),scrollHeight:z28.number().describe("Total scrollable height after scrolling."),clientWidth:z28.number().describe("Viewport/container client width after scrolling."),clientHeight:z28.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:z28.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:z28.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:z28.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:z28.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:z28.boolean().describe("Whether the scroll position is at the far left."),isAtRight:z28.boolean().describe("Whether the scroll position is at the far right."),isAtTop:z28.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:z28.boolean().describe("Whether the scroll position is at the very bottom.")}}async handle(context,args){let mode=args.mode??DEFAULT_MODE,selector=args.selector,behavior=args.behavior??DEFAULT_BEHAVIOR,dx=args.dx??0,dy=args.dy??0,x=args.x,y=args.y;if(mode==="to"&&typeof x!="number"&&typeof y!="number")throw new Error('mode="to" requires at least one of x or y.');if(mode==="by"&&dx===0&&dy===0)throw new Error('mode="by" requires dx and/or dy to be non-zero.');let params={modeEval:mode,selectorEval:selector,dxEval:dx,dyEval:dy,xEval:x,yEval:y,behaviorEval:behavior},result;return selector?result=await resolveSelectorOrRef(context,selector).evaluate((el,p)=>{let before={x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight},readMetrics=elem2=>({x:elem2.scrollLeft,y:elem2.scrollTop,scrollWidth:elem2.scrollWidth,scrollHeight:elem2.scrollHeight,clientWidth:elem2.clientWidth,clientHeight:elem2.clientHeight}),clamp=(v,min,max)=>Math.max(min,Math.min(max,v)),elem=el,maxX=Math.max(0,elem.scrollWidth-elem.clientWidth),maxY=Math.max(0,elem.scrollHeight-elem.clientHeight);p.modeEval==="by"?elem.scrollTo({left:clamp(elem.scrollLeft+p.dxEval,0,maxX),top:clamp(elem.scrollTop+p.dyEval,0,maxY),behavior:p.behaviorEval}):p.modeEval==="to"?elem.scrollTo({left:typeof p.xEval=="number"?clamp(p.xEval,0,maxX):elem.scrollLeft,top:typeof p.yEval=="number"?clamp(p.yEval,0,maxY):elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="top"?elem.scrollTo({top:0,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="bottom"?elem.scrollTo({top:maxY,left:elem.scrollLeft,behavior:p.behaviorEval}):p.modeEval==="left"?elem.scrollTo({left:0,top:elem.scrollTop,behavior:p.behaviorEval}):p.modeEval==="right"&&elem.scrollTo({left:maxX,top:elem.scrollTop,behavior:p.behaviorEval});let after=readMetrics(elem);return{before,after,canScrollX:after.scrollWidth>after.clientWidth,canScrollY:after.scrollHeight>after.clientHeight,maxScrollX:Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY:Math.max(0,after.scrollHeight-after.clientHeight),isAtLeft:after.x<=1,isAtRight:after.x>=Math.max(0,after.scrollWidth-after.clientWidth)-1,isAtTop:after.y<=1,isAtBottom:after.y>=Math.max(0,after.scrollHeight-after.clientHeight)-1}},params):result=await context.page.evaluate(params2=>{let modeEval=params2.modeEval,selectorEval=params2.selectorEval,dxEval=params2.dxEval,dyEval=params2.dyEval,xEval=params2.xEval,yEval=params2.yEval,behaviorEval=params2.behaviorEval,getTarget=()=>{if(selectorEval){let el=document.querySelector(selectorEval);if(!el)throw new Error(`Element with selector "${selectorEval}" not found`);return el}let scrolling=document.scrollingElement||document.documentElement||document.body;if(!scrolling)throw new Error("No scrolling element available.");return scrolling},readMetrics=el=>({x:el.scrollLeft,y:el.scrollTop,scrollWidth:el.scrollWidth,scrollHeight:el.scrollHeight,clientWidth:el.clientWidth,clientHeight:el.clientHeight}),clamp=(v,min,max)=>v<min?min:v>max?max:v,doScroll=el=>{let maxX=Math.max(0,el.scrollWidth-el.clientWidth),maxY=Math.max(0,el.scrollHeight-el.clientHeight);if(modeEval==="by"){let nextX=clamp(el.scrollLeft+dxEval,0,maxX),nextY=clamp(el.scrollTop+dyEval,0,maxY);el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="to"){let nextX=typeof xEval=="number"?clamp(xEval,0,maxX):el.scrollLeft,nextY=typeof yEval=="number"?clamp(yEval,0,maxY):el.scrollTop;el.scrollTo({left:nextX,top:nextY,behavior:behaviorEval});return}if(modeEval==="top"){el.scrollTo({top:0,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="bottom"){el.scrollTo({top:maxY,left:el.scrollLeft,behavior:behaviorEval});return}if(modeEval==="left"){el.scrollTo({left:0,top:el.scrollTop,behavior:behaviorEval});return}if(modeEval==="right"){el.scrollTo({left:maxX,top:el.scrollTop,behavior:behaviorEval});return}},target=getTarget(),before=readMetrics(target);doScroll(target);let after=readMetrics(target),maxScrollX=Math.max(0,after.scrollWidth-after.clientWidth),maxScrollY=Math.max(0,after.scrollHeight-after.clientHeight),canScrollX=after.scrollWidth>after.clientWidth,canScrollY=after.scrollHeight>after.clientHeight,eps=1,isAtLeft=after.x<=eps,isAtRight=after.x>=maxScrollX-eps,isAtTop=after.y<=eps,isAtBottom=after.y>=maxScrollY-eps;return{before,after,canScrollX,canScrollY,maxScrollX,maxScrollY,isAtLeft,isAtRight,isAtTop,isAtBottom}},params),{mode,selector:selector??null,behavior,before:result.before,after:result.after,canScrollX:result.canScrollX,canScrollY:result.canScrollY,maxScrollX:result.maxScrollX,maxScrollY:result.maxScrollY,isAtLeft:result.isAtLeft,isAtRight:result.isAtRight,isAtTop:result.isAtTop,isAtBottom:result.isAtBottom}}};var tools5=[new Click,new Drag,new Fill,new Hover,new PressKey,new ResizeViewport,new ResizeWindow,new Select,new Scroll];import fs4 from"node:fs/promises";import os3 from"node:os";import path4 from"node:path";import{z as z29}from"zod";var DEFAULT_SCREENSHOT_NAME2="screenshot",DEFAULT_TIMEOUT_MS=0,DEFAULT_WAIT_FOR_TIMEOUT_MS2=3e4,DEFAULT_WAIT_UNTIL="load",DIRECTION_VALUES=["back","forward"],refMapEntrySchema2=z29.object({role:z29.string(),name:z29.string().optional(),selector:z29.string(),nth:z29.number().optional()}),GoBackOrForward=class{name(){return"navigation_go-back-or-forward"}description(){return'\nNavigates to the previous or next page in history.\n- `direction: "back"` \u2014 previous page in history.\n- `direction: "forward"` \u2014 next page in history.\n\nIn case of multiple redirects, the navigation will resolve with the response of the last redirect.\nIf cannot go back/forward, returns empty response.\n\nBy default (includeSnapshot: true), an ARIA snapshot with refs is returned. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false), same as a11y_take-aria-snapshot.\n\nWhen `includeScreenshot: true`, the screenshot is always saved to disk; `screenshotFilePath` is returned. By default `outputPath` is the OS temp dir and `name` is "screenshot" (same as `content_take-screenshot`). Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the returned path (e.g. remote, container).\n '}inputSchema(){return{direction:z29.enum(DIRECTION_VALUES),timeout:z29.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS).describe("Max wait ms. 0=no timeout."),waitUntil:z29.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL).describe("Resolve when this load event fires."),waitForNavigation:z29.boolean().optional().default(!0).describe("Wait for navigation then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z29.number().int().min(0).optional().default(DEFAULT_WAIT_FOR_TIMEOUT_MS2).describe("Timeout for navigation and network idle wait (ms). Only when waitForNavigation is true. Default 30000."),includeSnapshot:z29.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z29.object({interactiveOnly:z29.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z29.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z29.boolean().optional().default(!1).describe("Take a screenshot after navigation; saved to disk (default: OS temp dir). Use includeBase64 only when file cannot be read from path."),screenshotOptions:z29.object({outputPath:z29.string().optional().default(os3.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z29.string().optional().default(DEFAULT_SCREENSHOT_NAME2).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z29.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z29.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z29.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z29.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z29.string().describe("Contains the URL of the navigated page.").optional(),status:z29.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z29.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z29.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z29.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z29.string().optional(),refs:z29.record(z29.string(),refMapEntrySchema2).optional(),image:z29.object({data:z29.any().describe("Base64-encoded image data."),mimeType:z29.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=args.direction==="back"?await context.page.goBack({timeout:args.timeout,waitUntil:args.waitUntil}):await context.page.goForward({timeout:args.timeout,waitUntil:args.waitUntil});if(args.waitForNavigation!==!1){let waitForTimeoutMs=args.waitForTimeoutMs??DEFAULT_WAIT_FOR_TIMEOUT_MS2;await waitForNetworkIdle(context,{timeoutMs:waitForTimeoutMs})}let base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os3.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME2;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path4.resolve(outputPath,filename);await fs4.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs5 from"node:fs/promises";import os4 from"node:os";import path5 from"node:path";import{z as z30}from"zod";var DEFAULT_SCREENSHOT_NAME3="screenshot",DEFAULT_TIMEOUT_MS2=0,DEFAULT_WAIT_FOR_TIMEOUT_MS3=3e4,DEFAULT_WAIT_UNTIL2="load",refMapEntrySchema3=z30.object({role:z30.string(),name:z30.string().optional(),selector:z30.string(),nth:z30.number().optional()}),GoTo=class{name(){return"navigation_go-to"}description(){return'\nNavigates to the given URL.\n**NOTE**: The tool either throws an error or returns a main resource response. \nThe only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, \nwhich would succeed and return empty response.\n\n**By default** (`includeSnapshot: true`), an ARIA snapshot with refs is taken after navigation and returned in `output` and `refs`; you can use refs (e1, e2, ...) in interaction tools without calling `a11y_take-aria-snapshot` separately. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false). Set `includeSnapshot: false` to get only url/status/ok.\n\nWhen `includeScreenshot: true`, the screenshot is always saved to disk; `screenshotFilePath` is returned. By default `outputPath` is the OS temp dir and `name` is "screenshot" (same as `content_take-screenshot`). Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the returned path (e.g. remote, container).\n '}inputSchema(){return{url:z30.string(),timeout:z30.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS2).describe("Max wait ms. 0=no timeout."),waitUntil:z30.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL2).describe("Resolve when this load event fires."),waitForNavigation:z30.boolean().optional().default(!0).describe("Wait for navigation then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z30.number().int().min(0).optional().default(DEFAULT_WAIT_FOR_TIMEOUT_MS3).describe("Timeout for navigation and network idle wait (ms). Only when waitForNavigation is true. Default 30000."),includeSnapshot:z30.boolean().optional().default(!0).describe("Return ARIA snapshot with refs after nav."),snapshotOptions:z30.object({interactiveOnly:z30.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z30.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z30.boolean().optional().default(!1).describe("Take a screenshot after navigation; saved to disk (default: OS temp dir). Use includeBase64 only when file cannot be read from path."),screenshotOptions:z30.object({outputPath:z30.string().optional().default(os4.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z30.string().optional().default(DEFAULT_SCREENSHOT_NAME3).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z30.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z30.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z30.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z30.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z30.string().describe("Contains the URL of the navigated page.").optional(),status:z30.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z30.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z30.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z30.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z30.string().optional().describe("When includeSnapshot is true: page URL, title, and ARIA tree with refs."),refs:z30.record(z30.string(),refMapEntrySchema3).optional().describe("When includeSnapshot is true: map of ref id (e1, e2, ...) to role/name/selector for use in interaction tools."),image:z30.object({data:z30.any().describe("Base64-encoded image data."),mimeType:z30.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=await context.page.goto(args.url,{timeout:args.timeout,waitUntil:args.waitUntil});if(args.waitForNavigation!==!1){let waitForTimeoutMs=args.waitForTimeoutMs??DEFAULT_WAIT_FOR_TIMEOUT_MS3;await waitForNetworkIdle(context,{timeoutMs:waitForTimeoutMs})}let base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os4.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME3;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path5.resolve(outputPath,filename);await fs5.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs6 from"node:fs/promises";import os5 from"node:os";import path6 from"node:path";import{z as z31}from"zod";var DEFAULT_SCREENSHOT_NAME4="screenshot",DEFAULT_TIMEOUT_MS3=0,DEFAULT_WAIT_FOR_TIMEOUT_MS4=3e4,DEFAULT_WAIT_UNTIL3="load",refMapEntrySchema4=z31.object({role:z31.string(),name:z31.string().optional(),selector:z31.string(),nth:z31.number().optional()}),Reload=class{name(){return"navigation_reload"}description(){return'\nReloads the current page.\nIn case of multiple redirects, the navigation resolves with the response of the last redirect.\nIf the reload does not produce a response, returns empty response.\n\nBy default (includeSnapshot: true), an ARIA snapshot with refs is returned. Use `snapshotOptions` for `interactiveOnly` (default false) and `cursorInteractive` (default false), same as a11y_take-aria-snapshot.\n\nWhen `includeScreenshot: true`, the screenshot is saved to disk; `screenshotFilePath` is returned. Default path/name: OS temp dir and "screenshot". Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the path.\n '}inputSchema(){return{timeout:z31.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS3).describe("Max wait ms."),waitUntil:z31.enum(["load","domcontentloaded","networkidle","commit"]).optional().default(DEFAULT_WAIT_UNTIL3).describe("Resolve when this load event fires."),waitForNavigation:z31.boolean().optional().default(!0).describe("Wait for reload then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z31.number().int().min(0).optional().default(DEFAULT_WAIT_FOR_TIMEOUT_MS4).describe("Timeout for reload and network idle wait (ms). Only when waitForNavigation is true. Default 30000."),includeSnapshot:z31.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z31.object({interactiveOnly:z31.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z31.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements. Default false.")}).optional().describe("Options when includeSnapshot is true. Same as a11y_take-aria-snapshot."),includeScreenshot:z31.boolean().optional().default(!1).describe("Take a screenshot after reload; saved to disk (default: OS temp dir)."),screenshotOptions:z31.object({outputPath:z31.string().optional().default(os5.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z31.string().optional().default(DEFAULT_SCREENSHOT_NAME4).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z31.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z31.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z31.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z31.boolean().optional().default(!1).describe("Include base64 image in response; use only when file cannot be read from path (e.g. remote, container).")}).optional().describe('Options when includeScreenshot is true. outputPath/name default to tmp and "screenshot".')}}outputSchema(){return{url:z31.string().describe("Contains the URL of the reloaded page.").optional(),status:z31.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:z31.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:z31.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z31.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z31.string().optional(),refs:z31.record(z31.string(),refMapEntrySchema4).optional(),image:z31.object({data:z31.any().describe("Base64-encoded image data."),mimeType:z31.string().describe("MIME type of the image.")}).optional().describe("When includeScreenshot and screenshotOptions.includeBase64 are true: image sent as separate image content part.")}}async handle(context,args){let response=await context.page.reload({timeout:args.timeout,waitUntil:args.waitUntil});if(args.waitForNavigation!==!1){let waitForTimeoutMs=args.waitForTimeoutMs??DEFAULT_WAIT_FOR_TIMEOUT_MS4;await waitForNetworkIdle(context,{timeoutMs:waitForTimeoutMs})}let base={url:response?.url(),status:response?.status(),statusText:response?.statusText(),ok:response?.ok()},output=base;if(args.includeSnapshot!==!1)try{let snapOpts=args.snapshotOptions??{},snapshot=await takeAriaSnapshotWithRefs(context,{interactiveOnly:snapOpts.interactiveOnly??!1,cursorInteractive:snapOpts.cursorInteractive??!1});output={...base,output:snapshot.output,refs:snapshot.refs}}catch{}let screenshotFilePath,image;if(args.includeScreenshot===!0){let opts=args.screenshotOptions??{},outputPath=opts.outputPath??os5.tmpdir(),name=opts.name??DEFAULT_SCREENSHOT_NAME4;try{let screenshotType=(opts.type??"png")==="jpeg"?"jpeg":"png",shot=await captureScreenshot(context,{fullPage:opts.fullPage??!0,type:screenshotType,annotate:opts.annotate??!0}),filename=`${name}-${formattedTimeForFilename()}.${shot.screenshotType}`,filePath=path6.resolve(outputPath,filename);await fs6.writeFile(filePath,shot.rawBuffer),screenshotFilePath=filePath,opts.includeBase64===!0&&(image=shot.image)}catch{}}return{url:output.url,status:output.status,statusText:output.statusText,ok:output.ok,...screenshotFilePath!==void 0&&{screenshotFilePath},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};var tools6=[new GoBackOrForward,new GoTo,new Reload];import{z as z32}from"zod";var GetConsoleMessages=class{name(){return"o11y_get-console-messages"}description(){return"Retrieves console messages/logs from the browser with filtering options."}inputSchema(){return{type:z32.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z32.string().optional().describe("Filter by message text."),timestamp:z32.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z32.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z32.object({count:z32.number().int().nonnegative().default(100),from:z32.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional()}}outputSchema(){return{messages:z32.array(z32.object({type:z32.string().describe("Type of the console message."),text:z32.string().describe("Text of the console message."),location:z32.object({url:z32.string().describe("URL of the resource."),lineNumber:z32.number().nonnegative().describe("0-based line number in the resource."),columnNumber:z32.number().nonnegative().describe("0-based column number in the resource.")}).describe("Location of the console message in the resource.").optional(),timestamp:z32.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the console message."),sequenceNumber:z32.number().int().nonnegative().describe(`
458
463
  A monotonically increasing sequence number assigned to each console message.
459
464
  It reflects the order in which messages were captured and can be used by clients
460
465
  to retrieve messages incrementally by requesting only those with a higher sequence
461
- number than the last one received.`)}).describe("Console message item.")).describe("Retrieved console messages.")}}async handle(context,args){let consoleMessageLevelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filteredConsoleMessages=context.getConsoleMessages().filter(msg=>{let filter=!0;return consoleMessageLevelCodeThreshold!==void 0&&(filter=msg.level.code>=consoleMessageLevelCodeThreshold),filter&&args.timestamp&&(filter=msg.timestamp>=args.timestamp),filter&&args.sequenceNumber&&(filter=msg.sequenceNumber>args.sequenceNumber),filter&&args.search&&(filter=msg.text.includes(args.search)),filter});return{messages:(args.limit?.count?args.limit.from==="start"?filteredConsoleMessages.slice(0,args.limit.count):filteredConsoleMessages.slice(-args.limit.count):filteredConsoleMessages).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z33}from"zod";var GetHttpRequests=class{name(){return"o11y_get-http-requests"}description(){return"Retrieves HTTP requests from the browser with filtering options."}inputSchema(){return{resourceType:z33.enum(getEnumKeyTuples(HttpResourceType)).transform(createEnumTransformer(HttpResourceType)).optional().describe("Type filter."),status:z33.object({min:z33.number().int().positive().optional(),max:z33.number().int().positive().optional()}).optional().describe("HTTP status range."),ok:z33.boolean().optional().describe("2xx only."),timestamp:z33.number().int().nonnegative().optional().describe("Only requests at or after this Unix ms."),sequenceNumber:z33.number().int().nonnegative().optional().describe("Incremental: only requests with sequence > this."),limit:z33.object({count:z33.number().int().nonnegative().default(100),from:z33.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional()}}outputSchema(){return{requests:z33.array(z33.object({url:z33.string().describe("HTTP request url."),method:z33.enum(getEnumKeyTuples(HttpMethod)).describe(`HTTP request method. Valid values are: ${getEnumKeyTuples(HttpMethod)}`),headers:z33.record(z33.string(),z33.string()).describe("HTTP request headers as key-value pairs."),body:z33.string().describe("HTTP request body if available.").optional(),resourceType:z33.enum(getEnumKeyTuples(HttpResourceType)).describe(`
466
+ number than the last one received.`)}).describe("Console message item.")).describe("Retrieved console messages.")}}async handle(context,args){let consoleMessageLevelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filteredConsoleMessages=context.getConsoleMessages().filter(msg=>{let filter=!0;return consoleMessageLevelCodeThreshold!==void 0&&(filter=msg.level.code>=consoleMessageLevelCodeThreshold),filter&&args.timestamp&&(filter=msg.timestamp>=args.timestamp),filter&&args.sequenceNumber&&(filter=msg.sequenceNumber>args.sequenceNumber),filter&&args.search&&(filter=msg.text.includes(args.search)),filter});return{messages:(args.limit?.count?args.limit.from==="start"?filteredConsoleMessages.slice(0,args.limit.count):filteredConsoleMessages.slice(-args.limit.count):filteredConsoleMessages).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z33}from"zod";var GetHttpRequests=class{name(){return"o11y_get-http-requests"}description(){return"Retrieves HTTP requests from the browser with filtering options."}inputSchema(){return{resourceType:z33.enum(getEnumKeyTuples(HttpResourceType)).transform(createEnumTransformer(HttpResourceType)).optional().describe("Type filter."),status:z33.object({min:z33.number().int().positive().optional(),max:z33.number().int().positive().optional()}).optional().describe("HTTP status range."),ok:z33.boolean().optional().describe("2xx only."),timestamp:z33.number().int().nonnegative().optional().describe("Only requests at or after this Unix ms."),sequenceNumber:z33.number().int().nonnegative().optional().describe("Incremental: only requests with sequence > this."),limit:z33.object({count:z33.number().int().nonnegative().default(100),from:z33.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional(),includeRequestHeaders:z33.boolean().default(!1).optional().describe("Include request headers in each item. Default false."),includeResponseHeaders:z33.boolean().default(!1).optional().describe("Include response headers in each item. Default false."),includeResponseBody:z33.boolean().default(!1).optional().describe("Include response body in each item. Default false.")}}outputSchema(){return{requests:z33.array(z33.object({url:z33.string().describe("HTTP request url."),method:z33.enum(getEnumKeyTuples(HttpMethod)).describe(`HTTP request method. Valid values are: ${getEnumKeyTuples(HttpMethod)}`),headers:z33.record(z33.string(),z33.string()).describe("HTTP request headers as key-value pairs. Present when includeRequestHeaders was true.").optional(),body:z33.string().describe("HTTP request body if available.").optional(),resourceType:z33.enum(getEnumKeyTuples(HttpResourceType)).describe(`
462
467
  HTTP request resource type as it was perceived by the rendering engine.
463
- Valid values are: ${getEnumKeyTuples(HttpResourceType)}`),failure:z33.string().describe("Error message of the HTTP request if failed.").optional(),duration:z33.number().describe('HTTP request duration in milliseconds. "-1" if not available (no response).').optional(),response:z33.object({status:z33.number().int().positive().describe("HTTP response status code."),statusText:z33.string().describe("HTTP response status text."),headers:z33.record(z33.string(),z33.string()).describe("HTTP response headers as key-value pairs."),body:z33.string().describe("HTTP response body if available.").optional()}).describe("HTTP response.").optional(),ok:z33.boolean().describe(`
468
+ Valid values are: ${getEnumKeyTuples(HttpResourceType)}`),failure:z33.string().describe("Error message of the HTTP request if failed.").optional(),duration:z33.number().describe('HTTP request duration in milliseconds. "-1" if not available (no response).').optional(),response:z33.object({status:z33.number().int().positive().describe("HTTP response status code."),statusText:z33.string().describe("HTTP response status text."),headers:z33.record(z33.string(),z33.string()).describe("HTTP response headers. Present when includeResponseHeaders was true.").optional(),body:z33.string().describe("HTTP response body if available. Present when includeResponseBody was true.").optional()}).describe("HTTP response.").optional(),ok:z33.boolean().describe(`
464
469
  Flag to represent whether the HTTP request successful or failed.
465
470
  An HTTP request is considered successful only if its status code is 2XX.
466
471
  Otherwise (non-2XX status code or no response at all because of timeout, network failure, etc ...) it is considered as failed.`).optional(),timestamp:z33.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the HTTP request."),sequenceNumber:z33.number().int().nonnegative().describe(`
467
472
  A monotonically increasing sequence number assigned to each HTTP request.
468
473
  It reflects the order in which requests were captured and can be used by clients
469
474
  to retrieve requests incrementally by requesting only those with a higher sequence
470
- number than the last one received.`)}).describe("HTTP request item.")).describe("Retrieved HTTP requests.")}}async handle(context,args){let filteredHttpRequests=context.getHttpRequests().filter(req=>{let filter=!0;return filter&&args.resourceType&&(filter=req.resourceType===args.resourceType),filter&&args.status&&(filter&&args.status.min&&(filter=req.response?req.response.status>=args.status.min:!1),filter&&args.status.max&&(filter=req.response?req.response.status<=args.status.max:!1)),filter&&args.ok!==void 0&&(filter=req.ok),filter&&args.timestamp&&(filter=req.timestamp>=args.timestamp),filter&&args.sequenceNumber&&(filter=req.sequenceNumber>args.sequenceNumber),filter});return{requests:(args.limit?.count?args.limit.from==="start"?filteredHttpRequests.slice(0,args.limit.count):filteredHttpRequests.slice(-args.limit.count):filteredHttpRequests).map(req=>({url:req.url,method:req.method,headers:req.headers,body:req.body,resourceType:req.resourceType,failure:req.failure,duration:req.duration,response:req.response?{status:req.response.status,statusText:req.response.statusText,headers:req.response.headers,body:req.response.body}:void 0,ok:req.ok,timestamp:req.timestamp,sequenceNumber:req.sequenceNumber}))}}};import{z as z34}from"zod";var GetTraceId=class{name(){return"o11y_get-trace-id"}description(){return"Gets the OpenTelemetry compatible trace id of the current session."}inputSchema(){return{}}outputSchema(){return{traceId:z34.string().describe("The OpenTelemetry compatible trace id of the current session if available.").optional()}}async handle(context,args){return{traceId:await context.getTraceId()}}};import{z as z35}from"zod";var DEFAULT_WAIT_MS=0,MAX_WAIT_MS=3e4;function rateMs(value,good,poor){return typeof value!="number"||!Number.isFinite(value)?{rating:"not_available",value:null,unit:"ms",thresholds:{good,poor}}:value<=good?{rating:"good",value,unit:"ms",thresholds:{good,poor}}:value>poor?{rating:"poor",value,unit:"ms",thresholds:{good,poor}}:{rating:"needs_improvement",value,unit:"ms",thresholds:{good,poor}}}function rateScore(value,good,poor){return typeof value!="number"||!Number.isFinite(value)?{rating:"not_available",value:null,unit:"score",thresholds:{good,poor}}:value<=good?{rating:"good",value,unit:"score",thresholds:{good,poor}}:value>poor?{rating:"poor",value,unit:"score",thresholds:{good,poor}}:{rating:"needs_improvement",value,unit:"score",thresholds:{good,poor}}}function formatRating(r){return r==="needs_improvement"?"needs improvement":r==="not_available"?"not available":r}function buildRecommendations(params){let lcpRating=params.ratings.lcp.rating,inpRating=params.ratings.inp.rating,clsRating=params.ratings.cls.rating,ttfbRating=params.ratings.ttfb.rating,fcpRating=params.ratings.fcp.rating,coreWebVitalsPassed=lcpRating==="good"&&inpRating==="good"&&clsRating==="good",summary=[];coreWebVitalsPassed?summary.push("Core Web Vitals look good (LCP, INP and CLS are all within recommended thresholds)."):summary.push("Core Web Vitals need attention. Focus on the worst-rated metric first (LCP, INP, or CLS).");let lcp=[],inp=[],cls=[],ttfb=[],fcp=[],general=[];return params.lcpSelectorHint&&general.push(`LCP element hint (best-effort): ${params.lcpSelectorHint}`),lcpRating==="poor"||lcpRating==="needs_improvement"?(lcp.push("Optimize the LCP element (often the hero image, headline, or main content above the fold)."),lcp.push("Reduce render-blocking resources (critical CSS, JS). Consider inlining critical CSS and deferring non-critical JS."),lcp.push('Preload the LCP resource (e.g., <link rel="preload"> for the hero image/font) and ensure it is discoverable without heavy JS.'),lcp.push("Improve server response and caching. A slow TTFB often delays LCP."),lcp.push("Avoid client-only rendering for above-the-fold content when possible; stream/SSR critical content.")):lcpRating==="good"?lcp.push("LCP is within the recommended threshold. Keep the above-the-fold path lean."):lcp.push("LCP is not available in this browser/session. Consider using Chromium or a page-load scenario that produces LCP entries."),inpRating==="poor"||inpRating==="needs_improvement"?(inp.push("Break up long main-thread tasks. Aim to keep tasks under ~50ms (split work, yield to the event loop)."),inp.push("Reduce expensive work in input handlers (click, pointer, key events). Move non-urgent work to idle time."),inp.push("Avoid synchronous layout thrash during interactions (batch DOM reads/writes, reduce forced reflow)."),inp.push("Defer heavy third-party scripts and reduce JavaScript bundle size to improve responsiveness.")):inpRating==="good"?inp.push("INP is within the recommended threshold. Keep interaction handlers lightweight."):inp.push("INP is not available in this browser/session. It requires Event Timing support and user interactions."),clsRating==="poor"||clsRating==="needs_improvement"?(cls.push("Reserve space for images/iframes/ads (set width/height or aspect-ratio) to prevent layout jumps."),cls.push("Avoid inserting content above existing content unless it is in response to a user interaction."),cls.push("Use stable font loading (font-display: swap/optional) and consider preloading critical fonts to reduce text shifts."),cls.push("Be careful with late-loading banners/toasts; render them in reserved containers.")):clsRating==="good"?cls.push("CLS is within the recommended threshold. Keep layout stable during load and async updates."):cls.push("CLS is not available in this browser/session. Consider Chromium or a scenario with visible layout changes."),ttfbRating==="poor"||ttfbRating==="needs_improvement"?(ttfb.push("Improve backend latency: reduce server processing time, optimize DB queries, and eliminate unnecessary middleware."),ttfb.push("Enable CDN/edge caching where possible. Use caching headers and avoid dynamic responses for static content."),ttfb.push("Reduce cold-start and TLS overhead (keep-alive, warm pools, edge runtimes).")):ttfbRating==="good"?ttfb.push("TTFB is good. Backend/network latency is unlikely to be the primary bottleneck."):ttfb.push("TTFB is not available in this browser/session."),fcpRating==="poor"||fcpRating==="needs_improvement"?(fcp.push("Reduce render-blocking CSS/JS and prioritize critical content for first paint."),fcp.push("Optimize above-the-fold resources and avoid large synchronous scripts during initial load."),fcp.push("Consider code-splitting and preloading critical assets to improve first paint.")):fcpRating==="good"?fcp.push("FCP is good. The page provides early visual feedback."):fcp.push("FCP is not available in this browser/session."),general.push("For reliable debugging, capture metrics after navigation and after user actions that trigger loading or layout changes."),general.push("If values look unstable, try adding waitMs (e.g., 1000-3000) and re-measure after the UI settles."),{coreWebVitalsPassed,summary,lcp,inp,cls,ttfb,fcp,general}}var GetWebVitals=class{name(){return"o11y_get-web-vitals"}description(){return`
475
+ number than the last one received.`)}).describe("HTTP request item.")).describe("Retrieved HTTP requests.")}}async handle(context,args){let filteredHttpRequests=context.getHttpRequests().filter(req=>{let filter=!0;return filter&&args.resourceType&&(filter=req.resourceType===args.resourceType),filter&&args.status&&(filter&&args.status.min&&(filter=req.response?req.response.status>=args.status.min:!1),filter&&args.status.max&&(filter=req.response?req.response.status<=args.status.max:!1)),filter&&args.ok!==void 0&&(filter=req.ok),filter&&args.timestamp&&(filter=req.timestamp>=args.timestamp),filter&&args.sequenceNumber&&(filter=req.sequenceNumber>args.sequenceNumber),filter}),trimmedHttpRequests=args.limit?.count?args.limit.from==="start"?filteredHttpRequests.slice(0,args.limit.count):filteredHttpRequests.slice(-args.limit.count):filteredHttpRequests,includeReqHeaders=args.includeRequestHeaders===!0,includeResHeaders=args.includeResponseHeaders===!0,includeResBody=args.includeResponseBody===!0;return{requests:trimmedHttpRequests.map(req=>({url:req.url,method:req.method,...includeReqHeaders&&{headers:req.headers},body:req.body,resourceType:req.resourceType,failure:req.failure,duration:req.duration,response:req.response?{status:req.response.status,statusText:req.response.statusText,...includeResHeaders&&{headers:req.response.headers},...includeResBody&&{body:req.response.body}}:void 0,ok:req.ok,timestamp:req.timestamp,sequenceNumber:req.sequenceNumber}))}}};import{z as z34}from"zod";var GetTraceId=class{name(){return"o11y_get-trace-id"}description(){return"Gets the OpenTelemetry compatible trace id of the current session."}inputSchema(){return{}}outputSchema(){return{traceId:z34.string().describe("The OpenTelemetry compatible trace id of the current session if available.").optional()}}async handle(context,args){return{traceId:await context.getTraceId()}}};import{z as z35}from"zod";var DEFAULT_WAIT_MS=0,MAX_WAIT_MS=3e4;function rateMs(value,good,poor){return typeof value!="number"||!Number.isFinite(value)?{rating:"not_available",value:null,unit:"ms",thresholds:{good,poor}}:value<=good?{rating:"good",value,unit:"ms",thresholds:{good,poor}}:value>poor?{rating:"poor",value,unit:"ms",thresholds:{good,poor}}:{rating:"needs_improvement",value,unit:"ms",thresholds:{good,poor}}}function rateScore(value,good,poor){return typeof value!="number"||!Number.isFinite(value)?{rating:"not_available",value:null,unit:"score",thresholds:{good,poor}}:value<=good?{rating:"good",value,unit:"score",thresholds:{good,poor}}:value>poor?{rating:"poor",value,unit:"score",thresholds:{good,poor}}:{rating:"needs_improvement",value,unit:"score",thresholds:{good,poor}}}function formatRating(r){return r==="needs_improvement"?"needs improvement":r==="not_available"?"not available":r}function buildRecommendations(params){let lcpRating=params.ratings.lcp.rating,inpRating=params.ratings.inp.rating,clsRating=params.ratings.cls.rating,ttfbRating=params.ratings.ttfb.rating,fcpRating=params.ratings.fcp.rating,coreWebVitalsPassed=lcpRating==="good"&&inpRating==="good"&&clsRating==="good",summary=[];coreWebVitalsPassed?summary.push("Core Web Vitals look good (LCP, INP and CLS are all within recommended thresholds)."):summary.push("Core Web Vitals need attention. Focus on the worst-rated metric first (LCP, INP, or CLS).");let lcp=[],inp=[],cls=[],ttfb=[],fcp=[],general=[];return params.lcpSelectorHint&&general.push(`LCP element hint (best-effort): ${params.lcpSelectorHint}`),lcpRating==="poor"||lcpRating==="needs_improvement"?(lcp.push("Optimize the LCP element (often the hero image, headline, or main content above the fold)."),lcp.push("Reduce render-blocking resources (critical CSS, JS). Consider inlining critical CSS and deferring non-critical JS."),lcp.push('Preload the LCP resource (e.g., <link rel="preload"> for the hero image/font) and ensure it is discoverable without heavy JS.'),lcp.push("Improve server response and caching. A slow TTFB often delays LCP."),lcp.push("Avoid client-only rendering for above-the-fold content when possible; stream/SSR critical content.")):lcpRating==="good"?lcp.push("LCP is within the recommended threshold. Keep the above-the-fold path lean."):lcp.push("LCP is not available in this browser/session. Consider using Chromium or a page-load scenario that produces LCP entries."),inpRating==="poor"||inpRating==="needs_improvement"?(inp.push("Break up long main-thread tasks. Aim to keep tasks under ~50ms (split work, yield to the event loop)."),inp.push("Reduce expensive work in input handlers (click, pointer, key events). Move non-urgent work to idle time."),inp.push("Avoid synchronous layout thrash during interactions (batch DOM reads/writes, reduce forced reflow)."),inp.push("Defer heavy third-party scripts and reduce JavaScript bundle size to improve responsiveness.")):inpRating==="good"?inp.push("INP is within the recommended threshold. Keep interaction handlers lightweight."):inp.push("INP is not available in this browser/session. It requires Event Timing support and user interactions."),clsRating==="poor"||clsRating==="needs_improvement"?(cls.push("Reserve space for images/iframes/ads (set width/height or aspect-ratio) to prevent layout jumps."),cls.push("Avoid inserting content above existing content unless it is in response to a user interaction."),cls.push("Use stable font loading (font-display: swap/optional) and consider preloading critical fonts to reduce text shifts."),cls.push("Be careful with late-loading banners/toasts; render them in reserved containers.")):clsRating==="good"?cls.push("CLS is within the recommended threshold. Keep layout stable during load and async updates."):cls.push("CLS is not available in this browser/session. Consider Chromium or a scenario with visible layout changes."),ttfbRating==="poor"||ttfbRating==="needs_improvement"?(ttfb.push("Improve backend latency: reduce server processing time, optimize DB queries, and eliminate unnecessary middleware."),ttfb.push("Enable CDN/edge caching where possible. Use caching headers and avoid dynamic responses for static content."),ttfb.push("Reduce cold-start and TLS overhead (keep-alive, warm pools, edge runtimes).")):ttfbRating==="good"?ttfb.push("TTFB is good. Backend/network latency is unlikely to be the primary bottleneck."):ttfb.push("TTFB is not available in this browser/session."),fcpRating==="poor"||fcpRating==="needs_improvement"?(fcp.push("Reduce render-blocking CSS/JS and prioritize critical content for first paint."),fcp.push("Optimize above-the-fold resources and avoid large synchronous scripts during initial load."),fcp.push("Consider code-splitting and preloading critical assets to improve first paint.")):fcpRating==="good"?fcp.push("FCP is good. The page provides early visual feedback."):fcp.push("FCP is not available in this browser/session."),general.push("For reliable debugging, capture metrics after navigation and after user actions that trigger loading or layout changes."),general.push("If values look unstable, try adding waitMs (e.g., 1000-3000) and re-measure after the UI settles."),{coreWebVitalsPassed,summary,lcp,inp,cls,ttfb,fcp,general}}var GetWebVitals=class{name(){return"o11y_get-web-vitals"}description(){return`
471
476
  Collects Web Vitals (LCP, INP, CLS, TTFB, FCP) with Google thresholds and recommendations.
472
477
  Call after navigation or user actions; use waitMs for more stable LCP/CLS/INP.
473
478
  Some metrics may be unavailable depending on browser and interactions.
@@ -484,53 +489,12 @@ Prefer an anchor (anchorSelector or anchorX/anchorY) to target the instance; opt
484
489
  React DevTools hook gives reliable root discovery (getFiberRoots); without it we fall back to DOM scan for __reactFiber$ (best-effort).
485
490
  For more reliable roots in a persistent browser, install the React Developer Tools Chrome extension.
486
491
  Debug source is best-effort and may be missing in some builds.
487
- `.trim()}inputSchema(){return{anchorSelector:z39.string().optional().describe("Anchor selector/ref."),anchorX:z39.number().int().nonnegative().optional().describe("Viewport X when anchorSelector omitted."),anchorY:z39.number().int().nonnegative().optional().describe("Viewport Y when anchorSelector omitted."),componentName:z39.string().optional(),matchStrategy:z39.enum(["exact","contains"]).optional().default(DEFAULT_MATCH_STRATEGY),fileNameHint:z39.string().optional().describe("File hint."),lineNumber:z39.number().int().positive().optional(),maxElements:z39.number().int().positive().optional().default(DEFAULT_MAX_ELEMENTS),onlyVisible:z39.boolean().optional().default(DEFAULT_ONLY_VISIBLE2),onlyInViewport:z39.boolean().optional().default(DEFAULT_ONLY_IN_VIEWPORT2),textPreviewMaxLength:z39.number().int().positive().optional().default(DEFAULT_TEXT_PREVIEW_MAX_LENGTH2),maxMatches:z39.number().int().positive().optional().default(DEFAULT_MAX_MATCHES)}}outputSchema(){return{reactDetected:z39.boolean().describe("True if __REACT_DEVTOOLS_GLOBAL_HOOK__ looks available."),fiberDetected:z39.boolean().describe("True if DOM appears to contain React Fiber pointers (__reactFiber$...)."),rootDiscovery:z39.enum(["devtools-hook","dom-fiber-scan","none"]).describe("How roots were discovered."),component:z39.object({name:z39.string().nullable(),displayName:z39.string().nullable(),debugSource:z39.object({fileName:z39.string().nullable(),lineNumber:z39.number().int().nullable(),columnNumber:z39.number().int().nullable()}).nullable(),componentStack:z39.array(z39.string()),selection:z39.enum(["anchor","query","query+anchor","unknown"]),scoring:z39.object({score:z39.number(),nameMatched:z39.boolean(),debugSourceMatched:z39.boolean(),anchorRelated:z39.boolean(),proximityPx:z39.number().nullable().optional()}).optional()}).nullable(),candidates:z39.array(z39.object({name:z39.string().nullable(),displayName:z39.string().nullable(),debugSource:z39.object({fileName:z39.string().nullable(),lineNumber:z39.number().int().nullable(),columnNumber:z39.number().int().nullable()}).nullable(),componentStack:z39.array(z39.string()),selection:z39.enum(["anchor","query","query+anchor","unknown"]),scoring:z39.object({score:z39.number(),nameMatched:z39.boolean(),debugSourceMatched:z39.boolean(),anchorRelated:z39.boolean(),proximityPx:z39.number().nullable().optional()}).optional(),domFootprintCount:z39.number().int()})).describe("Ranked candidate matches (best-first)."),elements:z39.array(z39.object({tagName:z39.string(),id:z39.string().nullable(),className:z39.string().nullable(),selectorHint:z39.string().nullable(),textPreview:z39.string().nullable(),boundingBox:z39.object({x:z39.number(),y:z39.number(),width:z39.number(),height:z39.number()}).nullable(),isVisible:z39.boolean(),isInViewport:z39.boolean()})),notes:z39.array(z39.string())}}async handle(context,args){let anchorSelector=args.anchorSelector,anchorX=args.anchorX,anchorY=args.anchorY,componentName=args.componentName,matchStrategy=args.matchStrategy??DEFAULT_MATCH_STRATEGY,fileNameHint=args.fileNameHint,lineNumber=args.lineNumber,maxElements=args.maxElements??DEFAULT_MAX_ELEMENTS,onlyVisible=args.onlyVisible??DEFAULT_ONLY_VISIBLE2,onlyInViewport=args.onlyInViewport??DEFAULT_ONLY_IN_VIEWPORT2,textPreviewMaxLength=args.textPreviewMaxLength??DEFAULT_TEXT_PREVIEW_MAX_LENGTH2,maxMatches=args.maxMatches??DEFAULT_MAX_MATCHES,hasAnchorSelector=typeof anchorSelector=="string"&&anchorSelector.trim().length>0,hasAnchorPoint=typeof anchorX=="number"&&typeof anchorY=="number",hasQuery=typeof componentName=="string"&&componentName.trim().length>0||typeof fileNameHint=="string"&&fileNameHint.trim().length>0||typeof lineNumber=="number";if(!hasAnchorSelector&&!hasAnchorPoint&&!hasQuery)throw new Error("Provide at least one targeting method: anchorSelector, (anchorX+anchorY), or componentName/debugSource hints.");return await context.page.evaluate(params=>{let anchorSelectorEval=params.anchorSelectorEval,anchorXEval=params.anchorXEval,anchorYEval=params.anchorYEval,componentNameEval=params.componentNameEval,matchStrategyEval=params.matchStrategyEval,fileNameHintEval=params.fileNameHintEval,lineNumberEval=params.lineNumberEval,maxElementsEval=params.maxElementsEval,onlyVisibleEval=params.onlyVisibleEval,onlyInViewportEval=params.onlyInViewportEval,textPreviewMaxLengthEval=params.textPreviewMaxLengthEval,maxMatchesEval=params.maxMatchesEval,maxRootsScan=params.maxRootsScan,maxFibersVisited=params.maxFibersVisited,stackLimit=params.stackLimit,notes=[];function isElement(v){return typeof Element<"u"&&v instanceof Element}function normalizeStr(v){if(typeof v!="string")return null;let t=v.trim();return t||null}function getFiberFromDomElement(el){let anyEl=el,keys=Object.keys(anyEl);for(let k of keys)if(k.startsWith("__reactFiber$")){let f=anyEl[k];if(f)return f}for(let k of keys)if(k.startsWith("__reactInternalInstance$")){let f=anyEl[k];if(f)return f}return null}function getDevtoolsHook(){let hook2=globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;return!hook2||typeof hook2.getFiberRoots!="function"?null:hook2}function getAllRootsFromHook(hook2){let roots2=[],renderers=hook2.renderers;return!renderers||typeof renderers.forEach!="function"?roots2:(renderers.forEach((_renderer,rendererId)=>{try{let set=hook2.getFiberRoots(rendererId);set&&typeof set.forEach=="function"&&set.forEach(root=>{root&&roots2.push(root)})}catch{}}),roots2.slice(0,maxRootsScan))}function getSeedFibersFromDomScan(maxSeeds){let out=[],els=Array.from(document.querySelectorAll("*")),step=els.length>5e3?Math.ceil(els.length/5e3):1;for(let i=0;i<els.length;i+=step){let el=els[i],f=getFiberFromDomElement(el);if(f&&(out.push(f),out.length>=maxSeeds))break}return out}function getFunctionDisplayName(fn){if(typeof fn!="function")return null;let anyFn=fn,dn=normalizeStr(anyFn.displayName);if(dn)return dn;let nm=normalizeStr(anyFn.name);return nm||null}function getFiberTypeName(fiber){if(!fiber)return null;let t=fiber.type??fiber.elementType??null;if(!t)return null;if(typeof t=="function")return getFunctionDisplayName(t);if(typeof t=="string")return t;let dn=normalizeStr(t.displayName);return dn||normalizeStr(t.name)}function getDisplayName(fiber){if(!fiber)return null;let t=fiber.type??fiber.elementType??null;if(!t)return null;let dn=normalizeStr(t.displayName);return dn||getFiberTypeName(fiber)}function getDebugSource(fiber){let seen=new Set,queue=[],push=x=>{x&&(seen.has(x)||(seen.add(x),queue.push(x)))};push(fiber),push(fiber?.alternate);for(let i=0;i<8;i++){let f=queue[i];if(!f)break;push(f?._debugOwner),push(f?._debugOwner?.alternate)}for(let f of queue){let src=f?._debugSource??f?._debugOwner?._debugSource??f?.type?._debugSource??f?.elementType?._debugSource??null;if(!src)continue;let fileName=normalizeStr(src.fileName)??null,lineNumber2=typeof src.lineNumber=="number"?src.lineNumber:null,columnNumber=typeof src.columnNumber=="number"?src.columnNumber:null;if(fileName||lineNumber2!==null||columnNumber!==null)return{fileName,lineNumber:lineNumber2,columnNumber}}return null}function isHostFiber(fiber){let sn=fiber?.stateNode;return isElement(sn)}function buildComponentStack(fiber,limit){let out=[],cur=fiber??null;for(let i=0;i<limit&&cur;i++){let name=getDisplayName(cur),host=isHostFiber(cur);name&&!host&&out.push(name),cur=cur.return??null}if(out.length===0){cur=fiber??null;for(let i=0;i<limit&&cur;i++){let name=getDisplayName(cur);name&&out.push(name),cur=cur.return??null}}let deduped=[];for(let n of out)(deduped.length===0||deduped[deduped.length-1]!==n)&&deduped.push(n);return deduped}function firstMeaningfulComponentAbove(fiber){let cur=fiber??null,visited=new Set;for(;cur;){let key=cur.alternate??cur;if(visited.has(key))break;if(visited.add(key),!isHostFiber(cur))return cur;cur=cur.return??null}return null}function matchesName(candidate,query2,strategy){if(!candidate)return!1;let a=candidate.trim(),b=query2.trim();return!a||!b?!1:strategy==="exact"?a===b:a.toLowerCase().includes(b.toLowerCase())}function matchesDebugSource(fiber,fileNameHint2,lineNumber2){let src=getDebugSource(fiber);if(!src)return!1;if(fileNameHint2){let hint=fileNameHint2.trim();if(hint){let fn=(src.fileName??"").trim();if(!fn)return!1;let fnLower=fn.toLowerCase(),hintLower=hint.toLowerCase();if(!(fnLower.endsWith(hintLower)||fnLower.includes(hintLower)))return!1}}return!(typeof lineNumber2=="number"&&(src.lineNumber===null||Math.abs(src.lineNumber-lineNumber2)>3))}function toStartFiber(rootOrFiber){if(!rootOrFiber)return null;let maybeCurrent=rootOrFiber.current;return maybeCurrent?maybeCurrent.child??maybeCurrent:rootOrFiber}function pickAnchorElement(anchorSelector2,x,y){if(anchorSelector2&&anchorSelector2.trim()){let el=document.querySelector(anchorSelector2);return el||(notes.push(`anchorSelector did not match any element: ${anchorSelector2}`),null)}if(typeof x=="number"&&typeof y=="number"){let el=document.elementFromPoint(x,y);return el||(notes.push(`anchorPoint did not hit any element: (${x}, ${y})`),null)}return null}function selectorHintFor(el){let dt=normalizeStr(el.getAttribute("data-testid"))??normalizeStr(el.getAttribute("data-test-id"))??normalizeStr(el.getAttribute("data-test"))??null;if(dt)return`[data-testid='${dt.replace(/'/g,"\\'")}']`;let ds=normalizeStr(el.getAttribute("data-selector"))??null;if(ds)return`[data-selector='${ds.replace(/'/g,"\\'")}']`;if(el.id)try{return`#${CSS.escape(el.id)}`}catch{return`#${el.id}`}return el.tagName.toLowerCase()}function textPreviewFor(el,maxLen){let aria=normalizeStr(el.getAttribute("aria-label"))??null;if(aria)return aria.slice(0,maxLen);let txt=String(el.innerText??el.textContent??"").trim();return txt?txt.slice(0,maxLen):null}function computeRuntime(el){let r=el.getBoundingClientRect(),s=getComputedStyle(el),isVisible=s.display!=="none"&&s.visibility!=="hidden"&&parseFloat(s.opacity||"1")>0&&r.width>0&&r.height>0,vw=window.innerWidth,vh=window.innerHeight,isInViewport=r.right>0&&r.bottom>0&&r.left<vw&&r.top<vh,boundingBox=Number.isFinite(r.x)&&Number.isFinite(r.y)&&Number.isFinite(r.width)&&Number.isFinite(r.height)?{x:r.x,y:r.y,width:r.width,height:r.height}:null,centerX=r.left+r.width/2,centerY=r.top+r.height/2;return{boundingBox,isVisible,isInViewport,centerX,centerY}}function collectDomElementsFromFiberSubtree(fiber,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2){let out=[],seen=new Set,stack=[];fiber&&stack.push(fiber);let visited=new Set;for(;stack.length>0&&!(out.length>=maxElements2);){let f=stack.pop();if(!f)continue;let key=f.alternate??f;if(visited.has(key))continue;if(visited.add(key),isHostFiber(f)){let el=f.stateNode;if(!seen.has(el)){seen.add(el);let rt=computeRuntime(el);if(!(onlyVisible2&&!rt.isVisible)){if(!(onlyInViewport2&&!rt.isInViewport)){let tagName=el.tagName.toLowerCase(),id=el.id?String(el.id):null,classNameRaw=el.className,className=typeof classNameRaw=="string"&&classNameRaw.trim()?classNameRaw.trim():null;out.push({tagName,id,className,selectorHint:selectorHintFor(el),textPreview:textPreviewFor(el,textPreviewMaxLength2),boundingBox:rt.boundingBox,isVisible:rt.isVisible,isInViewport:rt.isInViewport})}}}}let child=f.child??null,sibling=f.sibling??null;child&&stack.push(child),sibling&&stack.push(sibling)}return out}function findFibersByQuery(roots2,query2,maxMatches2){let nameQ=query2.componentName?query2.componentName.trim():void 0,hasNameQ=!!nameQ,fileQ=query2.fileNameHint?query2.fileNameHint.trim():void 0,hasFileQ=!!fileQ,hasLineQ=typeof query2.lineNumber=="number";if(!(hasNameQ||hasFileQ||hasLineQ))return[];let stack=[];for(let r of roots2){let start=toStartFiber(r);start&&stack.push(start)}let visited=new Set,matches=[],visitedCount=0;for(;stack.length>0&&!(matches.length>=maxMatches2);){let f=stack.pop();if(!f)continue;let key=f.alternate??f;if(visited.has(key))continue;if(visited.add(key),visitedCount++,visitedCount>maxFibersVisited){notes.push(`Fiber traversal safety cap reached (${maxFibersVisited}). Results may be incomplete.`);break}let dn=getDisplayName(f),tn=getFiberTypeName(f),nameMatches=hasNameQ?matchesName(dn,nameQ,query2.matchStrategy)||matchesName(tn,nameQ,query2.matchStrategy):!0,srcMatches=hasFileQ||hasLineQ?matchesDebugSource(f,fileQ,query2.lineNumber):!0;nameMatches&&srcMatches&&matches.push(f);let child=f.child??null,sibling=f.sibling??null;child&&stack.push(child),sibling&&stack.push(sibling)}return matches}function scoreCandidate(fiber,anchorEl2,anchorX2,anchorY2,q,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2){let nameQ=q.componentName?q.componentName.trim():void 0,hasNameQ=!!nameQ,dn=getDisplayName(fiber),tn=getFiberTypeName(fiber),nameMatched=hasNameQ?matchesName(dn,nameQ,q.matchStrategy)||matchesName(tn,nameQ,q.matchStrategy):!1,debugSourceMatched=normalizeStr(q.fileNameHint)||typeof q.lineNumber=="number"?matchesDebugSource(fiber,normalizeStr(q.fileNameHint)??void 0,typeof q.lineNumber=="number"?q.lineNumber:void 0):!1,dom=collectDomElementsFromFiberSubtree(fiber,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2),anchorRelated=!1,proximityPx=null;if(anchorEl2){let anchorSel=selectorHintFor(anchorEl2);if(anchorRelated=dom.some(d=>!d.selectorHint||!anchorSel?!1:d.selectorHint===anchorSel)||dom.some(d=>{if(!d.boundingBox)return!1;let r=anchorEl2.getBoundingClientRect(),bb=d.boundingBox;return r.left<bb.x+bb.width&&r.left+r.width>bb.x&&r.top<bb.y+bb.height&&r.top+r.height>bb.y}),typeof anchorX2=="number"&&typeof anchorY2=="number"&&dom.length>0){let best2=null;for(let item of dom){if(!item.boundingBox)continue;let cx=item.boundingBox.x+item.boundingBox.width/2,cy=item.boundingBox.y+item.boundingBox.height/2,dx=cx-anchorX2,dy=cy-anchorY2,dist=Math.sqrt(dx*dx+dy*dy);(best2===null||dist<best2)&&(best2=dist)}proximityPx=best2}}let score=0;if(score+=nameMatched?3:0,score+=debugSourceMatched?3:0,score+=anchorRelated?2:0,score+=Math.min(1.5,dom.length/25),typeof proximityPx=="number"&&Number.isFinite(proximityPx)){let p=Math.max(0,Math.min(1,1-proximityPx/1e3));score+=1.5*p}return{score,nameMatched,debugSourceMatched,anchorRelated,proximityPx,dom}}let hook=getDevtoolsHook(),reactDetected=!!hook;reactDetected?notes.push("React DevTools hook detected. Root discovery will use getFiberRoots() (more reliable)."):(notes.push("React DevTools hook was not detected (__REACT_DEVTOOLS_GLOBAL_HOOK__)."),notes.push('If you are using a persistent/headful browser profile, install the "React Developer Tools" Chrome extension in that profile (manual step by the user) to enable reliable root discovery and component search.'));let fiberDetected=(()=>{let body=document.body;return body?!!(getFiberFromDomElement(body)||getSeedFibersFromDomScan(1).length>0):!1})();fiberDetected?notes.push("React Fiber pointers detected on DOM nodes. Anchor-based mapping may still work without the DevTools hook (best-effort)."):notes.push("No React Fiber pointers were detected on DOM nodes (__reactFiber$...). This can happen if the page is not React, React has not rendered yet, or internals are unavailable.");let anchorEl=pickAnchorElement(anchorSelectorEval,anchorXEval,anchorYEval),anchorSelected=null;if(anchorEl){let anchorFiber=getFiberFromDomElement(anchorEl);if(anchorFiber){let candidateA=anchorFiber,candidateB=anchorFiber.alternate??null,chosen=candidateA;candidateB&&(candidateB.child||candidateB.sibling)&&!(candidateA.child||candidateA.sibling)&&(chosen=candidateB);let nearestComponent=firstMeaningfulComponentAbove(chosen);nearestComponent?anchorSelected=nearestComponent:notes.push("Anchor fiber found but no meaningful component fiber was found above it.")}else notes.push("Anchor element found but React fiber was not found on it.")}let roots=[],rootDiscovery="none";if(hook){let r=getAllRootsFromHook(hook);r.length>0&&(roots=r,rootDiscovery="devtools-hook")}if(roots.length===0&&fiberDetected){let seeds=getSeedFibersFromDomScan(10);seeds.length>0&&(roots=seeds,rootDiscovery="dom-fiber-scan",notes.push("Using DOM fiber scan as fallback root discovery (best-effort)."))}let hasQuery2=!!normalizeStr(componentNameEval)||!!normalizeStr(fileNameHintEval)||typeof lineNumberEval=="number",query={componentName:normalizeStr(componentNameEval)??void 0,matchStrategy:matchStrategyEval,fileNameHint:normalizeStr(fileNameHintEval)??void 0,lineNumber:typeof lineNumberEval=="number"?lineNumberEval:void 0},queryMatches=[];hasQuery2&&(roots.length>0?queryMatches=findFibersByQuery(roots,query,Math.max(1,Math.floor(maxMatchesEval))):notes.push("Query was provided but no roots could be discovered. Provide an anchorSelector/anchorX+anchorY so we can map via DOM fiber pointers."));let candidates=[];anchorSelected&&candidates.push(anchorSelected);for(let f of queryMatches)candidates.push(f);let uniq=[],seenCand=new Set;for(let f of candidates){let key=f?.alternate??f;f&&!seenCand.has(key)&&(seenCand.add(key),uniq.push(f))}if(uniq.length===0)return reactDetected||notes.push("No candidates found. Without the DevTools hook, component search may be unreliable unless you provide a strong anchor."),{reactDetected,fiberDetected,rootDiscovery,component:null,candidates:[],elements:[],notes:[...notes,"Failed to select a target component fiber. Provide a better anchor, or ensure React is present and render has happened."]};let scored=[];for(let f of uniq){let s=scoreCandidate(f,anchorEl,anchorXEval,anchorYEval,query,maxElementsEval,onlyVisibleEval,onlyInViewportEval,textPreviewMaxLengthEval),selection="unknown",isAnchor=anchorSelected?(anchorSelected.alternate??anchorSelected)===(f.alternate??f):!1,isQuery=queryMatches.some(qf=>(qf.alternate??qf)===(f.alternate??f));isAnchor&&isQuery?selection="query+anchor":isAnchor?selection="anchor":isQuery?selection="query":selection="unknown";let name=getFiberTypeName(f),displayName=getDisplayName(f),debugSource=getDebugSource(f),componentStack=buildComponentStack(f,stackLimit),meta={name,displayName,debugSource,componentStack,selection,scoring:{score:s.score,nameMatched:s.nameMatched,debugSourceMatched:s.debugSourceMatched,anchorRelated:s.anchorRelated,proximityPx:s.proximityPx}};scored.push({fiber:f,meta,dom:s.dom,domFootprintCount:s.dom.length})}scored.sort((a,b)=>(b.meta.scoring?.score??0)-(a.meta.scoring?.score??0));let best=scored[0],outCandidates=scored.slice(0,Math.max(1,Math.floor(maxMatchesEval))).map(c=>({...c.meta,domFootprintCount:c.domFootprintCount})),elements=best.dom;return hasQuery2&&(query.fileNameHint||typeof query.lineNumber=="number")&&(outCandidates.some(c=>!!(c.debugSource?.fileName||c.debugSource?.lineNumber!==null))||(notes.push("debugSource hints were provided, but no _debugSource information was found on matched fibers. This is common in production builds and some dev toolchains."),notes.push('React DevTools may still display "Rendered by <file>:<line>" using sourcemaps/stack traces even when fiber._debugSource is null.'))),elements.length>=maxElementsEval&&notes.push(`Element list was truncated at maxElements=${maxElementsEval}. Increase maxElements if needed.`),elements.length===0&&notes.push("No DOM elements were returned for the selected component. It may render no host elements, or filtering (onlyVisible/onlyInViewport) removed them."),!reactDetected&&fiberDetected?notes.push("React DevTools hook was not detected, but DOM fiber pointers were found. Using DOM-fiber scanning and anchor-based mapping (best-effort)."):!reactDetected&&!fiberDetected&&notes.push("React DevTools hook was not detected and no DOM fiber pointers were found. Component-to-DOM mapping is unavailable on this page."),{reactDetected,fiberDetected,rootDiscovery,component:best.meta,candidates:outCandidates,elements,notes:[...notes,"Component metadata is best-effort. Wrappers (memo/forwardRef/HOCs), Suspense/Offscreen, and portals may make names/stacks noisy."]}},{anchorSelectorEval:typeof anchorSelector=="string"&&anchorSelector.trim()?anchorSelector.trim():void 0,anchorXEval:typeof anchorX=="number"&&Number.isFinite(anchorX)?Math.floor(anchorX):void 0,anchorYEval:typeof anchorY=="number"&&Number.isFinite(anchorY)?Math.floor(anchorY):void 0,componentNameEval:typeof componentName=="string"&&componentName.trim()?componentName.trim():void 0,matchStrategyEval:matchStrategy,fileNameHintEval:typeof fileNameHint=="string"&&fileNameHint.trim()?fileNameHint.trim():void 0,lineNumberEval:typeof lineNumber=="number"&&Number.isFinite(lineNumber)?Math.floor(lineNumber):void 0,maxElementsEval:Math.max(1,Math.floor(maxElements)),onlyVisibleEval:!!onlyVisible,onlyInViewportEval:!!onlyInViewport,textPreviewMaxLengthEval:Math.max(1,Math.floor(textPreviewMaxLength)),maxMatchesEval:Math.max(1,Math.floor(maxMatches)),maxRootsScan:INTERNAL_MAX_ROOTS_SCAN,maxFibersVisited:INTERNAL_MAX_FIBERS_VISITED,stackLimit:DEFAULT_STACK_LIMIT})}};var tools8=[new GetComponentForElement,new GetElementForComponent];import{z as z40}from"zod";var JsInBrowser=class{name(){return"run_js-in-browser"}description(){return`
488
- Runs JavaScript inside the active page (page.evaluate). Executes in page context: window, document, DOM, Web APIs; can read/modify page state.
489
- Use to inspect or mutate DOM, read client variables, trigger browser logic, or extract computed values.
490
- Return value must be serializable; script runs as a function body so "return" and top-level "await" are valid. No Node.js APIs.
491
- When using fetch(), check response.ok and Content-Type before .json()\u2014error pages often return HTML and cause invalid JSON if parsed.
492
- `.trim()}inputSchema(){return{script:z40.string().describe("JavaScript to run in page context. Use return for serializable result; runs as function body.")}}outputSchema(){return{result:z40.any().describe(`
493
- The result of the evaluation. This value can be of any type, including primitives, arrays, or objects.
494
- It represents the direct return value of the JavaScript expression executed in the page context.
495
- The structure and type of this value are not constrained and depend entirely on the evaluated code.`)}}async handle(context,args){let wrapped=`(async function() { ${args.script} })()`;return{result:await context.page.evaluate(wrapped)}}};import crypto3 from"node:crypto";import vm from"node:vm";import{z as z41}from"zod";var DEFAULT_TIMEOUT_MS4=5e3,MAX_TIMEOUT_MS=3e4;function _toJsonSafe(value){if(value==null||typeof value=="string"||typeof value=="number"||typeof value=="boolean")return value;try{return JSON.parse(JSON.stringify(value))}catch{return String(value)}}var JsInSandbox=class{name(){return"run_js-in-sandbox"}description(){return`
496
- Runs custom JavaScript inside a Node.js VM sandbox.
497
-
498
- This runs on the MCP SERVER (not in the browser).
499
-
500
- Available bindings:
501
- - page: Playwright Page (main interaction surface)
502
- - console: captured logs (log/warn/error)
503
- - sleep(ms): async helper
504
-
505
- Safe built-ins:
506
- - Math, JSON, Number, String, Boolean, Array, Object, Date, RegExp
507
- - isFinite, isNaN, parseInt, parseFloat
508
- - URL, URLSearchParams
509
- - TextEncoder, TextDecoder
510
- - structuredClone
511
- - crypto.randomUUID()
512
- - AbortController
513
- - setTimeout / clearTimeout
514
-
515
- NOT available:
516
- - require, process, fs, Buffer
517
- - globalThis
518
-
519
- This is NOT a security boundary. Intended for trusted automation logic.
520
- `.trim()}inputSchema(){return{code:z41.string().describe("JavaScript to run in Node VM (async allowed)."),timeoutMs:z41.number().int().min(0).max(MAX_TIMEOUT_MS).optional().default(DEFAULT_TIMEOUT_MS4).describe("Max VM CPU time ms; does not time out awaited Promises.")}}outputSchema(){return{result:z41.any().describe(`
521
- Return value of the sandboxed code (best-effort JSON-safe).
522
- If user returns undefined but logs exist, returns { logs }.
523
- If error occurs, returns { error, logs }.`)}}async handle(context,args){let logs=[],sandboxConsole={log:(...items)=>{logs.push({level:"log",message:items.map(x=>String(x)).join(" ")})},warn:(...items)=>{logs.push({level:"warn",message:items.map(x=>String(x)).join(" ")})},error:(...items)=>{logs.push({level:"error",message:items.map(x=>String(x)).join(" ")})}},sleep=async ms=>{let d=Math.max(0,Math.floor(ms));await new Promise(resolve=>{setTimeout(()=>resolve(),d)})},sandbox={page:context.page,console:sandboxConsole,sleep,Math,JSON,Number,String,Boolean,Array,Object,Date,RegExp,isFinite,isNaN,parseInt,parseFloat,URL,URLSearchParams,TextEncoder,TextDecoder,structuredClone,crypto:{randomUUID:crypto3.randomUUID},AbortController,setTimeout,clearTimeout},vmContext=vm.createContext(sandbox),wrappedSource=`
524
- 'use strict';
525
- (async () => {
526
- ${String(args.code??"")}
527
- })()
528
- `.trim();try{let value=new vm.Script(wrappedSource,{filename:"mcp-sandbox.js"}).runInContext(vmContext,{timeout:args.timeoutMs??DEFAULT_TIMEOUT_MS4}),awaited=await Promise.resolve(value);return awaited===void 0&&logs.length>0?{result:{logs}}:{result:_toJsonSafe(awaited)}}catch(e){return{result:{error:e instanceof Error?e.stack??e.message:String(e),logs}}}}};var tools9=[new JsInBrowser,new JsInSandbox];import picomatch from"picomatch";var STORE_BY_CONTEXT2=new WeakMap;function _ensureStore2(ctx){let existing=STORE_BY_CONTEXT2.get(ctx);if(existing)return existing;let created={stubs:[],installed:!1};return STORE_BY_CONTEXT2.set(ctx,created),created}function _nowId(){let t=Date.now(),r=Math.floor(Math.random()*1e6);return`${t.toString(36)}-${r.toString(36)}`}async function _sleep(ms){ms<=0||await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}function _normalizeTimes(times){return typeof times!="number"||!Number.isFinite(times)||times<=0?-1:Math.floor(times)}function _isTimesRemaining(times,usedCount){return times===-1?!0:usedCount<times}function _normalizeChance(chance){if(typeof chance=="number"&&Number.isFinite(chance))return Math.max(0,Math.min(1,chance))}function _shouldApplyChance(chance){return chance===void 0?!0:chance<=0?!1:chance>=1?!0:Math.random()<chance}function _compileMatcher(pattern){let p=pattern.trim();return p?picomatch(p,{dot:!0,nocase:!1}):()=>!1}function _pickStub(stubs,url){for(let s of stubs)if(s.enabled&&_isTimesRemaining(s.times,s.usedCount)&&s.matcher(url)&&!(s.kind==="mock_http_response"&&!_shouldApplyChance(s.chance)))return s}async function _applyStub(route,stub){let req=route.request();if(stub.usedCount++,stub.delayMs>0&&await _sleep(stub.delayMs),stub.kind==="mock_http_response"){if(stub.action==="abort"){let code=stub.abortErrorCode??"failed";await route.abort(code);return}let status=typeof stub.status=="number"?stub.status:200,headers=stub.headers??{},body=typeof stub.body=="string"?stub.body:"";await route.fulfill({status,headers,body});return}else if(stub.kind==="intercept_http_request"){let overrides={headers:{...req.headers(),...stub.modifications.headers??{}}};typeof stub.modifications.method=="string"&&stub.modifications.method.trim()&&(overrides.method=stub.modifications.method.trim().toUpperCase()),typeof stub.modifications.body=="string"&&(overrides.postData=stub.modifications.body),await route.continue(overrides);return}await route.continue()}async function ensureRoutingInstalled(ctx){let store=_ensureStore2(ctx);store.installed||(await ctx.route("**/*",async route=>{let url=route.request().url(),innerStore=_ensureStore2(ctx),stub=_pickStub(innerStore.stubs,url);if(!stub){await route.continue();return}try{await _applyStub(route,stub)}finally{_isTimesRemaining(stub.times,stub.usedCount)||(innerStore.stubs=innerStore.stubs.filter(x=>x.id!==stub.id))}}),store.installed=!0)}function addMockHttpResponseStub(ctx,input){let store=_ensureStore2(ctx),stub={...input,kind:"mock_http_response",id:_nowId(),usedCount:0,matcher:_compileMatcher(input.pattern),times:_normalizeTimes(input.times),delayMs:Math.max(0,Math.floor(input.delayMs)),chance:_normalizeChance(input.chance)};return store.stubs.push(stub),stub}function addHttpInterceptRequestStub(ctx,input){let store=_ensureStore2(ctx),stub={...input,kind:"intercept_http_request",id:_nowId(),usedCount:0,matcher:_compileMatcher(input.pattern),times:_normalizeTimes(input.times),delayMs:Math.max(0,Math.floor(input.delayMs))};return store.stubs.push(stub),stub}function clearStub(ctx,id){let store=_ensureStore2(ctx);if(!id){let n=store.stubs.length;return store.stubs=[],n}let before=store.stubs.length;return store.stubs=store.stubs.filter(s=>s.id!==id),before-store.stubs.length}function listStubs(ctx){return[..._ensureStore2(ctx).stubs]}function normalizeDelayMs(delayMs){return typeof delayMs!="number"||!Number.isFinite(delayMs)||delayMs<=0?0:Math.floor(delayMs)}function normalizeTimesPublic(times){return _normalizeTimes(times)}function normalizeHeaders(headers){if(!headers)return;let out={};for(let[k,v]of Object.entries(headers))out[String(k)]=String(v);return out}function normalizeBody(body){if(typeof body=="string")return body;if(body&&typeof body=="object")return JSON.stringify(body)}function normalizeAbortCode(code){if(typeof code!="string")return;let v=code.trim();if(v)return v}function normalizeMethod(method){if(typeof method!="string")return;let v=method.trim();if(v)return v.toUpperCase()}function normalizeChance(chance){return _normalizeChance(chance)}import{z as z42}from"zod";var Clear=class{name(){return"stub_clear"}description(){return`
492
+ `.trim()}inputSchema(){return{anchorSelector:z39.string().optional().describe("Anchor selector/ref."),anchorX:z39.number().int().nonnegative().optional().describe("Viewport X when anchorSelector omitted."),anchorY:z39.number().int().nonnegative().optional().describe("Viewport Y when anchorSelector omitted."),componentName:z39.string().optional(),matchStrategy:z39.enum(["exact","contains"]).optional().default(DEFAULT_MATCH_STRATEGY),fileNameHint:z39.string().optional().describe("File hint."),lineNumber:z39.number().int().positive().optional(),maxElements:z39.number().int().positive().optional().default(DEFAULT_MAX_ELEMENTS),onlyVisible:z39.boolean().optional().default(DEFAULT_ONLY_VISIBLE2),onlyInViewport:z39.boolean().optional().default(DEFAULT_ONLY_IN_VIEWPORT2),textPreviewMaxLength:z39.number().int().positive().optional().default(DEFAULT_TEXT_PREVIEW_MAX_LENGTH2),maxMatches:z39.number().int().positive().optional().default(DEFAULT_MAX_MATCHES)}}outputSchema(){return{reactDetected:z39.boolean().describe("True if __REACT_DEVTOOLS_GLOBAL_HOOK__ looks available."),fiberDetected:z39.boolean().describe("True if DOM appears to contain React Fiber pointers (__reactFiber$...)."),rootDiscovery:z39.enum(["devtools-hook","dom-fiber-scan","none"]).describe("How roots were discovered."),component:z39.object({name:z39.string().nullable(),displayName:z39.string().nullable(),debugSource:z39.object({fileName:z39.string().nullable(),lineNumber:z39.number().int().nullable(),columnNumber:z39.number().int().nullable()}).nullable(),componentStack:z39.array(z39.string()),selection:z39.enum(["anchor","query","query+anchor","unknown"]),scoring:z39.object({score:z39.number(),nameMatched:z39.boolean(),debugSourceMatched:z39.boolean(),anchorRelated:z39.boolean(),proximityPx:z39.number().nullable().optional()}).optional()}).nullable(),candidates:z39.array(z39.object({name:z39.string().nullable(),displayName:z39.string().nullable(),debugSource:z39.object({fileName:z39.string().nullable(),lineNumber:z39.number().int().nullable(),columnNumber:z39.number().int().nullable()}).nullable(),componentStack:z39.array(z39.string()),selection:z39.enum(["anchor","query","query+anchor","unknown"]),scoring:z39.object({score:z39.number(),nameMatched:z39.boolean(),debugSourceMatched:z39.boolean(),anchorRelated:z39.boolean(),proximityPx:z39.number().nullable().optional()}).optional(),domFootprintCount:z39.number().int()})).describe("Ranked candidate matches (best-first)."),elements:z39.array(z39.object({tagName:z39.string(),id:z39.string().nullable(),className:z39.string().nullable(),selectorHint:z39.string().nullable(),textPreview:z39.string().nullable(),boundingBox:z39.object({x:z39.number(),y:z39.number(),width:z39.number(),height:z39.number()}).nullable(),isVisible:z39.boolean(),isInViewport:z39.boolean()})),notes:z39.array(z39.string())}}async handle(context,args){let anchorSelector=args.anchorSelector,anchorX=args.anchorX,anchorY=args.anchorY,componentName=args.componentName,matchStrategy=args.matchStrategy??DEFAULT_MATCH_STRATEGY,fileNameHint=args.fileNameHint,lineNumber=args.lineNumber,maxElements=args.maxElements??DEFAULT_MAX_ELEMENTS,onlyVisible=args.onlyVisible??DEFAULT_ONLY_VISIBLE2,onlyInViewport=args.onlyInViewport??DEFAULT_ONLY_IN_VIEWPORT2,textPreviewMaxLength=args.textPreviewMaxLength??DEFAULT_TEXT_PREVIEW_MAX_LENGTH2,maxMatches=args.maxMatches??DEFAULT_MAX_MATCHES,hasAnchorSelector=typeof anchorSelector=="string"&&anchorSelector.trim().length>0,hasAnchorPoint=typeof anchorX=="number"&&typeof anchorY=="number",hasQuery=typeof componentName=="string"&&componentName.trim().length>0||typeof fileNameHint=="string"&&fileNameHint.trim().length>0||typeof lineNumber=="number";if(!hasAnchorSelector&&!hasAnchorPoint&&!hasQuery)throw new Error("Provide at least one targeting method: anchorSelector, (anchorX+anchorY), or componentName/debugSource hints.");return await context.page.evaluate(params=>{let anchorSelectorEval=params.anchorSelectorEval,anchorXEval=params.anchorXEval,anchorYEval=params.anchorYEval,componentNameEval=params.componentNameEval,matchStrategyEval=params.matchStrategyEval,fileNameHintEval=params.fileNameHintEval,lineNumberEval=params.lineNumberEval,maxElementsEval=params.maxElementsEval,onlyVisibleEval=params.onlyVisibleEval,onlyInViewportEval=params.onlyInViewportEval,textPreviewMaxLengthEval=params.textPreviewMaxLengthEval,maxMatchesEval=params.maxMatchesEval,maxRootsScan=params.maxRootsScan,maxFibersVisited=params.maxFibersVisited,stackLimit=params.stackLimit,notes=[];function isElement(v){return typeof Element<"u"&&v instanceof Element}function normalizeStr(v){if(typeof v!="string")return null;let t=v.trim();return t||null}function getFiberFromDomElement(el){let anyEl=el,keys=Object.keys(anyEl);for(let k of keys)if(k.startsWith("__reactFiber$")){let f=anyEl[k];if(f)return f}for(let k of keys)if(k.startsWith("__reactInternalInstance$")){let f=anyEl[k];if(f)return f}return null}function getDevtoolsHook(){let hook2=globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;return!hook2||typeof hook2.getFiberRoots!="function"?null:hook2}function getAllRootsFromHook(hook2){let roots2=[],renderers=hook2.renderers;return!renderers||typeof renderers.forEach!="function"?roots2:(renderers.forEach((_renderer,rendererId)=>{try{let set=hook2.getFiberRoots(rendererId);set&&typeof set.forEach=="function"&&set.forEach(root=>{root&&roots2.push(root)})}catch{}}),roots2.slice(0,maxRootsScan))}function getSeedFibersFromDomScan(maxSeeds){let out=[],els=Array.from(document.querySelectorAll("*")),step=els.length>5e3?Math.ceil(els.length/5e3):1;for(let i=0;i<els.length;i+=step){let el=els[i],f=getFiberFromDomElement(el);if(f&&(out.push(f),out.length>=maxSeeds))break}return out}function getFunctionDisplayName(fn){if(typeof fn!="function")return null;let anyFn=fn,dn=normalizeStr(anyFn.displayName);if(dn)return dn;let nm=normalizeStr(anyFn.name);return nm||null}function getFiberTypeName(fiber){if(!fiber)return null;let t=fiber.type??fiber.elementType??null;if(!t)return null;if(typeof t=="function")return getFunctionDisplayName(t);if(typeof t=="string")return t;let dn=normalizeStr(t.displayName);return dn||normalizeStr(t.name)}function getDisplayName(fiber){if(!fiber)return null;let t=fiber.type??fiber.elementType??null;if(!t)return null;let dn=normalizeStr(t.displayName);return dn||getFiberTypeName(fiber)}function getDebugSource(fiber){let seen=new Set,queue=[],push=x=>{x&&(seen.has(x)||(seen.add(x),queue.push(x)))};push(fiber),push(fiber?.alternate);for(let i=0;i<8;i++){let f=queue[i];if(!f)break;push(f?._debugOwner),push(f?._debugOwner?.alternate)}for(let f of queue){let src=f?._debugSource??f?._debugOwner?._debugSource??f?.type?._debugSource??f?.elementType?._debugSource??null;if(!src)continue;let fileName=normalizeStr(src.fileName)??null,lineNumber2=typeof src.lineNumber=="number"?src.lineNumber:null,columnNumber=typeof src.columnNumber=="number"?src.columnNumber:null;if(fileName||lineNumber2!==null||columnNumber!==null)return{fileName,lineNumber:lineNumber2,columnNumber}}return null}function isHostFiber(fiber){let sn=fiber?.stateNode;return isElement(sn)}function buildComponentStack(fiber,limit){let out=[],cur=fiber??null;for(let i=0;i<limit&&cur;i++){let name=getDisplayName(cur),host=isHostFiber(cur);name&&!host&&out.push(name),cur=cur.return??null}if(out.length===0){cur=fiber??null;for(let i=0;i<limit&&cur;i++){let name=getDisplayName(cur);name&&out.push(name),cur=cur.return??null}}let deduped=[];for(let n of out)(deduped.length===0||deduped[deduped.length-1]!==n)&&deduped.push(n);return deduped}function firstMeaningfulComponentAbove(fiber){let cur=fiber??null,visited=new Set;for(;cur;){let key=cur.alternate??cur;if(visited.has(key))break;if(visited.add(key),!isHostFiber(cur))return cur;cur=cur.return??null}return null}function matchesName(candidate,query2,strategy){if(!candidate)return!1;let a=candidate.trim(),b=query2.trim();return!a||!b?!1:strategy==="exact"?a===b:a.toLowerCase().includes(b.toLowerCase())}function matchesDebugSource(fiber,fileNameHint2,lineNumber2){let src=getDebugSource(fiber);if(!src)return!1;if(fileNameHint2){let hint=fileNameHint2.trim();if(hint){let fn=(src.fileName??"").trim();if(!fn)return!1;let fnLower=fn.toLowerCase(),hintLower=hint.toLowerCase();if(!(fnLower.endsWith(hintLower)||fnLower.includes(hintLower)))return!1}}return!(typeof lineNumber2=="number"&&(src.lineNumber===null||Math.abs(src.lineNumber-lineNumber2)>3))}function toStartFiber(rootOrFiber){if(!rootOrFiber)return null;let maybeCurrent=rootOrFiber.current;return maybeCurrent?maybeCurrent.child??maybeCurrent:rootOrFiber}function pickAnchorElement(anchorSelector2,x,y){if(anchorSelector2&&anchorSelector2.trim()){let el=document.querySelector(anchorSelector2);return el||(notes.push(`anchorSelector did not match any element: ${anchorSelector2}`),null)}if(typeof x=="number"&&typeof y=="number"){let el=document.elementFromPoint(x,y);return el||(notes.push(`anchorPoint did not hit any element: (${x}, ${y})`),null)}return null}function selectorHintFor(el){let dt=normalizeStr(el.getAttribute("data-testid"))??normalizeStr(el.getAttribute("data-test-id"))??normalizeStr(el.getAttribute("data-test"))??null;if(dt)return`[data-testid='${dt.replace(/'/g,"\\'")}']`;let ds=normalizeStr(el.getAttribute("data-selector"))??null;if(ds)return`[data-selector='${ds.replace(/'/g,"\\'")}']`;if(el.id)try{return`#${CSS.escape(el.id)}`}catch{return`#${el.id}`}return el.tagName.toLowerCase()}function textPreviewFor(el,maxLen){let aria=normalizeStr(el.getAttribute("aria-label"))??null;if(aria)return aria.slice(0,maxLen);let txt=String(el.innerText??el.textContent??"").trim();return txt?txt.slice(0,maxLen):null}function computeRuntime(el){let r=el.getBoundingClientRect(),s=getComputedStyle(el),isVisible=s.display!=="none"&&s.visibility!=="hidden"&&parseFloat(s.opacity||"1")>0&&r.width>0&&r.height>0,vw=window.innerWidth,vh=window.innerHeight,isInViewport=r.right>0&&r.bottom>0&&r.left<vw&&r.top<vh,boundingBox=Number.isFinite(r.x)&&Number.isFinite(r.y)&&Number.isFinite(r.width)&&Number.isFinite(r.height)?{x:r.x,y:r.y,width:r.width,height:r.height}:null,centerX=r.left+r.width/2,centerY=r.top+r.height/2;return{boundingBox,isVisible,isInViewport,centerX,centerY}}function collectDomElementsFromFiberSubtree(fiber,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2){let out=[],seen=new Set,stack=[];fiber&&stack.push(fiber);let visited=new Set;for(;stack.length>0&&!(out.length>=maxElements2);){let f=stack.pop();if(!f)continue;let key=f.alternate??f;if(visited.has(key))continue;if(visited.add(key),isHostFiber(f)){let el=f.stateNode;if(!seen.has(el)){seen.add(el);let rt=computeRuntime(el);if(!(onlyVisible2&&!rt.isVisible)){if(!(onlyInViewport2&&!rt.isInViewport)){let tagName=el.tagName.toLowerCase(),id=el.id?String(el.id):null,classNameRaw=el.className,className=typeof classNameRaw=="string"&&classNameRaw.trim()?classNameRaw.trim():null;out.push({tagName,id,className,selectorHint:selectorHintFor(el),textPreview:textPreviewFor(el,textPreviewMaxLength2),boundingBox:rt.boundingBox,isVisible:rt.isVisible,isInViewport:rt.isInViewport})}}}}let child=f.child??null,sibling=f.sibling??null;child&&stack.push(child),sibling&&stack.push(sibling)}return out}function findFibersByQuery(roots2,query2,maxMatches2){let nameQ=query2.componentName?query2.componentName.trim():void 0,hasNameQ=!!nameQ,fileQ=query2.fileNameHint?query2.fileNameHint.trim():void 0,hasFileQ=!!fileQ,hasLineQ=typeof query2.lineNumber=="number";if(!(hasNameQ||hasFileQ||hasLineQ))return[];let stack=[];for(let r of roots2){let start=toStartFiber(r);start&&stack.push(start)}let visited=new Set,matches=[],visitedCount=0;for(;stack.length>0&&!(matches.length>=maxMatches2);){let f=stack.pop();if(!f)continue;let key=f.alternate??f;if(visited.has(key))continue;if(visited.add(key),visitedCount++,visitedCount>maxFibersVisited){notes.push(`Fiber traversal safety cap reached (${maxFibersVisited}). Results may be incomplete.`);break}let dn=getDisplayName(f),tn=getFiberTypeName(f),nameMatches=hasNameQ?matchesName(dn,nameQ,query2.matchStrategy)||matchesName(tn,nameQ,query2.matchStrategy):!0,srcMatches=hasFileQ||hasLineQ?matchesDebugSource(f,fileQ,query2.lineNumber):!0;nameMatches&&srcMatches&&matches.push(f);let child=f.child??null,sibling=f.sibling??null;child&&stack.push(child),sibling&&stack.push(sibling)}return matches}function scoreCandidate(fiber,anchorEl2,anchorX2,anchorY2,q,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2){let nameQ=q.componentName?q.componentName.trim():void 0,hasNameQ=!!nameQ,dn=getDisplayName(fiber),tn=getFiberTypeName(fiber),nameMatched=hasNameQ?matchesName(dn,nameQ,q.matchStrategy)||matchesName(tn,nameQ,q.matchStrategy):!1,debugSourceMatched=normalizeStr(q.fileNameHint)||typeof q.lineNumber=="number"?matchesDebugSource(fiber,normalizeStr(q.fileNameHint)??void 0,typeof q.lineNumber=="number"?q.lineNumber:void 0):!1,dom=collectDomElementsFromFiberSubtree(fiber,maxElements2,onlyVisible2,onlyInViewport2,textPreviewMaxLength2),anchorRelated=!1,proximityPx=null;if(anchorEl2){let anchorSel=selectorHintFor(anchorEl2);if(anchorRelated=dom.some(d=>!d.selectorHint||!anchorSel?!1:d.selectorHint===anchorSel)||dom.some(d=>{if(!d.boundingBox)return!1;let r=anchorEl2.getBoundingClientRect(),bb=d.boundingBox;return r.left<bb.x+bb.width&&r.left+r.width>bb.x&&r.top<bb.y+bb.height&&r.top+r.height>bb.y}),typeof anchorX2=="number"&&typeof anchorY2=="number"&&dom.length>0){let best2=null;for(let item of dom){if(!item.boundingBox)continue;let cx=item.boundingBox.x+item.boundingBox.width/2,cy=item.boundingBox.y+item.boundingBox.height/2,dx=cx-anchorX2,dy=cy-anchorY2,dist=Math.sqrt(dx*dx+dy*dy);(best2===null||dist<best2)&&(best2=dist)}proximityPx=best2}}let score=0;if(score+=nameMatched?3:0,score+=debugSourceMatched?3:0,score+=anchorRelated?2:0,score+=Math.min(1.5,dom.length/25),typeof proximityPx=="number"&&Number.isFinite(proximityPx)){let p=Math.max(0,Math.min(1,1-proximityPx/1e3));score+=1.5*p}return{score,nameMatched,debugSourceMatched,anchorRelated,proximityPx,dom}}let hook=getDevtoolsHook(),reactDetected=!!hook;reactDetected?notes.push("React DevTools hook detected. Root discovery will use getFiberRoots() (more reliable)."):(notes.push("React DevTools hook was not detected (__REACT_DEVTOOLS_GLOBAL_HOOK__)."),notes.push('If you are using a persistent/headful browser profile, install the "React Developer Tools" Chrome extension in that profile (manual step by the user) to enable reliable root discovery and component search.'));let fiberDetected=(()=>{let body=document.body;return body?!!(getFiberFromDomElement(body)||getSeedFibersFromDomScan(1).length>0):!1})();fiberDetected?notes.push("React Fiber pointers detected on DOM nodes. Anchor-based mapping may still work without the DevTools hook (best-effort)."):notes.push("No React Fiber pointers were detected on DOM nodes (__reactFiber$...). This can happen if the page is not React, React has not rendered yet, or internals are unavailable.");let anchorEl=pickAnchorElement(anchorSelectorEval,anchorXEval,anchorYEval),anchorSelected=null;if(anchorEl){let anchorFiber=getFiberFromDomElement(anchorEl);if(anchorFiber){let candidateA=anchorFiber,candidateB=anchorFiber.alternate??null,chosen=candidateA;candidateB&&(candidateB.child||candidateB.sibling)&&!(candidateA.child||candidateA.sibling)&&(chosen=candidateB);let nearestComponent=firstMeaningfulComponentAbove(chosen);nearestComponent?anchorSelected=nearestComponent:notes.push("Anchor fiber found but no meaningful component fiber was found above it.")}else notes.push("Anchor element found but React fiber was not found on it.")}let roots=[],rootDiscovery="none";if(hook){let r=getAllRootsFromHook(hook);r.length>0&&(roots=r,rootDiscovery="devtools-hook")}if(roots.length===0&&fiberDetected){let seeds=getSeedFibersFromDomScan(10);seeds.length>0&&(roots=seeds,rootDiscovery="dom-fiber-scan",notes.push("Using DOM fiber scan as fallback root discovery (best-effort)."))}let hasQuery2=!!normalizeStr(componentNameEval)||!!normalizeStr(fileNameHintEval)||typeof lineNumberEval=="number",query={componentName:normalizeStr(componentNameEval)??void 0,matchStrategy:matchStrategyEval,fileNameHint:normalizeStr(fileNameHintEval)??void 0,lineNumber:typeof lineNumberEval=="number"?lineNumberEval:void 0},queryMatches=[];hasQuery2&&(roots.length>0?queryMatches=findFibersByQuery(roots,query,Math.max(1,Math.floor(maxMatchesEval))):notes.push("Query was provided but no roots could be discovered. Provide an anchorSelector/anchorX+anchorY so we can map via DOM fiber pointers."));let candidates=[];anchorSelected&&candidates.push(anchorSelected);for(let f of queryMatches)candidates.push(f);let uniq=[],seenCand=new Set;for(let f of candidates){let key=f?.alternate??f;f&&!seenCand.has(key)&&(seenCand.add(key),uniq.push(f))}if(uniq.length===0)return reactDetected||notes.push("No candidates found. Without the DevTools hook, component search may be unreliable unless you provide a strong anchor."),{reactDetected,fiberDetected,rootDiscovery,component:null,candidates:[],elements:[],notes:[...notes,"Failed to select a target component fiber. Provide a better anchor, or ensure React is present and render has happened."]};let scored=[];for(let f of uniq){let s=scoreCandidate(f,anchorEl,anchorXEval,anchorYEval,query,maxElementsEval,onlyVisibleEval,onlyInViewportEval,textPreviewMaxLengthEval),selection="unknown",isAnchor=anchorSelected?(anchorSelected.alternate??anchorSelected)===(f.alternate??f):!1,isQuery=queryMatches.some(qf=>(qf.alternate??qf)===(f.alternate??f));isAnchor&&isQuery?selection="query+anchor":isAnchor?selection="anchor":isQuery?selection="query":selection="unknown";let name=getFiberTypeName(f),displayName=getDisplayName(f),debugSource=getDebugSource(f),componentStack=buildComponentStack(f,stackLimit),meta={name,displayName,debugSource,componentStack,selection,scoring:{score:s.score,nameMatched:s.nameMatched,debugSourceMatched:s.debugSourceMatched,anchorRelated:s.anchorRelated,proximityPx:s.proximityPx}};scored.push({fiber:f,meta,dom:s.dom,domFootprintCount:s.dom.length})}scored.sort((a,b)=>(b.meta.scoring?.score??0)-(a.meta.scoring?.score??0));let best=scored[0],outCandidates=scored.slice(0,Math.max(1,Math.floor(maxMatchesEval))).map(c=>({...c.meta,domFootprintCount:c.domFootprintCount})),elements=best.dom;return hasQuery2&&(query.fileNameHint||typeof query.lineNumber=="number")&&(outCandidates.some(c=>!!(c.debugSource?.fileName||c.debugSource?.lineNumber!==null))||(notes.push("debugSource hints were provided, but no _debugSource information was found on matched fibers. This is common in production builds and some dev toolchains."),notes.push('React DevTools may still display "Rendered by <file>:<line>" using sourcemaps/stack traces even when fiber._debugSource is null.'))),elements.length>=maxElementsEval&&notes.push(`Element list was truncated at maxElements=${maxElementsEval}. Increase maxElements if needed.`),elements.length===0&&notes.push("No DOM elements were returned for the selected component. It may render no host elements, or filtering (onlyVisible/onlyInViewport) removed them."),!reactDetected&&fiberDetected?notes.push("React DevTools hook was not detected, but DOM fiber pointers were found. Using DOM-fiber scanning and anchor-based mapping (best-effort)."):!reactDetected&&!fiberDetected&&notes.push("React DevTools hook was not detected and no DOM fiber pointers were found. Component-to-DOM mapping is unavailable on this page."),{reactDetected,fiberDetected,rootDiscovery,component:best.meta,candidates:outCandidates,elements,notes:[...notes,"Component metadata is best-effort. Wrappers (memo/forwardRef/HOCs), Suspense/Offscreen, and portals may make names/stacks noisy."]}},{anchorSelectorEval:typeof anchorSelector=="string"&&anchorSelector.trim()?anchorSelector.trim():void 0,anchorXEval:typeof anchorX=="number"&&Number.isFinite(anchorX)?Math.floor(anchorX):void 0,anchorYEval:typeof anchorY=="number"&&Number.isFinite(anchorY)?Math.floor(anchorY):void 0,componentNameEval:typeof componentName=="string"&&componentName.trim()?componentName.trim():void 0,matchStrategyEval:matchStrategy,fileNameHintEval:typeof fileNameHint=="string"&&fileNameHint.trim()?fileNameHint.trim():void 0,lineNumberEval:typeof lineNumber=="number"&&Number.isFinite(lineNumber)?Math.floor(lineNumber):void 0,maxElementsEval:Math.max(1,Math.floor(maxElements)),onlyVisibleEval:!!onlyVisible,onlyInViewportEval:!!onlyInViewport,textPreviewMaxLengthEval:Math.max(1,Math.floor(textPreviewMaxLength)),maxMatchesEval:Math.max(1,Math.floor(maxMatches)),maxRootsScan:INTERNAL_MAX_ROOTS_SCAN,maxFibersVisited:INTERNAL_MAX_FIBERS_VISITED,stackLimit:DEFAULT_STACK_LIMIT})}};var tools8=[new GetComponentForElement,new GetElementForComponent];import picomatch from"picomatch";var STORE_BY_CONTEXT2=new WeakMap;function _ensureStore2(ctx){let existing=STORE_BY_CONTEXT2.get(ctx);if(existing)return existing;let created={stubs:[],installed:!1};return STORE_BY_CONTEXT2.set(ctx,created),created}function _nowId(){let t=Date.now(),r=Math.floor(Math.random()*1e6);return`${t.toString(36)}-${r.toString(36)}`}async function _sleep(ms){ms<=0||await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}function _normalizeTimes(times){return typeof times!="number"||!Number.isFinite(times)||times<=0?-1:Math.floor(times)}function _isTimesRemaining(times,usedCount){return times===-1?!0:usedCount<times}function _normalizeChance(chance){if(typeof chance=="number"&&Number.isFinite(chance))return Math.max(0,Math.min(1,chance))}function _shouldApplyChance(chance){return chance===void 0?!0:chance<=0?!1:chance>=1?!0:Math.random()<chance}function _compileMatcher(pattern){let p=pattern.trim();return p?picomatch(p,{dot:!0,nocase:!1}):()=>!1}function _pickStub(stubs,url){for(let s of stubs)if(s.enabled&&_isTimesRemaining(s.times,s.usedCount)&&s.matcher(url)&&!(s.kind==="mock_http_response"&&!_shouldApplyChance(s.chance)))return s}async function _applyStub(route,stub){let req=route.request();if(stub.usedCount++,stub.delayMs>0&&await _sleep(stub.delayMs),stub.kind==="mock_http_response"){if(stub.action==="abort"){let code=stub.abortErrorCode??"failed";await route.abort(code);return}let status=typeof stub.status=="number"?stub.status:200,headers=stub.headers??{},body=typeof stub.body=="string"?stub.body:"";await route.fulfill({status,headers,body});return}else if(stub.kind==="intercept_http_request"){let overrides={headers:{...req.headers(),...stub.modifications.headers??{}}};typeof stub.modifications.method=="string"&&stub.modifications.method.trim()&&(overrides.method=stub.modifications.method.trim().toUpperCase()),typeof stub.modifications.body=="string"&&(overrides.postData=stub.modifications.body),await route.continue(overrides);return}await route.continue()}async function ensureRoutingInstalled(ctx){let store=_ensureStore2(ctx);store.installed||(await ctx.route("**/*",async route=>{let url=route.request().url(),innerStore=_ensureStore2(ctx),stub=_pickStub(innerStore.stubs,url);if(!stub){await route.continue();return}try{await _applyStub(route,stub)}finally{_isTimesRemaining(stub.times,stub.usedCount)||(innerStore.stubs=innerStore.stubs.filter(x=>x.id!==stub.id))}}),store.installed=!0)}function addMockHttpResponseStub(ctx,input){let store=_ensureStore2(ctx),stub={...input,kind:"mock_http_response",id:_nowId(),usedCount:0,matcher:_compileMatcher(input.pattern),times:_normalizeTimes(input.times),delayMs:Math.max(0,Math.floor(input.delayMs)),chance:_normalizeChance(input.chance)};return store.stubs.push(stub),stub}function addHttpInterceptRequestStub(ctx,input){let store=_ensureStore2(ctx),stub={...input,kind:"intercept_http_request",id:_nowId(),usedCount:0,matcher:_compileMatcher(input.pattern),times:_normalizeTimes(input.times),delayMs:Math.max(0,Math.floor(input.delayMs))};return store.stubs.push(stub),stub}function clearStub(ctx,id){let store=_ensureStore2(ctx);if(!id){let n=store.stubs.length;return store.stubs=[],n}let before=store.stubs.length;return store.stubs=store.stubs.filter(s=>s.id!==id),before-store.stubs.length}function listStubs(ctx){return[..._ensureStore2(ctx).stubs]}function normalizeDelayMs(delayMs){return typeof delayMs!="number"||!Number.isFinite(delayMs)||delayMs<=0?0:Math.floor(delayMs)}function normalizeTimesPublic(times){return _normalizeTimes(times)}function normalizeHeaders(headers){if(!headers)return;let out={};for(let[k,v]of Object.entries(headers))out[String(k)]=String(v);return out}function normalizeBody(body){if(typeof body=="string")return body;if(body&&typeof body=="object")return JSON.stringify(body)}function normalizeAbortCode(code){if(typeof code!="string")return;let v=code.trim();if(v)return v}function normalizeMethod(method){if(typeof method!="string")return;let v=method.trim();if(v)return v.toUpperCase()}function normalizeChance(chance){return _normalizeChance(chance)}import{z as z40}from"zod";var Clear=class{name(){return"stub_clear"}description(){return`
529
493
  Clears stubs installed.
530
494
 
531
495
  - If stubId is provided, clears only that stub.
532
496
  - If stubId is omitted, clears all stubs for the current session/context.
533
- `.trim()}inputSchema(){return{stubId:z42.string().optional().describe("Remove this stub; omit to clear all.")}}outputSchema(){return{clearedCount:z42.number().int().nonnegative().describe("Number of stubs removed.")}}async handle(context,args){return{clearedCount:clearStub(context.browserContext,args.stubId)}}};import{z as z43}from"zod";var InterceptHttpRequest=class{name(){return"stub_intercept-http-request"}description(){return`
497
+ `.trim()}inputSchema(){return{stubId:z40.string().optional().describe("Remove this stub; omit to clear all.")}}outputSchema(){return{clearedCount:z40.number().int().nonnegative().describe("Number of stubs removed.")}}async handle(context,args){return{clearedCount:clearStub(context.browserContext,args.stubId)}}};import{z as z41}from"zod";var InterceptHttpRequest=class{name(){return"stub_intercept-http-request"}description(){return`
534
498
  Installs a request interceptor stub that can modify outgoing requests before they are sent.
535
499
 
536
500
  Use cases:
@@ -543,10 +507,10 @@ Notes:
543
507
  - pattern is a glob matched against the full request URL (picomatch).
544
508
  - This modifies requests; it does not change responses.
545
509
  - times limits how many times the interceptor applies (-1 means infinite).
546
- `.trim()}inputSchema(){return{pattern:z43.string().describe("URL glob (picomatch)."),modifications:z43.object({headers:z43.record(z43.string(),z43.string()).optional(),body:z43.union([z43.string(),z43.record(z43.string(),z43.any()),z43.array(z43.any())]).optional(),method:z43.string().optional().describe("Override method (e.g. GET, POST).")}).optional().describe("Changes to apply to the request."),delayMs:z43.number().int().nonnegative().optional(),times:z43.number().int().optional().describe("Max applications; -1 = infinite.")}}outputSchema(){return{stubId:z43.string().describe("Unique id of the installed stub."),kind:z43.literal("intercept_http_request").describe("Stub kind."),pattern:z43.string().describe("Glob pattern."),enabled:z43.boolean().describe("Whether the stub is enabled."),delayMs:z43.number().int().describe("Applied artificial delay in milliseconds."),times:z43.number().int().describe("Max applications (-1 means infinite).")}}async handle(context,args){await ensureRoutingInstalled(context.browserContext);let delayMs=normalizeDelayMs(args.delayMs),times=normalizeTimesPublic(args.times),headers=normalizeHeaders(args.modifications?.headers),body=normalizeBody(args.modifications?.body),method=normalizeMethod(args.modifications?.method),stub=addHttpInterceptRequestStub(context.browserContext,{enabled:!0,pattern:args.pattern,modifications:{headers,body,method},delayMs,times});return{stubId:stub.id,kind:"intercept_http_request",pattern:stub.pattern,enabled:stub.enabled,delayMs:stub.delayMs,times:stub.times}}};import{z as z44}from"zod";var List=class{name(){return"stub_list"}description(){return`
510
+ `.trim()}inputSchema(){return{pattern:z41.string().describe("URL glob (picomatch)."),modifications:z41.object({headers:z41.record(z41.string(),z41.string()).optional(),body:z41.union([z41.string(),z41.record(z41.string(),z41.any()),z41.array(z41.any())]).optional(),method:z41.string().optional().describe("Override method (e.g. GET, POST).")}).optional().describe("Changes to apply to the request."),delayMs:z41.number().int().nonnegative().optional(),times:z41.number().int().optional().describe("Max applications; -1 = infinite.")}}outputSchema(){return{stubId:z41.string().describe("Unique id of the installed stub."),kind:z41.literal("intercept_http_request").describe("Stub kind."),pattern:z41.string().describe("Glob pattern."),enabled:z41.boolean().describe("Whether the stub is enabled."),delayMs:z41.number().int().describe("Applied artificial delay in milliseconds."),times:z41.number().int().describe("Max applications (-1 means infinite).")}}async handle(context,args){await ensureRoutingInstalled(context.browserContext);let delayMs=normalizeDelayMs(args.delayMs),times=normalizeTimesPublic(args.times),headers=normalizeHeaders(args.modifications?.headers),body=normalizeBody(args.modifications?.body),method=normalizeMethod(args.modifications?.method),stub=addHttpInterceptRequestStub(context.browserContext,{enabled:!0,pattern:args.pattern,modifications:{headers,body,method},delayMs,times});return{stubId:stub.id,kind:"intercept_http_request",pattern:stub.pattern,enabled:stub.enabled,delayMs:stub.delayMs,times:stub.times}}};import{z as z42}from"zod";var List=class{name(){return"stub_list"}description(){return`
547
511
  Lists currently installed stubs for the active browser context/session.
548
512
  Useful to debug why certain calls are being mocked/intercepted.
549
- `.trim()}inputSchema(){return{}}outputSchema(){return{stubs:z44.array(z44.object({id:z44.string().describe("Stub id."),kind:z44.string().describe("Stub kind."),enabled:z44.boolean().describe("Whether stub is enabled."),pattern:z44.string().describe("Glob pattern (picomatch)."),delayMs:z44.number().int().describe("Artificial delay in ms."),times:z44.number().int().describe("Max applications (-1 means infinite)."),usedCount:z44.number().int().describe("How many times it has been applied."),action:z44.string().optional().describe("For mock_response: fulfill/abort."),status:z44.number().int().optional().describe("For mock_response: HTTP status (if set).")}))}}async handle(context,args){return{stubs:listStubs(context.browserContext).map(s=>{let base={id:s.id,kind:s.kind,enabled:s.enabled,pattern:s.pattern,delayMs:s.delayMs,times:s.times,usedCount:s.usedCount};return s.kind==="mock_http_response"&&(base.action=s.action,typeof s.status=="number"&&(base.status=s.status)),base})}}};import{z as z45}from"zod";var MockHttpResponse=class{name(){return"stub_mock-http-response"}description(){return`
513
+ `.trim()}inputSchema(){return{}}outputSchema(){return{stubs:z42.array(z42.object({id:z42.string().describe("Stub id."),kind:z42.string().describe("Stub kind."),enabled:z42.boolean().describe("Whether stub is enabled."),pattern:z42.string().describe("Glob pattern (picomatch)."),delayMs:z42.number().int().describe("Artificial delay in ms."),times:z42.number().int().describe("Max applications (-1 means infinite)."),usedCount:z42.number().int().describe("How many times it has been applied."),action:z42.string().optional().describe("For mock_response: fulfill/abort."),status:z42.number().int().optional().describe("For mock_response: HTTP status (if set).")}))}}async handle(context,args){return{stubs:listStubs(context.browserContext).map(s=>{let base={id:s.id,kind:s.kind,enabled:s.enabled,pattern:s.pattern,delayMs:s.delayMs,times:s.times,usedCount:s.usedCount};return s.kind==="mock_http_response"&&(base.action=s.action,typeof s.status=="number"&&(base.status=s.status)),base})}}};import{z as z43}from"zod";var MockHttpResponse=class{name(){return"stub_mock-http-response"}description(){return`
550
514
  Installs a response stub for matching requests using glob patterns (picomatch).
551
515
 
552
516
  Use cases:
@@ -560,10 +524,10 @@ Notes:
560
524
  - pattern is a glob matched against the full request URL.
561
525
  - stubs are evaluated in insertion order; first match wins.
562
526
  - times limits how many times the stub applies (-1 means infinite).
563
- `.trim()}inputSchema(){return{pattern:z45.string().describe("URL glob."),response:z45.object({action:z45.enum(["fulfill","abort"]).optional().default("fulfill"),status:z45.number().int().min(100).max(599).optional(),headers:z45.record(z45.string(),z45.string()).optional(),body:z45.union([z45.string(),z45.record(z45.string(),z45.any()),z45.array(z45.any())]).optional(),abortErrorCode:z45.string().optional().describe("Error code when action=abort.")}).describe("Response."),delayMs:z45.number().int().nonnegative().optional(),times:z45.number().int().optional().describe("Max times to apply; -1=infinite."),chance:z45.number().min(0).max(1).optional().describe("Probability 0\u20131; omit=always.")}}outputSchema(){return{stubId:z45.string().describe("Unique id of the installed stub (use it to clear later)."),kind:z45.literal("mock_http_response").describe("Stub kind."),pattern:z45.string().describe("Glob pattern."),enabled:z45.boolean().describe("Whether the stub is enabled."),delayMs:z45.number().int().describe("Applied artificial delay in milliseconds."),times:z45.number().int().describe("Max applications (-1 means infinite)."),chance:z45.number().optional().describe("Apply probability (omit means always)."),action:z45.enum(["fulfill","abort"]).describe("Applied action."),status:z45.number().int().optional().describe("HTTP status (present when action=fulfill).")}}async handle(context,args){await ensureRoutingInstalled(context.browserContext);let action=args.response.action??"fulfill",delayMs=normalizeDelayMs(args.delayMs),times=normalizeTimesPublic(args.times),chance=normalizeChance(args.chance),status=args.response.status,headers=normalizeHeaders(args.response.headers),body=normalizeBody(args.response.body),abortErrorCode=normalizeAbortCode(args.response.abortErrorCode),stub=addMockHttpResponseStub(context.browserContext,{enabled:!0,pattern:args.pattern,action,status,headers,body,abortErrorCode,delayMs,times,chance}),out={stubId:stub.id,kind:"mock_http_response",pattern:stub.pattern,enabled:stub.enabled,delayMs:stub.delayMs,times:stub.times,action:stub.action};return typeof stub.chance=="number"&&(out.chance=stub.chance),stub.action==="fulfill"&&(out.status=typeof stub.status=="number"?stub.status:200),out}};var tools10=[new Clear,new InterceptHttpRequest,new List,new MockHttpResponse];import{z as z46}from"zod";var DEFAULT_TIMEOUT_MS5=3e4,DEFAULT_IDLE_TIME_MS=500,DEFAULT_MAX_CONNECTIONS=0,DEFAULT_POLL_INTERVAL_MS=50,WaitForNetworkIdle=class{name(){return"sync_wait-for-network-idle"}description(){return`
527
+ `.trim()}inputSchema(){return{pattern:z43.string().describe("URL glob."),response:z43.object({action:z43.enum(["fulfill","abort"]).optional().default("fulfill"),status:z43.number().int().min(100).max(599).optional(),headers:z43.record(z43.string(),z43.string()).optional(),body:z43.union([z43.string(),z43.record(z43.string(),z43.any()),z43.array(z43.any())]).optional(),abortErrorCode:z43.string().optional().describe("Error code when action=abort.")}).describe("Response."),delayMs:z43.number().int().nonnegative().optional(),times:z43.number().int().optional().describe("Max times to apply; -1=infinite."),chance:z43.number().min(0).max(1).optional().describe("Probability 0\u20131; omit=always.")}}outputSchema(){return{stubId:z43.string().describe("Unique id of the installed stub (use it to clear later)."),kind:z43.literal("mock_http_response").describe("Stub kind."),pattern:z43.string().describe("Glob pattern."),enabled:z43.boolean().describe("Whether the stub is enabled."),delayMs:z43.number().int().describe("Applied artificial delay in milliseconds."),times:z43.number().int().describe("Max applications (-1 means infinite)."),chance:z43.number().optional().describe("Apply probability (omit means always)."),action:z43.enum(["fulfill","abort"]).describe("Applied action."),status:z43.number().int().optional().describe("HTTP status (present when action=fulfill).")}}async handle(context,args){await ensureRoutingInstalled(context.browserContext);let action=args.response.action??"fulfill",delayMs=normalizeDelayMs(args.delayMs),times=normalizeTimesPublic(args.times),chance=normalizeChance(args.chance),status=args.response.status,headers=normalizeHeaders(args.response.headers),body=normalizeBody(args.response.body),abortErrorCode=normalizeAbortCode(args.response.abortErrorCode),stub=addMockHttpResponseStub(context.browserContext,{enabled:!0,pattern:args.pattern,action,status,headers,body,abortErrorCode,delayMs,times,chance}),out={stubId:stub.id,kind:"mock_http_response",pattern:stub.pattern,enabled:stub.enabled,delayMs:stub.delayMs,times:stub.times,action:stub.action};return typeof stub.chance=="number"&&(out.chance=stub.chance),stub.action==="fulfill"&&(out.status=typeof stub.status=="number"?stub.status:200),out}};var tools9=[new Clear,new InterceptHttpRequest,new List,new MockHttpResponse];import{z as z44}from"zod";var DEFAULT_TIMEOUT_MS4=3e4,DEFAULT_IDLE_TIME_MS=500,DEFAULT_MAX_CONNECTIONS=0,DEFAULT_POLL_INTERVAL_MS=50,WaitForNetworkIdle=class{name(){return"sync_wait-for-network-idle"}description(){return`
564
528
  Waits until the page is network-idle: in-flight requests <= maxConnections for at least idleTimeMs (server-side tracking, no page globals).
565
529
  Use before SPAs, screenshots, or AX snapshots for stable results. With long-polling, increase maxConnections or use shorter idleTimeMs.
566
- `.trim()}inputSchema(){return{timeoutMs:z46.number().int().min(0).optional().default(DEFAULT_TIMEOUT_MS5).describe("Max wait ms before failing."),idleTimeMs:z46.number().int().min(0).optional().default(DEFAULT_IDLE_TIME_MS).describe("Network must stay idle for this many ms to resolve."),maxConnections:z46.number().int().min(0).optional().default(DEFAULT_MAX_CONNECTIONS).describe("Idle when in-flight requests <= this."),pollIntervalMs:z46.number().int().min(10).optional().default(DEFAULT_POLL_INTERVAL_MS).describe("Polling interval ms.")}}outputSchema(){return{waitedMs:z46.number().int().nonnegative().describe("Total time waited until the network became idle or the tool timed out (milliseconds)."),idleTimeMs:z46.number().int().nonnegative().describe("Idle duration required for success (milliseconds)."),timeoutMs:z46.number().int().nonnegative().describe("Maximum allowed wait time (milliseconds)."),maxConnections:z46.number().int().nonnegative().describe("Idle threshold used: in-flight requests must be <= this value."),pollIntervalMs:z46.number().int().nonnegative().describe("Polling interval used to sample the in-flight request count (milliseconds)."),finalInFlightRequests:z46.number().int().nonnegative().describe("The last observed number of in-flight requests at the moment the tool returned."),observedIdleMs:z46.number().int().nonnegative().describe("How long the in-flight request count stayed <= maxConnections right before returning (milliseconds).")}}async handle(context,args){let timeoutMs=args.timeoutMs??DEFAULT_TIMEOUT_MS5,idleTimeMs=args.idleTimeMs??DEFAULT_IDLE_TIME_MS,maxConnections=args.maxConnections??DEFAULT_MAX_CONNECTIONS,pollIntervalMs=args.pollIntervalMs??DEFAULT_POLL_INTERVAL_MS,startMs=Date.now(),deadlineMs=startMs+timeoutMs,lastNotIdleMs=startMs,lastInFlight=0;for(;;){let nowMs=Date.now();lastInFlight=context.numOfInFlightRequests(),lastInFlight>maxConnections&&(lastNotIdleMs=nowMs);let observedIdleMs=nowMs-lastNotIdleMs;if(observedIdleMs>=idleTimeMs)return{waitedMs:nowMs-startMs,idleTimeMs,timeoutMs,maxConnections,pollIntervalMs,finalInFlightRequests:lastInFlight,observedIdleMs};if(nowMs>=deadlineMs){let waitedMs=nowMs-startMs;throw new Error(`Timed out after ${waitedMs}ms waiting for network idle (idleTimeMs=${idleTimeMs}, maxConnections=${maxConnections}, inFlight=${lastInFlight}).`)}await this.sleep(pollIntervalMs)}}async sleep(ms){await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}};var tools11=[new WaitForNetworkIdle];var tools12=[...tools,...tools2,...tools3,...tools4,...tools5,...tools6,...tools7,...tools8,...tools9,...tools10,...tools11];async function createToolSessionContext(sessionIdProvider){let sessionId=sessionIdProvider(),browserContextInfo=await newBrowserContext(),page=await newPage(browserContextInfo.browserContext),context=new BrowserToolSessionContext(sessionId,browserContextInfo.browserContext,page,{otelEnable:OTEL_ENABLE});return await context.init(),debug(`Created session context for the session with id ${context.sessionId()}`),context}function createToolExecutor(){return new BrowserToolExecutor}import{Option}from"commander";var BrowserCliProvider=class{platform="browser";cliName="browser-devtools-cli";tools=tools12;sessionDescription="Manage browser sessions";cliExamples=["browser-devtools-cli --no-headless interactive","browser-devtools-cli --persistent --no-headless interactive"];bashCompletionOptions="--headless --no-headless --persistent --no-persistent --user-data-dir --use-system-browser --browser-path";bashCompletionCommands="daemon session tools config completion interactive navigation content interaction a11y accessibility o11y react run stub sync figma debug";zshCompletionOptions=` '--headless[Run in headless mode]' \\
530
+ `.trim()}inputSchema(){return{timeoutMs:z44.number().int().min(0).optional().default(DEFAULT_TIMEOUT_MS4).describe("Max wait ms before failing."),idleTimeMs:z44.number().int().min(0).optional().default(DEFAULT_IDLE_TIME_MS).describe("Network must stay idle for this many ms to resolve."),maxConnections:z44.number().int().min(0).optional().default(DEFAULT_MAX_CONNECTIONS).describe("Idle when in-flight requests <= this."),pollIntervalMs:z44.number().int().min(10).optional().default(DEFAULT_POLL_INTERVAL_MS).describe("Polling interval ms.")}}outputSchema(){return{waitedMs:z44.number().int().nonnegative().describe("Total time waited until the network became idle or the tool timed out (milliseconds)."),idleTimeMs:z44.number().int().nonnegative().describe("Idle duration required for success (milliseconds)."),timeoutMs:z44.number().int().nonnegative().describe("Maximum allowed wait time (milliseconds)."),maxConnections:z44.number().int().nonnegative().describe("Idle threshold used: in-flight requests must be <= this value."),pollIntervalMs:z44.number().int().nonnegative().describe("Polling interval used to sample the in-flight request count (milliseconds)."),finalInFlightRequests:z44.number().int().nonnegative().describe("The last observed number of in-flight requests at the moment the tool returned."),observedIdleMs:z44.number().int().nonnegative().describe("How long the in-flight request count stayed <= maxConnections right before returning (milliseconds).")}}async handle(context,args){let timeoutMs=args.timeoutMs??DEFAULT_TIMEOUT_MS4,idleTimeMs=args.idleTimeMs??DEFAULT_IDLE_TIME_MS,maxConnections=args.maxConnections??DEFAULT_MAX_CONNECTIONS,pollIntervalMs=args.pollIntervalMs??DEFAULT_POLL_INTERVAL_MS,startMs=Date.now(),deadlineMs=startMs+timeoutMs,lastNotIdleMs=startMs,lastInFlight=0;for(;;){let nowMs=Date.now();lastInFlight=context.numOfInFlightRequests(),lastInFlight>maxConnections&&(lastNotIdleMs=nowMs);let observedIdleMs=nowMs-lastNotIdleMs;if(observedIdleMs>=idleTimeMs)return{waitedMs:nowMs-startMs,idleTimeMs,timeoutMs,maxConnections,pollIntervalMs,finalInFlightRequests:lastInFlight,observedIdleMs};if(nowMs>=deadlineMs){let waitedMs=nowMs-startMs;throw new Error(`Timed out after ${waitedMs}ms waiting for network idle (idleTimeMs=${idleTimeMs}, maxConnections=${maxConnections}, inFlight=${lastInFlight}).`)}await this.sleep(pollIntervalMs)}}async sleep(ms){await new Promise(resolve=>{setTimeout(()=>resolve(),ms)})}};var tools10=[new WaitForNetworkIdle];var tools11=[...tools,...tools2,...tools3,...tools4,...tools5,...tools6,...tools7,...tools8,...tools9,...tools10];async function createToolSessionContext(sessionIdProvider){let sessionId=sessionIdProvider(),browserContextInfo=await newBrowserContext(),page=await newPage(browserContextInfo.browserContext),context=new BrowserToolSessionContext(sessionId,browserContextInfo.browserContext,page,{otelEnable:OTEL_ENABLE});return await context.init(),debug(`Created session context for the session with id ${context.sessionId()}`),context}function createToolExecutor(){return new BrowserToolExecutor}import{Option}from"commander";var BrowserCliProvider=class{platform="browser";cliName="browser-devtools-cli";tools=tools11;sessionDescription="Manage browser sessions";cliExamples=["browser-devtools-cli --no-headless interactive","browser-devtools-cli --persistent --no-headless interactive"];bashCompletionOptions="--headless --no-headless --persistent --no-persistent --user-data-dir --use-system-browser --browser-path";bashCompletionCommands="daemon session tools config completion interactive navigation content interaction a11y accessibility o11y react run stub sync figma debug";zshCompletionOptions=` '--headless[Run in headless mode]' \\
567
531
  '--no-headless[Run in headful mode]' \\
568
532
  '--persistent[Use persistent context]' \\
569
533
  '--no-persistent[Use ephemeral context]' \\
@@ -575,171 +539,94 @@ Use before SPAs, screenshots, or AX snapshots for stable results. With long-poll
575
539
  This MCP server exposes a Playwright-powered browser runtime to AI agents,
576
540
  enabling deep, bidirectional debugging and interaction with live web pages.
577
541
 
578
- It supports both visual understanding and code-level inspection of browser state,
579
- similar to existing Playwright and Chrome DevTools\u2013based MCP servers, with a focus on AI-driven exploration, diagnosis, and action.
542
+ It supports both visual understanding and code-level inspection of browser state
543
+ with a focus on AI-driven exploration, diagnosis, and action.
544
+
545
+ Capabilities include navigation, ARIA/AX snapshots, screenshots, element interaction (click, fill, hover, select, drag),
546
+ the execute tool for batching multiple steps in one call, and observability (console, HTTP, Web Vitals, stubs, non-blocking debug probes).
580
547
 
581
548
  **Important rules for AI agents:**
582
549
 
583
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
550
+ ---
584
551
  *** CRITICAL \u2014 TO UNDERSTAND PAGE STRUCTURE (do not default to screenshot) ***
552
+
585
553
  Use ARIA snapshot first, then AX tree if needed. Use screenshot only when necessary.
586
554
 
587
- After navigating to a page, your FIRST inspection call MUST be "a11y_take-aria-snapshot" (or "a11y_take-ax-tree-snapshot"), NOT "content_take-screenshot".
555
+ After navigating to a page, your FIRST inspection call MUST be "a11y_take-aria-snapshot" (or "a11y_take-ax-tree-snapshot"),
556
+ NOT "content_take-screenshot".
588
557
  DO NOT take a screenshot just to "see the page" or understand what is on it.
589
558
  1. Call "a11y_take-aria-snapshot" FIRST for structure, roles, and semantics.
590
559
  2. If you need layout, bounding boxes, or occlusion: call "a11y_take-ax-tree-snapshot".
591
- 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.
592
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
560
+ 3. Call "content_take-screenshot" ONLY when you need to verify how something looks visually
561
+ (e.g. design check, visual bug, or when ARIA/AX are insufficient). Do not use screenshot to understand page structure.
562
+ ---
593
563
 
594
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
564
+ ---
595
565
  *** CRITICAL \u2014 BEFORE ANY ELEMENT INTERACTION ***
596
566
  (click | fill | hover | select | drag)
597
567
 
598
568
  DO NOT GUESS SELECTORS FROM SCREENSHOTS.
599
569
 
600
570
  Take a snapshot first to get real selectors and refs:
601
- - 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.
602
- - "a11y_take-aria-snapshot" returns a tree with refs (e1, e2, ...) and stores them in context. In click, fill, hover, select, drag, scroll use the selector parameter as: a ref ("e1", "@e1", "ref=e1"), a Playwright-style expression (e.g. getByRole('button', { name: 'Login' }), getByLabel('Email'), getByText('Register'), getByPlaceholder('\u2026'), getByTitle('\u2026'), getByAltText('\u2026'), getByTestId('\u2026')), or a CSS selector. Refs are valid until the next snapshot or navigation; re-snapshot after page/DOM changes.
603
- - Then use the returned selectors or refs for interaction. Guessing (e.g. input[type="email"]) often fails because real markup may use type="text", wrappers, or different attributes.
571
+ - Call "a11y_take-aria-snapshot", "a11y_take-ax-tree-snapshot", or "content_get-as-html" (e.g. selector "form")
572
+ to see actual ids, roles, and markup.
573
+ - "a11y_take-aria-snapshot" returns a tree with refs (e1, e2, ...) and stores them in context. In click, fill, hover, select, drag, scroll use the selector as:
574
+ a ref ("e1", "@e1", "ref=e1"), a Playwright-style expression (e.g. getByRole('button', { name: 'Login' }), getByLabel('Email'),
575
+ getByText('Register'), getByPlaceholder('\u2026'), getByTitle('\u2026'), getByAltText('\u2026'), getByTestId('\u2026')), or a CSS selector.
576
+ Refs are valid until the next snapshot or navigation; re-snapshot after page/DOM changes.
577
+ - Then use the returned selectors or refs for interaction. Guessing (e.g. input[type="email"]) often fails because real markup may use
578
+ type="text", wrappers, or different attributes.
604
579
 
605
580
  SNAPSHOT FIRST, THEN INTERACT.
606
- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
607
-
608
- Core capabilities include:
609
-
610
- **Content & Visual Inspection:**
611
- - Screenshots (full page or specific elements); optional "annotate:true" overlays numbered labels (e1, e2, ...) from the last ARIA snapshot refs and returns annotations (ref, role, box)
612
- - HTML and text content extraction with filtering options
613
- - PDF generation with customizable formats
614
- - Design comparison: Compare live page UI against Figma designs with similarity scoring
615
- - Accessibility snapshots (ARIA and AX tree) with visual diagnostics
616
- - Viewport and window resizing for responsive testing
617
-
618
- **Browser Control & Interaction:**
619
- - Navigation (go to URL, back, forward)
620
- - Element interaction (click, fill, hover, select, drag): selector accepts snapshot ref (e1, @e1, ref=e1), Playwright-style expression (getByRole, getByLabel, getByText, getByPlaceholder, getByTitle, getByAltText, getByTestId), or CSS selector; refs come from a11y_take-aria-snapshot and are valid until next snapshot or navigation
621
- - Keyboard simulation (press-key)
622
- - Scrolling (viewport or container-based with multiple modes)
623
- - Viewport emulation and real window resizing
624
-
625
- **JavaScript Execution:**
626
- - Run JavaScript in browser page context (access to window, document, DOM, Web APIs)
627
- - Run JavaScript in Node.js VM sandbox on the server (with Playwright Page access and safe built-ins)
628
-
629
- **Observability & Monitoring:**
630
- - Console message capture with filtering
631
- - HTTP request/response monitoring with detailed filtering
632
- - Web Vitals performance metrics (LCP, INP, CLS, TTFB, FCP) with recommendations
633
- - OpenTelemetry trace ID management for distributed tracing correlation
634
-
635
- **Network Stubbing & Mocking:**
636
- - HTTP request interception and modification (headers, body, method) using glob patterns
637
- - HTTP response mocking (fulfill with custom status/headers/body or abort) with configurable delay, times limit, and probability
638
- - Stub management (list all installed stubs, clear specific or all stubs)
639
- - Supports A/B testing, security testing, offline testing, error scenarios, and flaky API testing
640
-
641
- **Synchronization:**
642
- - Network idle waiting for async operations
643
- - Configurable timeouts and polling intervals
644
-
645
- **Design Comparison:**
646
- - Figma design comparison: Compare live page UI against Figma design snapshots
647
- - Multi-signal similarity scoring (MSSIM, image embedding, text embedding)
648
- - Configurable weights and comparison modes (raw vs semantic)
649
-
650
- **React Component Inspection:**
651
- - Get component for element: Find React component(s) associated with a DOM element using React Fiber
652
- - Get element for component: Map a React component instance to the DOM elements it renders
653
- - Requires persistent browser context (BROWSER_PERSISTENT_ENABLE=true) for optimal operation
654
- - React DevTools extension must be manually installed in the browser profile (MCP server does NOT auto-install)
655
- - Without extension, tools fall back to best-effort DOM scanning for __reactFiber$ pointers (less reliable)
656
-
657
- **Non-Blocking Debugging:**
658
- - Tracepoints: Capture call stack, local variables, and watch expressions at specific code locations without pausing execution
659
- - Logpoints: Evaluate and log expressions at code locations (lightweight alternative to tracepoints)
660
- - Exceptionpoints: Automatically capture snapshots when uncaught or all exceptions occur
661
- - Watch expressions: Evaluate custom expressions at every tracepoint hit
662
- - Source map support: Automatically resolves bundled code locations to original source files
663
- - Snapshot retrieval: Query captured snapshots by probe ID or sequence number for incremental polling
664
-
665
- **Advanced Features:**
666
- - OpenTelemetry integration: Automatic UI trace collection and backend trace correlation
667
- - Session-based architecture with long-lived browser contexts
668
- - Persistent browser contexts for stateful sessions (required for React tools)
669
- - Headless and headful mode support
670
- - System-installed browser usage option
671
- - Streamable responses and server-initiated notifications
672
- - Clean lifecycle management and teardown
673
-
674
- UI debugging guidance for AI agents:
675
-
676
- *** INSPECTION PREFERENCE (follow this order) ***
677
- 1. For page structure, roles, and semantics: use the ARIA snapshot you already have. After navigation (go-to, go-back-or-forward, reload) the response includes output and refs by default \u2014 use that. If you did not navigate or the page changed, call "a11y_take-aria-snapshot". Do NOT take a screenshot just to understand what is on the page.
678
- 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.
679
- 3. Use "content_take-screenshot" ONLY when you have a concrete visual problem to verify (e.g. design check, visual bug, contrast, layout issue). For understanding page structure, snapshots are sufficient \u2014 screenshot is not needed. If in doubt, use snapshots first; add a screenshot only when snapshots are insufficient for the visual question.
680
- *** SCREENSHOT TOOL: includeBase64 ***
681
- - 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.
682
-
683
- - For page structure, always use snapshots (ARIA or AX), not screenshots. Navigation responses already include an ARIA snapshot with refs; use it. Take a screenshot only when you must verify something visual (design, contrast, layout bug).
684
- - Prefer Accessibility (AX) and ARIA snapshots over raw DOM dumps when diagnosing UI problems.
685
- These snapshots provide higher-signal, semantically meaningful anchors (roles, names, states) that
686
- map more reliably to what users perceive and what assistive tech can interact with.
687
- - Use the AX Tree Snapshot tool to correlate interactive semantics with runtime visual truth:
688
- bounding boxes, visibility, viewport intersection, and (optionally) computed styles.
689
- - If a UI control appears present but interactions fail (e.g., clicks do nothing), suspect overlap/occlusion.
690
- In such cases, enable occlusion checking ("elementFromPoint") to identify which element is actually on top.
691
- - Use ARIA snapshots to reason about accessibility roles/states and to validate that the intended
692
- semantics (labels, roles, disabled state, focusability) match the visible UI.
693
- - Before taking screenshots or snapshots, wait for network idle to ensure page stability.
694
- - Use Web Vitals tool to assess performance and identify optimization opportunities.
695
- - For design validation, use "figma_compare-page-with-design" to compare live page UI against Figma designs:
696
- - Use "semantic" MSSIM mode for comparing real data vs design data (less sensitive to text/value differences)
697
- - Use "raw" MSSIM mode only when expecting near pixel-identical output
698
- - If layout mismatch is suspected, run with fullPage=true first, then retry with a selector for the problematic region
699
- - Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration
700
- - For React component inspection, use "react_get-component-for-element" and "react_get-element-for-component":
701
- - These tools work best with persistent browser context enabled (BROWSER_PERSISTENT_ENABLE=true)
702
- - React DevTools extension must be manually installed in the browser profile (MCP server does NOT auto-install)
703
- - Chrome Web Store: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
704
- - Without extension, tools use best-effort DOM scanning (less reliable than using DevTools hook)
705
- - Component names and debug source info are best-effort and may vary by build (dev/prod)
706
- - For distributed tracing, set trace IDs before navigation to correlate frontend and backend traces.
707
- - For testing and debugging scenarios, use stub tools to intercept/modify requests or mock responses:
708
- - Use "stub_intercept-http-request" to modify outgoing requests (inject headers, change body/method)
709
- - Use "stub_mock-http-response" to mock responses for offline testing, error scenarios, or flaky API simulation
710
- - Use "stub_list" to check what stubs are active and "stub_clear" to remove them when done
711
- - For non-blocking JavaScript debugging:
712
- - Use "debug_put-tracepoint" to capture call stack, local variables, and watch expressions at specific code locations
713
- - Use "debug_put-logpoint" for lightweight logging at code locations (no call stack capture)
714
- - Use "debug_put-exceptionpoint" to capture snapshots when exceptions occur (state: "uncaught" or "all")
715
- - Use "debug_add-watch" to add watch expressions evaluated at every tracepoint hit
716
- - Use "debug_list-probes" (optional types: tracepoint, logpoint, watch) to list tracepoints, logpoints, and watch expressions
717
- - Use "debug_remove-probe" (type + id) to remove a tracepoint, logpoint, or watch
718
- - Use "debug_clear-probes" (optional types: tracepoint, logpoint, watches) to clear tracepoints, logpoints, and/or watch expressions
719
- - Use "debug_get-probe-snapshots" (optional types: tracepoint, logpoint, exceptionpoint; optional probeId, fromSequence, limit) to retrieve captured data; response has tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots
720
- - Use "debug_clear-probe-snapshots" (optional types, optional probeId) to clear captured snapshots
721
- - Use "fromSequence" parameter for incremental polling of new snapshots
722
- - Tracepoints/logpoints support source maps: set probes on original source file locations
723
-
724
- This server is designed for AI coding assistants, visual debugging agents, and automated analysis tools
725
- that need to reason about what a page looks like, how it is structured, and how it behaves \u2014 all through a single MCP interface.
726
-
727
- It treats the browser as a queryable, inspectable, and controllable execution environment rather than a static screenshot source.
581
+ Before inspection: use sync_wait-for-network-idle for SPA/async content; use interaction_click with waitForNavigation: true
582
+ when the click navigates to a new page.
583
+ ---
584
+
585
+ ---
586
+ *** CRITICAL \u2014 PLAN THEN BATCH-EXECUTE ***
587
+
588
+ Design your workflow as small, focused plans:
589
+ 1. NAVIGATE \u2014 go to a page; the response includes an ARIA snapshot with refs.
590
+ 2. PLAN \u2014 read the snapshot to understand the page structure, then decide the next steps (e.g. fill fields, click buttons,
591
+ take a snapshot).
592
+ 3. BATCH \u2014 use the "execute" tool to run all planned steps in a SINGLE call via callTool().
593
+
594
+ This is the PREFERRED way to perform multi-step interactions. Instead of making
595
+ separate tool calls for each fill/click/select, batch them together:
596
+
597
+ // execute tool code (e.g. form submit then new page):
598
+ await callTool('interaction_fill', { selector: 'e3', value: 'user@test.com' });
599
+ await callTool('interaction_fill', { selector: 'e5', value: 'secret123' });
600
+ await callTool('interaction_click', { selector: 'e7', waitForNavigation: true });
601
+ await callTool('a11y_take-aria-snapshot', {}, true);
602
+ await callTool('content_take-screenshot', {}, true);
603
+
604
+ Benefits: fewer round-trips, lower token usage, faster execution.
605
+ Use individual tool calls only when you need to inspect the result before deciding the next step.
606
+ ---
728
607
  `,UI_DEBUGGING_POLICY=`
729
608
  <ui_debugging_policy>
730
609
  When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follow this policy:
731
610
 
732
- 1. **Synchronization**: If the page loads content asynchronously, call "sync_wait-for-network-idle" first
733
- to ensure the page is stable before inspection.
611
+ 1. **Synchronization**: When a click navigates to a new page (e.g. submit), use "interaction_click" with waitForNavigation: true \u2014
612
+ this waits for the new page in parallel with the click (race-free). If the page loads content asynchronously (SPA/XHR without navigation),
613
+ call "sync_wait-for-network-idle" after the click to ensure the page is stable before inspection.
734
614
 
735
615
  2. **To understand page structure (mandatory order\u2014do not default to screenshot):**
736
616
  - Call "a11y_take-aria-snapshot" FIRST for structure, roles, and semantics.
737
617
  - If you need layout/occlusion/visibility: call "a11y_take-ax-tree-snapshot" (with "checkOcclusion:true" when relevant).
738
- - 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.
618
+ - 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.
619
+ Do NOT use screenshot to "see the page" or understand structure.
739
620
 
740
- 3. **Before any element interaction** (click, fill, hover, select, drag): Take a snapshot first to get real selectors and refs. Call "a11y_take-aria-snapshot" (returns refs e1, e2, ... usable as selector "e1", "@e1", or "ref=e1"), "a11y_take-ax-tree-snapshot", or "content_get-as-html" (e.g. selector "form") to see actual ids, roles, and markup. You can also use Playwright-style expressions as selector: getByRole('button', { name: 'Login' }), getByLabel('Email'), getByText('Register'), getByPlaceholder('\u2026'), getByTitle('\u2026'), getByAltText('\u2026'), getByTestId('\u2026'), or CSS. Refs are valid until next snapshot or navigation. Do NOT guess selectors from screenshots\u2014real markup often differs (e.g. type="text" instead of type="email"). Snapshot first, then interact.
621
+ 3. **Before any element interaction** (click, fill, hover, select, drag): Take a snapshot first to get real selectors and refs.
622
+ Call "a11y_take-aria-snapshot" (returns refs e1, e2, ... usable as selector "e1", "@e1", or "ref=e1"), "a11y_take-ax-tree-snapshot",
623
+ or "content_get-as-html" (e.g. selector "form") to see actual ids, roles, and markup. You can also use Playwright-style expressions
624
+ as selector: getByRole('button', { name: 'Login' }), getByLabel('Email'), getByText('Register'), getByPlaceholder('\u2026'),
625
+ getByTitle('\u2026'), getByAltText('\u2026'), getByTestId('\u2026'), or CSS. Refs are valid until next snapshot or navigation.
626
+ Do NOT guess selectors from screenshots\u2014real markup often differs (e.g. type="text" instead of type="email"). Snapshot first, then interact.
741
627
 
742
- 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.
628
+ 4. **Screenshot usage**: When you do take a screenshot, do NOT set "includeBase64" to true unless the assistant cannot read the returned
629
+ file path (e.g. remote/container). The image is always saved to disk.
743
630
 
744
631
  5. **Accessibility Tree Analysis**: Call "a11y_take-ax-tree-snapshot" tool with "checkOcclusion:true"
745
632
  - Provides precise bounding boxes, runtime visual data, and occlusion detection
@@ -748,8 +635,10 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
748
635
  - Set "includeStyles:true" to analyze computed CSS properties
749
636
 
750
637
  6. **ARIA Snapshot**: Call "a11y_take-aria-snapshot" tool (full page or specific selector)
751
- - Provides semantic structure and accessibility roles; returns refs (e1, e2, ...) for use in interaction tools (selector "e1", "@e1", "ref=e1", or getByRole/getByLabel/getByText/getByPlaceholder/getByTitle/getByAltText/getByTestId, or CSS)
752
- - Set cursorInteractive: true to also get refs for custom clickable elements (e.g. div/span with cursor:pointer or onclick) that have no ARIA role
638
+ - Provides semantic structure and accessibility roles; returns refs (e1, e2, ...) for use in interaction tools (selector "e1", "@e1",
639
+ "ref=e1", or getByRole/getByLabel/getByText/getByPlaceholder/getByTitle/getByAltText/getByTestId, or CSS)
640
+ - Set cursorInteractive: true to also get refs for custom clickable elements (e.g. div/span with cursor:pointer or onclick)
641
+ that have no ARIA role
753
642
  - Best for understanding page hierarchy and accessibility issues
754
643
  - Refs are valid until next snapshot or navigation; re-snapshot after page/DOM changes
755
644
  - Use in combination with AX tree snapshot for comprehensive analysis
@@ -789,9 +678,8 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
789
678
  accessibility problems, semantic structure issues, design parity issues (if compared with Figma),
790
679
  React component structure issues (if inspected), performance problems, console errors, failed requests
791
680
 
792
- 13. **JavaScript Execution** (when needed for advanced debugging):
793
- - Use "run_js-in-browser" to inspect or mutate DOM state, read client-side variables, or extract computed values directly from the page
794
- - Use "run_js-in-sandbox" for server-side automation logic that needs access to Playwright Page object or safe built-ins
681
+ 13. **JavaScript Execution** (when needed): Use the "execute" tool; the VM receives \`page\` (Playwright Page) from the session context.
682
+ Use \`page.evaluate()\` to run script in the browser, or the Playwright API (e.g. \`page.locator()\`, \`page.goto()\`) for automation.
795
683
 
796
684
  14. **Non-Blocking JavaScript Debugging** (for deep code investigation):
797
685
  - Use "debug_put-tracepoint" to set breakpoints that capture call stack and local variables without pausing
@@ -801,20 +689,40 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
801
689
  - Use "debug_list-probes" to list tracepoints, logpoints, and watch expressions (optional types)
802
690
  - Use "debug_remove-probe" (type + id) to remove a single probe or watch
803
691
  - Use "debug_clear-probes" to clear tracepoints, logpoints, and/or watches (optional types)
804
- - Retrieve snapshots with "debug_get-probe-snapshots" (response: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots; optional types, probeId, fromSequence, limit)
692
+ - Retrieve snapshots with "debug_get-probe-snapshots" (response: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots;
693
+ optional types, probeId, fromSequence, limit)
805
694
  - Use "debug_clear-probe-snapshots" to clear captured snapshots (optional types, probeId)
806
695
  - Use "fromSequence" parameter to poll only new snapshots since last retrieval
807
696
  - Probes support source maps: specify original source file paths for bundled applications
808
697
  - Use "debug_status" to check current debugging state and probe counts
809
698
 
699
+ **Batch Execution \u2014 STRONGLY RECOMMENDED:**
700
+ - After navigating to a page and reading the ARIA snapshot (which comes with the navigation response), plan all the interactions you need,
701
+ then execute them in a single "execute" tool call using callTool().
702
+ - Example workflow:
703
+ 1. Call "navigation_go-to" \u2192 read the returned ARIA snapshot and refs.
704
+ 2. Identify the steps: fill email, fill password, click submit.
705
+ 3. Use "execute" to batch all steps in one call:
706
+ await callTool('interaction_fill', { selector: 'e3', value: 'user@test.com' });
707
+ await callTool('interaction_fill', { selector: 'e5', value: 'secret123' });
708
+ await callTool('interaction_click', { selector: 'e7', waitForNavigation: true });
709
+ await callTool('a11y_take-aria-snapshot', {}, true);
710
+ await callTool('content_take-screenshot', {}, true);
711
+ 4. Read the batched result and plan the next batch if needed.
712
+ - This dramatically reduces tool-call round-trips, saves tokens, and speeds up execution.
713
+ - Use individual (non-batched) tool calls only when you must inspect the result before deciding the next step.
714
+
810
715
  **Tool Usage Notes:**
811
- - To understand the page: ARIA snapshot first, then AX tree; do NOT use screenshot for structure\u2014use screenshot only for visual verification when needed.
812
- - Before any click/fill/hover/select/drag: take a snapshot first to get selectors or refs; you can use refs (e1, @e1), getByRole/getByLabel/getByText/etc., or CSS; do not guess from screenshots.
716
+ - To understand the page: ARIA snapshot first, then AX tree; do NOT use screenshot for structure\u2014use screenshot only for visual
717
+ verification when needed.
718
+ - Before any click/fill/hover/select/drag: take a snapshot first to get selectors or refs; you can use refs (e1, @e1),
719
+ getByRole/getByLabel/getByText/etc., or CSS; do not guess from screenshots.
813
720
  - Screenshot only when you need to verify how the UI looks (e.g. visual bug, design check) or when ARIA/AX are insufficient.
814
721
  - Do not set includeBase64 on screenshot unless the assistant cannot read the file path.
815
722
  - AX tree: Technical measurements, occlusion, precise positioning, visual diagnostics
816
723
  - ARIA snapshot: Semantic understanding, accessibility structure, role hierarchy
817
- - Network idle: Essential for SPAs and async content
724
+ - interaction_click waitForNavigation: true \u2014 when click opens a new page, waits for navigation in parallel (race-free)
725
+ - sync_wait-for-network-idle: configurable idle for SPAs and async content
818
726
  - Web Vitals: Performance context for UI issues
819
727
  - Tracepoints: Deep code investigation with call stack and variables (non-blocking)
820
728
  - Logpoints: Lightweight logging at specific code locations
@@ -827,25 +735,39 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
827
735
  - Use scroll tool if elements are below the fold before inspection.
828
736
  - For responsive issues, use resize-viewport or resize-window tools to test different sizes.
829
737
  </ui_debugging_policy>
830
- `;var platformInfo={serverInfo:{instructions:SERVER_INSTRUCTIONS,policies:[UI_DEBUGGING_POLICY]},toolsInfo:{tools:tools12,createToolSessionContext,createToolExecutor},cliInfo:{cliProvider}};import{EventEmitter}from"node:events";import{WebSocket}from"ws";var NodeCDPSession=class extends EventEmitter{ws=null;reqId=0;pending=new Map;connected=!1;wsUrl;connectOptions;constructor(wsUrl,connectOptions){super(),this.wsUrl=wsUrl,this.connectOptions=connectOptions}async connect(){if(this.connected)return;let wsOptions=this.connectOptions?.headers?{headers:this.connectOptions.headers}:void 0;return new Promise((resolve,reject)=>{this.ws=new WebSocket(this.wsUrl,wsOptions);let timeout=setTimeout(()=>{reject(new Error(`Connection timeout to ${this.wsUrl}`)),this.ws?.close()},1e4);this.ws.on("open",()=>{clearTimeout(timeout),this.connected=!0,resolve()}),this.ws.on("error",err=>{clearTimeout(timeout),this.connected||reject(err)}),this.ws.on("close",()=>{this.connected=!1;for(let[id,handler]of this.pending)handler.reject(new Error("CDP session closed"));this.pending.clear()}),this.ws.on("message",data=>{try{let msg=JSON.parse(data.toString());this._handleMessage(msg)}catch{}})})}async send(method,params){if(!this.ws||!this.connected)throw new Error("CDP session is not connected");let id=++this.reqId;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});let message=JSON.stringify({id,method,params:params||{}});this.ws.send(message,err=>{err&&(this.pending.delete(id),reject(err))}),setTimeout(()=>{this.pending.has(id)&&(this.pending.delete(id),reject(new Error(`CDP command timeout: ${method}`)))},3e4)})}async detach(){if(this.ws){this.connected=!1,this.ws.close(),this.ws=null;for(let[,handler]of this.pending)handler.reject(new Error("CDP session detached"));this.pending.clear()}}isConnected(){return this.connected}_handleMessage(msg){if(msg.id!==void 0){let handler=this.pending.get(msg.id);handler&&(this.pending.delete(msg.id),msg.error?handler.reject(new Error(`CDP error: ${msg.error.message||JSON.stringify(msg.error)}`)):handler.resolve(msg.result||{}))}else msg.method&&this.emit(msg.method,msg.params)}};async function createNodeCDPSession(wsUrl,connectOptions){let session=new NodeCDPSession(wsUrl,connectOptions);return await session.connect(),session}import{execSync}from"node:child_process";import*as http from"node:http";var DEFAULT_HOST="127.0.0.1",DEFAULT_PORT=9229,DEFAULT_TIMEOUT_MS6=1e4,ACTIVATION_POLL_INTERVAL_MS=200,MAX_INSPECTOR_SCAN_PORTS=20;function buildInspectorWebSocketUrl(host,port,targetWebSocketDebuggerUrl){let path7=new URL(targetWebSocketDebuggerUrl).pathname;return`ws://${host}:${port}${path7}`}var DISCOVERY_HOST_HEADER="127.0.0.1:9229";function inspectorWsConnectOptions(wsUrl){try{let hostname=new URL(wsUrl).hostname.toLowerCase();return hostname==="127.0.0.1"||hostname==="localhost"?void 0:{headers:{Host:DISCOVERY_HOST_HEADER}}}catch{return}}async function discoverInspectorTargets(host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=5e3){return new Promise((resolve,reject)=>{let timeout=setTimeout(()=>{reject(new Error(`Inspector discovery timeout on ${host}:${port}`))},timeoutMs),req=http.get({host,port,path:"/json/list",...host!==DEFAULT_HOST&&host!=="localhost"&&{headers:{Host:DISCOVERY_HOST_HEADER}}},res=>{let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>{clearTimeout(timeout);try{let targets=JSON.parse(data);resolve(targets)}catch{reject(new Error(`Invalid response from inspector at ${host}:${port}`))}})});req.on("error",err=>{clearTimeout(timeout),reject(new Error(`Cannot reach inspector at ${host}:${port}: ${err.message}`))}),req.end()})}async function scanForInspector(host=DEFAULT_HOST,startPort=DEFAULT_PORT,maxPorts=MAX_INSPECTOR_SCAN_PORTS){for(let port=startPort;port<startPort+maxPorts;port++)try{let targets=await discoverInspectorTargets(host,port,1e3);if(targets.length>0)return{port,targets}}catch{}return null}function findNodeProcesses(namePattern){try{let platform=process.platform,output;platform==="win32"?output=execSync(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
831
- `)){let trimmed=line.trim();if(trimmed)if(platform==="win32"){let parts=trimmed.split(",");if(parts.length>=3){let pid=parseInt(parts[parts.length-1],10),command=parts.slice(1,-1).join(",");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}else{let parts=trimmed.split(/\s+/);if(parts.length>=11){let pid=parseInt(parts[1],10),command=parts.slice(10).join(" ");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}}if(namePattern){let regex=new RegExp(namePattern,"i");return processes.filter(p=>regex.test(p.command))}return processes}catch{return[]}}async function activateInspector(pid,host=DEFAULT_HOST,timeoutMs=DEFAULT_TIMEOUT_MS6){if(process.platform==="win32")throw new Error("SIGUSR1 activation is not supported on Windows. Start the Node.js process with --inspect flag or NODE_OPTIONS=--inspect instead.");try{process.kill(pid,0)}catch{throw new Error(`Process ${pid} does not exist or is not accessible`)}debug(`Sending SIGUSR1 to process ${pid} to activate V8 Inspector...`),process.kill(pid,"SIGUSR1");let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){let result=await scanForInspector(host,DEFAULT_PORT,10);if(result)return debug(`Inspector activated on port ${result.port} for process ${pid}`),result;await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector to activate on process ${pid}`)}function resolveContainerId(containerName){try{let ids=execSync(`docker ps -q -f name=${containerName}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
832
- `).filter(Boolean);return ids.length===0?null:(ids.length>1&&debug(`Multiple containers match "${containerName}", using first: ${ids[0]}`),ids[0])}catch(err){if(/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??""))throw new Error(`${err.message} When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.`);return null}}async function activateInspectorInContainer(containerId,host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=DEFAULT_TIMEOUT_MS6){if(process.platform==="win32")throw new Error("Docker container activation may have limitations on Windows. Ensure the container has port 9229 exposed and Node is started with --inspect.");let hostPid="";try{let lines=execSync(`docker top ${containerId}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
833
- `);if(lines.length<2)throw new Error(`No Node.js process found inside container ${containerId}`);for(let i=1;i<lines.length;i++){let line=lines[i];if(!/node/i.test(line))continue;let parts=line.trim().split(/\s+/);if(parts.length>=2&&/^\d+$/.test(parts[1])){hostPid=parts[1];break}}if(!hostPid)throw new Error(`Could not parse Node PID from container ${containerId}`)}catch(err){if(err.message?.includes("No Node.js process"))throw err;let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Cannot find Node process in container ${containerId}: ${err.message}. Ensure Docker is running and the container has a Node.js process.${dockerHint}`)}debug(`Sending SIGUSR1 to Node process (host PID ${hostPid}) for container ${containerId}...`);try{execSync(`docker run --rm --pid=host alpine kill -USR1 ${hostPid}`,{encoding:"utf-8",timeout:1e4})}catch(err){let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Failed to send SIGUSR1 to process in container ${containerId}: ${err.message}. (Uses docker run --rm --pid=host alpine kill -USR1 <hostPid>; ensure alpine image is available.)${dockerHint}`)}let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){try{let targets=await discoverInspectorTargets(host,port,2e3);if(targets.length>0)return debug(`Inspector active at ${host}:${port} for container ${containerId}`),{port,targets}}catch(err){debug(`Inspector not yet ready at ${host}:${port}: ${err instanceof Error?err.message:String(err)}`)}await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector from container ${containerId} at ${host}:${port}. Ensure: (1) the container has NODE_OPTIONS=--inspect-port=0.0.0.0:9229 so inspector accepts external connections when activated via SIGUSR1; (2) the container exposes port 9229 to the host (e.g. -p 30019:9229); (3) when MCP runs in a container, host is reachable (e.g. NODE_INSPECTOR_HOST=host.docker.internal and on Linux add extra_hosts: host.docker.internal:host-gateway).`)}async function connectToNodeProcess(options){let host=options.host??NODE_INSPECTOR_HOST??DEFAULT_HOST,timeoutMs=options.timeoutMs||DEFAULT_TIMEOUT_MS6,activateIfNeeded=options.activateIfNeeded!==!1;if(options.wsUrl)return debug(`Connecting to Node.js inspector via WebSocket: ${options.wsUrl}`),{session:await createNodeCDPSession(options.wsUrl,inspectorWsConnectOptions(options.wsUrl)),processInfo:{pid:0,inspectorActive:!0,webSocketUrl:options.wsUrl},target:{id:"direct",type:"node",title:"Direct WebSocket connection",url:"",webSocketDebuggerUrl:options.wsUrl}};let containerId=options.containerId??null;if(!containerId&&options.containerName&&(containerId=resolveContainerId(options.containerName),!containerId))throw new Error(`No running container found matching name: ${options.containerName}`);if(containerId){let port2=options.port||DEFAULT_PORT,targets2=[];try{targets2=await discoverInspectorTargets(host,port2,3e3)}catch{}if(targets2.length===0&&activateIfNeeded&&(debug(`Inspector not active. Activating via SIGUSR1 in container ${containerId}...`),targets2=(await activateInspectorInContainer(containerId,host,port2,timeoutMs)).targets),targets2.length===0)throw new Error(`Cannot connect to Node.js in container ${containerId}. Inspector not active. Ensure port 9229 is exposed (e.g. -p 9229:9229) and use activateIfNeeded or start Node with --inspect.`);let target=targets2[0],wsUrl=buildInspectorWebSocketUrl(host,port2,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid:0,inspectorActive:!0,inspectorHost:host,inspectorPort:port2,webSocketUrl:wsUrl,containerId},target}}let pid=options.pid,processCommand;if(!pid&&options.processName){let processes=findNodeProcesses(options.processName);if(processes.length===0)throw new Error(`No Node.js process found matching: ${options.processName}`);if(processes.length>1){let procList=processes.map(p=>` PID ${p.pid}: ${p.command}`).join(`
738
+ `;var EXECUTE_IMPORTANT_DESCRIPTION_BROWSER="\n**IMPORTANT:** \n- `page` (Playwright Page) is available in the VM \u2014 use it for navigation or `page.evaluate()`. \n- Prefer interaction tools with refs (e1, e2 from `a11y_take-aria-snapshot`); use raw Playwright only as last resort.\n- `document`/`window` are not in the VM \u2014 use `page.evaluate(() => { ... })` to run code in the browser.\n- Use `waitForNavigation: true` on `interaction_click` when the click navigates. \n- After navigation, do not continue with refs from the previous page \u2014 take fresh refs with `a11y_take-aria-snapshot` first.\n".trim(),EXECUTE_DESCRIPTION_BROWSER=`
739
+ **Example** \u2014 fill form, submit (with navigation wait), then snapshot and screenshot:
740
+ await callTool('interaction_fill', { selector: 'e3', value: 'user@test.com' });
741
+ await callTool('interaction_fill', { selector: 'e5', value: 'secret123' });
742
+ await callTool('interaction_click', { selector: 'e7', waitForNavigation: true });
743
+ // Or with page.locator: await page.locator('button').click();
744
+ // Or with page.evaluate: await page.evaluate(() => document.querySelector('button').click());
745
+ await callTool('a11y_take-aria-snapshot', {}, true);
746
+ await callTool('content_take-screenshot', {}, true);
747
+ `.trim(),platformInfo={serverInfo:{instructions:BROWSER_SERVER_INSTRUCTIONS_ENABLE?SERVER_INSTRUCTIONS:void 0,policies:BROWSER_POLICY_UI_DEBUGGING_ENABLE?[UI_DEBUGGING_POLICY]:[]},toolsInfo:{tools:tools11,createToolSessionContext,createToolExecutor,executeImportantDescription:EXECUTE_IMPORTANT_DESCRIPTION_BROWSER,executeDescription:EXECUTE_DESCRIPTION_BROWSER},cliInfo:{cliProvider}};import{EventEmitter}from"node:events";import{WebSocket}from"ws";var NodeCDPSession=class extends EventEmitter{ws=null;reqId=0;pending=new Map;connected=!1;wsUrl;connectOptions;constructor(wsUrl,connectOptions){super(),this.wsUrl=wsUrl,this.connectOptions=connectOptions}async connect(){if(this.connected)return;let wsOptions=this.connectOptions?.headers?{headers:this.connectOptions.headers}:void 0;return new Promise((resolve,reject)=>{this.ws=new WebSocket(this.wsUrl,wsOptions);let timeout=setTimeout(()=>{reject(new Error(`Connection timeout to ${this.wsUrl}`)),this.ws?.close()},1e4);this.ws.on("open",()=>{clearTimeout(timeout),this.connected=!0,resolve()}),this.ws.on("error",err=>{clearTimeout(timeout),this.connected||reject(err)}),this.ws.on("close",()=>{this.connected=!1;for(let[id,handler]of this.pending)handler.reject(new Error("CDP session closed"));this.pending.clear()}),this.ws.on("message",data=>{try{let msg=JSON.parse(data.toString());this._handleMessage(msg)}catch{}})})}async send(method,params){if(!this.ws||!this.connected)throw new Error("CDP session is not connected");let id=++this.reqId;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});let message=JSON.stringify({id,method,params:params||{}});this.ws.send(message,err=>{err&&(this.pending.delete(id),reject(err))}),setTimeout(()=>{this.pending.has(id)&&(this.pending.delete(id),reject(new Error(`CDP command timeout: ${method}`)))},3e4)})}async detach(){if(this.ws){this.connected=!1,this.ws.close(),this.ws=null;for(let[,handler]of this.pending)handler.reject(new Error("CDP session detached"));this.pending.clear()}}isConnected(){return this.connected}_handleMessage(msg){if(msg.id!==void 0){let handler=this.pending.get(msg.id);handler&&(this.pending.delete(msg.id),msg.error?handler.reject(new Error(`CDP error: ${msg.error.message||JSON.stringify(msg.error)}`)):handler.resolve(msg.result||{}))}else msg.method&&this.emit(msg.method,msg.params)}};async function createNodeCDPSession(wsUrl,connectOptions){let session=new NodeCDPSession(wsUrl,connectOptions);return await session.connect(),session}import{execSync}from"node:child_process";import*as http from"node:http";var DEFAULT_HOST="127.0.0.1",DEFAULT_PORT=9229,DEFAULT_TIMEOUT_MS5=1e4,ACTIVATION_POLL_INTERVAL_MS=200,MAX_INSPECTOR_SCAN_PORTS=20;function buildInspectorWebSocketUrl(host,port,targetWebSocketDebuggerUrl){let path7=new URL(targetWebSocketDebuggerUrl).pathname;return`ws://${host}:${port}${path7}`}var DISCOVERY_HOST_HEADER="127.0.0.1:9229";function inspectorWsConnectOptions(wsUrl){try{let hostname=new URL(wsUrl).hostname.toLowerCase();return hostname==="127.0.0.1"||hostname==="localhost"?void 0:{headers:{Host:DISCOVERY_HOST_HEADER}}}catch{return}}async function discoverInspectorTargets(host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=5e3){return new Promise((resolve,reject)=>{let timeout=setTimeout(()=>{reject(new Error(`Inspector discovery timeout on ${host}:${port}`))},timeoutMs),req=http.get({host,port,path:"/json/list",...host!==DEFAULT_HOST&&host!=="localhost"&&{headers:{Host:DISCOVERY_HOST_HEADER}}},res=>{let data="";res.on("data",chunk=>{data+=chunk}),res.on("end",()=>{clearTimeout(timeout);try{let targets=JSON.parse(data);resolve(targets)}catch{reject(new Error(`Invalid response from inspector at ${host}:${port}`))}})});req.on("error",err=>{clearTimeout(timeout),reject(new Error(`Cannot reach inspector at ${host}:${port}: ${err.message}`))}),req.end()})}async function scanForInspector(host=DEFAULT_HOST,startPort=DEFAULT_PORT,maxPorts=MAX_INSPECTOR_SCAN_PORTS){for(let port=startPort;port<startPort+maxPorts;port++)try{let targets=await discoverInspectorTargets(host,port,1e3);if(targets.length>0)return{port,targets}}catch{}return null}function findNodeProcesses(namePattern){try{let platform=process.platform,output;platform==="win32"?output=execSync(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
748
+ `)){let trimmed=line.trim();if(trimmed)if(platform==="win32"){let parts=trimmed.split(",");if(parts.length>=3){let pid=parseInt(parts[parts.length-1],10),command=parts.slice(1,-1).join(",");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}else{let parts=trimmed.split(/\s+/);if(parts.length>=11){let pid=parseInt(parts[1],10),command=parts.slice(10).join(" ");!isNaN(pid)&&pid>0&&processes.push({pid,command})}}}if(namePattern){let regex=new RegExp(namePattern,"i");return processes.filter(p=>regex.test(p.command))}return processes}catch{return[]}}async function activateInspector(pid,host=DEFAULT_HOST,timeoutMs=DEFAULT_TIMEOUT_MS5){if(process.platform==="win32")throw new Error("SIGUSR1 activation is not supported on Windows. Start the Node.js process with --inspect flag or NODE_OPTIONS=--inspect instead.");try{process.kill(pid,0)}catch{throw new Error(`Process ${pid} does not exist or is not accessible`)}debug(`Sending SIGUSR1 to process ${pid} to activate V8 Inspector...`),process.kill(pid,"SIGUSR1");let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){let result=await scanForInspector(host,DEFAULT_PORT,10);if(result)return debug(`Inspector activated on port ${result.port} for process ${pid}`),result;await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector to activate on process ${pid}`)}function resolveContainerId(containerName){try{let ids=execSync(`docker ps -q -f name=${containerName}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
749
+ `).filter(Boolean);return ids.length===0?null:(ids.length>1&&debug(`Multiple containers match "${containerName}", using first: ${ids[0]}`),ids[0])}catch(err){if(/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??""))throw new Error(`${err.message} When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.`);return null}}async function activateInspectorInContainer(containerId,host=DEFAULT_HOST,port=DEFAULT_PORT,timeoutMs=DEFAULT_TIMEOUT_MS5){if(process.platform==="win32")throw new Error("Docker container activation may have limitations on Windows. Ensure the container has port 9229 exposed and Node is started with --inspect.");let hostPid="";try{let lines=execSync(`docker top ${containerId}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
750
+ `);if(lines.length<2)throw new Error(`No Node.js process found inside container ${containerId}`);for(let i=1;i<lines.length;i++){let line=lines[i];if(!/node/i.test(line))continue;let parts=line.trim().split(/\s+/);if(parts.length>=2&&/^\d+$/.test(parts[1])){hostPid=parts[1];break}}if(!hostPid)throw new Error(`Could not parse Node PID from container ${containerId}`)}catch(err){if(err.message?.includes("No Node.js process"))throw err;let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Cannot find Node process in container ${containerId}: ${err.message}. Ensure Docker is running and the container has a Node.js process.${dockerHint}`)}debug(`Sending SIGUSR1 to Node process (host PID ${hostPid}) for container ${containerId}...`);try{execSync(`docker run --rm --pid=host alpine kill -USR1 ${hostPid}`,{encoding:"utf-8",timeout:1e4})}catch(err){let dockerHint=/Cannot connect to the Docker daemon|docker: command not found/i.test(err.message??"")?" When MCP runs in a container, mount /var/run/docker.sock and ensure the docker CLI is available.":"";throw new Error(`Failed to send SIGUSR1 to process in container ${containerId}: ${err.message}. (Uses docker run --rm --pid=host alpine kill -USR1 <hostPid>; ensure alpine image is available.)${dockerHint}`)}let startTime=Date.now();for(;Date.now()-startTime<timeoutMs;){try{let targets=await discoverInspectorTargets(host,port,2e3);if(targets.length>0)return debug(`Inspector active at ${host}:${port} for container ${containerId}`),{port,targets}}catch(err){debug(`Inspector not yet ready at ${host}:${port}: ${err instanceof Error?err.message:String(err)}`)}await new Promise(resolve=>setTimeout(resolve,ACTIVATION_POLL_INTERVAL_MS))}throw new Error(`Timeout waiting for inspector from container ${containerId} at ${host}:${port}. Ensure: (1) the container has NODE_OPTIONS=--inspect-port=0.0.0.0:9229 so inspector accepts external connections when activated via SIGUSR1; (2) the container exposes port 9229 to the host (e.g. -p 30019:9229); (3) when MCP runs in a container, host is reachable (e.g. NODE_INSPECTOR_HOST=host.docker.internal and on Linux add extra_hosts: host.docker.internal:host-gateway).`)}async function connectToNodeProcess(options){let host=options.host??NODE_INSPECTOR_HOST??DEFAULT_HOST,timeoutMs=options.timeoutMs||DEFAULT_TIMEOUT_MS5,activateIfNeeded=options.activateIfNeeded!==!1;if(options.wsUrl)return debug(`Connecting to Node.js inspector via WebSocket: ${options.wsUrl}`),{session:await createNodeCDPSession(options.wsUrl,inspectorWsConnectOptions(options.wsUrl)),processInfo:{pid:0,inspectorActive:!0,webSocketUrl:options.wsUrl},target:{id:"direct",type:"node",title:"Direct WebSocket connection",url:"",webSocketDebuggerUrl:options.wsUrl}};let containerId=options.containerId??null;if(!containerId&&options.containerName&&(containerId=resolveContainerId(options.containerName),!containerId))throw new Error(`No running container found matching name: ${options.containerName}`);if(containerId){let port2=options.port||DEFAULT_PORT,targets2=[];try{targets2=await discoverInspectorTargets(host,port2,3e3)}catch{}if(targets2.length===0&&activateIfNeeded&&(debug(`Inspector not active. Activating via SIGUSR1 in container ${containerId}...`),targets2=(await activateInspectorInContainer(containerId,host,port2,timeoutMs)).targets),targets2.length===0)throw new Error(`Cannot connect to Node.js in container ${containerId}. Inspector not active. Ensure port 9229 is exposed (e.g. -p 9229:9229) and use activateIfNeeded or start Node with --inspect.`);let target=targets2[0],wsUrl=buildInspectorWebSocketUrl(host,port2,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid:0,inspectorActive:!0,inspectorHost:host,inspectorPort:port2,webSocketUrl:wsUrl,containerId},target}}let pid=options.pid,processCommand;if(!pid&&options.processName){let processes=findNodeProcesses(options.processName);if(processes.length===0)throw new Error(`No Node.js process found matching: ${options.processName}`);if(processes.length>1){let procList=processes.map(p=>` PID ${p.pid}: ${p.command}`).join(`
834
751
  `);throw new Error(`Multiple Node.js processes match "${options.processName}":
835
752
  ${procList}
836
- Please specify a PID directly.`)}pid=processes[0].pid,processCommand=processes[0].command,debug(`Found Node.js process: PID ${pid} - ${processCommand}`)}let port=options.port||DEFAULT_PORT,targets=[];try{targets=await discoverInspectorTargets(host,port,3e3)}catch{}if(targets.length>0){let target=targets[0];debug(`Inspector already active at ${host}:${port}, connecting...`);let wsUrl=buildInspectorWebSocketUrl(host,port,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid:pid||0,command:processCommand,inspectorActive:!0,inspectorHost:host,inspectorPort:port,webSocketUrl:wsUrl},target}}if(pid&&activateIfNeeded){debug(`Inspector not active on ${host}:${port}. Activating via SIGUSR1 on PID ${pid}...`);let{port:activePort,targets:activeTargets}=await activateInspector(pid,host,timeoutMs);if(activeTargets.length===0)throw new Error(`Inspector activated on port ${activePort} but no targets found`);let target=activeTargets[0],wsUrl=buildInspectorWebSocketUrl(host,activePort,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid,command:processCommand,inspectorActive:!0,inspectorHost:host,inspectorPort:activePort,webSocketUrl:wsUrl},target}}throw pid?new Error(`Cannot connect to process ${pid}. Inspector is not active and activation failed. Try starting the process with NODE_OPTIONS=--inspect or --inspect flag.`):new Error("No target specified. Provide a PID, process name, port, or WebSocket URL. If the process is not started with --inspect, provide a PID to activate via SIGUSR1.")}var NodeToolSessionContext=class{_sessionId;_connection=null;_storeKey=null;closed=!1;constructor(sessionId){this._sessionId=sessionId}sessionId(){return this._sessionId}async connect(options){if(this._connection)throw new Error("Already connected to a Node.js process. Disconnect first.");debug(`Connecting to Node.js process with options: ${JSON.stringify(options)}`);let result=await connectToNodeProcess(options);return this._connection=result,this._storeKey=getStoreKey(result.processInfo.pid,result.processInfo.webSocketUrl),await enableDebugging(this._storeKey,result.session,{pid:result.processInfo.pid,command:result.processInfo.command}),debug(`Connected to Node.js process: PID=${result.processInfo.pid}, store=${this._storeKey}`),{processInfo:result.processInfo,target:result.target}}async disconnect(){!this._connection||!this._storeKey||(debug(`Disconnecting from Node.js process: ${this._storeKey}`),await detachDebugging(this._storeKey),await this._connection.session.detach(),this._connection=null,this._storeKey=null)}get storeKey(){if(!this._storeKey)throw new Error("Not connected to any Node.js process. Call connect first.");return this._storeKey}get isConnected(){return this._connection!==null&&this._connection.session.isConnected()}get processInfo(){return this._connection?.processInfo??null}async close(){return this.closed?!1:(await this.disconnect(),this.closed=!0,!0)}};var NodeToolExecutor=class{async executeTool(context,tool,args){debug(`Executing Node.js tool ${tool.name()} with input: ${toJson(args)}`);try{let result=await tool.handle(context,args);return debug(`Executed Node.js tool ${tool.name()} successfully`),result}catch(err){throw debug(`Error executing Node.js tool ${tool.name()}: ${err}`),err}}};import{z as z47}from"zod";var Connect=class{name(){return"debug_connect"}description(){return`
753
+ Please specify a PID directly.`)}pid=processes[0].pid,processCommand=processes[0].command,debug(`Found Node.js process: PID ${pid} - ${processCommand}`)}let port=options.port||DEFAULT_PORT,targets=[];try{targets=await discoverInspectorTargets(host,port,3e3)}catch{}if(targets.length>0){let target=targets[0];debug(`Inspector already active at ${host}:${port}, connecting...`);let wsUrl=buildInspectorWebSocketUrl(host,port,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid:pid||0,command:processCommand,inspectorActive:!0,inspectorHost:host,inspectorPort:port,webSocketUrl:wsUrl},target}}if(pid&&activateIfNeeded){debug(`Inspector not active on ${host}:${port}. Activating via SIGUSR1 on PID ${pid}...`);let{port:activePort,targets:activeTargets}=await activateInspector(pid,host,timeoutMs);if(activeTargets.length===0)throw new Error(`Inspector activated on port ${activePort} but no targets found`);let target=activeTargets[0],wsUrl=buildInspectorWebSocketUrl(host,activePort,target.webSocketDebuggerUrl);return{session:await createNodeCDPSession(wsUrl,inspectorWsConnectOptions(wsUrl)),processInfo:{pid,command:processCommand,inspectorActive:!0,inspectorHost:host,inspectorPort:activePort,webSocketUrl:wsUrl},target}}throw pid?new Error(`Cannot connect to process ${pid}. Inspector is not active and activation failed. Try starting the process with NODE_OPTIONS=--inspect or --inspect flag.`):new Error("No target specified. Provide a PID, process name, port, or WebSocket URL. If the process is not started with --inspect, provide a PID to activate via SIGUSR1.")}var NodeToolSessionContext=class{_sessionId;_connection=null;_storeKey=null;closed=!1;constructor(sessionId){this._sessionId=sessionId}sessionId(){return this._sessionId}executionContext(){return{}}async connect(options){if(this._connection)throw new Error("Already connected to a Node.js process. Disconnect first.");debug(`Connecting to Node.js process with options: ${JSON.stringify(options)}`);let result=await connectToNodeProcess(options);return this._connection=result,this._storeKey=getStoreKey(result.processInfo.pid,result.processInfo.webSocketUrl),await enableDebugging(this._storeKey,result.session,{pid:result.processInfo.pid,command:result.processInfo.command}),debug(`Connected to Node.js process: PID=${result.processInfo.pid}, store=${this._storeKey}`),{processInfo:result.processInfo,target:result.target}}async disconnect(){!this._connection||!this._storeKey||(debug(`Disconnecting from Node.js process: ${this._storeKey}`),await detachDebugging(this._storeKey),await this._connection.session.detach(),this._connection=null,this._storeKey=null)}get storeKey(){if(!this._storeKey)throw new Error("Not connected to any Node.js process. Call connect first.");return this._storeKey}get isConnected(){return this._connection!==null&&this._connection.session.isConnected()}get processInfo(){return this._connection?.processInfo??null}async close(){return this.closed?!1:(await this.disconnect(),this.closed=!0,!0)}};var NodeToolExecutor=class{async executeTool(context,tool,args){debug(`Executing Node.js tool ${tool.name()} with input: ${toJson(args)}`);try{let result=await tool.handle(context,args);return debug(`Executed Node.js tool ${tool.name()} successfully`),result}catch(err){throw debug(`Error executing Node.js tool ${tool.name()}: ${err}`),err}}};import{z as z45}from"zod";var Connect=class{name(){return"debug_connect"}description(){return`
837
754
  Connects to a Node.js process for V8 debugging. Supports PID, process name, Docker (containerId/containerName, port 9229),
838
755
  inspector port, or WebSocket URL. Activates inspector via SIGUSR1 if needed; for Docker, uses docker top + exec.
839
756
  Default host 127.0.0.1 (NODE_INSPECTOR_HOST), port 9229. Container-to-container: use host.docker.internal, map port, mount docker.sock.
840
757
  After connect, use tracepoints, logpoints, exceptionpoints, watch expressions.
841
- `.trim()}inputSchema(){return{pid:z47.number().int().positive().optional(),processName:z47.string().optional().describe("Process name pattern to match."),containerId:z47.string().optional().describe("Docker container ID."),containerName:z47.string().optional().describe("Container name pattern."),host:z47.string().optional(),inspectorPort:z47.number().int().positive().optional().describe("Inspector port (default: 9229)."),wsUrl:z47.string().optional().describe("WebSocket URL when debugger already listening.")}}outputSchema(){return{connected:z47.boolean().describe("Whether connection was successful"),pid:z47.number().describe("Process ID"),command:z47.string().optional().describe("Process command line"),containerId:z47.string().optional().describe("Docker container ID when connected via container"),inspectorHost:z47.string().optional().describe("Inspector host"),inspectorPort:z47.number().optional().describe("Inspector port"),webSocketUrl:z47.string().optional().describe("WebSocket URL"),title:z47.string().describe("Target title")}}async handle(context,args){let result=await context.connect({pid:args.pid,processName:args.processName,containerId:args.containerId,containerName:args.containerName,host:args.host,port:args.inspectorPort,wsUrl:args.wsUrl,activateIfNeeded:!0});return{connected:!0,pid:result.processInfo.pid,command:result.processInfo.command,containerId:result.processInfo.containerId,inspectorHost:result.processInfo.inspectorHost,inspectorPort:result.processInfo.inspectorPort,webSocketUrl:result.processInfo.webSocketUrl,title:result.target.title}}};import{z as z48}from"zod";var Disconnect=class{name(){return"debug_disconnect"}description(){return"Disconnects from the currently connected Node.js process and cleans up debugging resources."}inputSchema(){return{}}outputSchema(){return{disconnected:z48.boolean().describe("Whether disconnection was successful")}}async handle(context,_args){return await context.disconnect(),{disconnected:!0}}};import{z as z49}from"zod";function applySnapshotFilters(snapshots,fromSequence,limit){let out=snapshots;return fromSequence!==void 0&&(out=out.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&out.length>limit&&(out=out.slice(-limit)),out}var GET_SNAPSHOT_TYPES=["tracepoint","logpoint","exceptionpoint"],GetProbeSnapshots2=class{name(){return"debug_get-probe-snapshots"}description(){return"\nRetrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, returns all.\nResponse: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots. Optional probeId, fromSequence, limit apply per type.\n ".trim()}inputSchema(){return{types:z49.array(z49.enum(GET_SNAPSHOT_TYPES)).optional().describe("Which snapshot types to return. If omitted or empty, all are returned."),probeId:z49.string().optional().describe("Filter tracepoint or logpoint snapshots by this probe ID"),fromSequence:z49.number().int().nonnegative().optional().describe("Return snapshots with sequence number > fromSequence (per type)."),limit:z49.number().int().positive().optional().describe("Maximum number of snapshots per type.")}}outputSchema(){return{tracepointSnapshots:z49.array(z49.any()).describe("Tracepoint snapshots"),logpointSnapshots:z49.array(z49.any()).describe("Logpoint snapshots"),exceptionpointSnapshots:z49.array(z49.any()).describe("Exceptionpoint snapshots")}}async handle(context,args){let getTracepoint=!args.types?.length||args.types.includes("tracepoint"),getLogpoint=!args.types?.length||args.types.includes("logpoint"),getExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointSnapshots=[],logpointSnapshots=[],exceptionpointSnapshots=[],allSnapshots=getSnapshots(context.storeKey),probes=listProbes(context.storeKey);if(args.probeId&&(getTracepoint||getLogpoint)){let raw=getSnapshotsByProbe(context.storeKey,args.probeId),filtered=applySnapshotFilters(raw,args.fromSequence,args.limit),probe=probes.find(p=>p.id===args.probeId);probe?.kind==="tracepoint"&&getTracepoint?tracepointSnapshots=filtered:probe?.kind==="logpoint"&&getLogpoint&&(logpointSnapshots=filtered)}else{if(getTracepoint){let tracepointIds=new Set(probes.filter(p=>p.kind==="tracepoint").map(p=>p.id)),raw=allSnapshots.filter(s=>tracepointIds.has(s.probeId));tracepointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}if(getLogpoint){let logpointIds=new Set(probes.filter(p=>p.kind==="logpoint").map(p=>p.id)),raw=allSnapshots.filter(s=>logpointIds.has(s.probeId));logpointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}}if(getExceptionpoint){let raw=allSnapshots.filter(s=>s.probeId==="__exception__");exceptionpointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}return{tracepointSnapshots,logpointSnapshots,exceptionpointSnapshots}}},SNAPSHOT_TYPES3=["tracepoint","logpoint","exceptionpoint"],ClearProbeSnapshots2=class{name(){return"debug_clear-probe-snapshots"}description(){return"\nClears snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, clears all.\nOptional `probeId`: clear only snapshots for this probe (tracepoint/logpoint).\n ".trim()}inputSchema(){return{types:z49.array(z49.enum(SNAPSHOT_TYPES3)).optional().describe("Which snapshot types to clear. If omitted or empty, all are cleared."),probeId:z49.string().optional().describe("Clear only snapshots for this probe ID (tracepoint or logpoint). Ignored for exceptionpoint.")}}outputSchema(){return{tracepointCleared:z49.number().describe("Tracepoint snapshots cleared"),logpointCleared:z49.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z49.number().describe("Exceptionpoint snapshots cleared"),message:z49.string().describe("Status message")}}async handle(context,args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2,listProbes:listProbes3}=await import("./core-GW4S4YVO.js"),clearTracepoint=!args.types?.length||args.types.includes("tracepoint"),clearLogpoint=!args.types?.length||args.types.includes("logpoint"),clearExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointCleared=0,logpointCleared=0,exceptionpointCleared=0;if(args.probeId&&(clearTracepoint||clearLogpoint)){let n=clearSnapshotsByProbe2(context.storeKey,args.probeId),p=listProbes3(context.storeKey).find(x=>x.id===args.probeId);clearTracepoint&&clearLogpoint?p?.kind==="tracepoint"?tracepointCleared=n:p?.kind==="logpoint"&&(logpointCleared=n):clearTracepoint?tracepointCleared=n:logpointCleared=n}else{if(clearTracepoint)for(let p of listProbes3(context.storeKey).filter(x=>x.kind==="tracepoint"))tracepointCleared+=clearSnapshotsByProbe2(context.storeKey,p.id);if(clearLogpoint)for(let p of listProbes3(context.storeKey).filter(x=>x.kind==="logpoint"))logpointCleared+=clearSnapshotsByProbe2(context.storeKey,p.id);clearExceptionpoint&&(exceptionpointCleared=clearSnapshotsByProbe2(context.storeKey,"__exception__"))}let parts=[tracepointCleared&&`${tracepointCleared} tracepoint snapshot(s)`,logpointCleared&&`${logpointCleared} logpoint snapshot(s)`,exceptionpointCleared&&`${exceptionpointCleared} exceptionpoint snapshot(s)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointCleared,logpointCleared,exceptionpointCleared,message}}};import{z as z50}from"zod";var GetLogs=class{name(){return"debug_get-logs"}description(){return"Retrieves console messages/logs from the Node.js process with filtering options."}inputSchema(){return{type:z50.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z50.string().optional().describe("Filter by message text."),timestamp:z50.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z50.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z50.object({count:z50.number().int().nonnegative().default(0).describe("0 = no limit."),from:z50.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).optional()}}outputSchema(){return{messages:z50.array(z50.object({type:z50.string().describe("Type of the console message."),text:z50.string().describe("Text of the console message."),location:z50.object({url:z50.string().describe("URL of the resource."),lineNumber:z50.number().nonnegative().describe("0-based line number."),columnNumber:z50.number().nonnegative().describe("0-based column number.")}).describe("Location of the console message.").optional(),timestamp:z50.number().int().nonnegative().describe("Unix epoch timestamp (ms)."),sequenceNumber:z50.number().int().nonnegative().describe("Monotonic sequence number.")})).describe("Retrieved console messages.")}}async handle(context,args){let levelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filtered=getConsoleMessages(context.storeKey).filter(msg=>!(levelCodeThreshold!==void 0&&msg.level.code<levelCodeThreshold||args.timestamp&&msg.timestamp<args.timestamp||args.sequenceNumber&&msg.sequenceNumber<=args.sequenceNumber||args.search&&!msg.text.includes(args.search)));return{messages:(args.limit?.count?args.limit.from==="start"?filtered.slice(0,args.limit.count):filtered.slice(-args.limit.count):filtered).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z51}from"zod";var REMOVE_TYPES2=["tracepoint","logpoint","watch"],RemoveProbe2=class{name(){return"debug_remove-probe"}description(){return"Removes a tracepoint, logpoint, or watch expression by ID. `type`: tracepoint, logpoint, or watch. `id`: the probe or watch ID."}inputSchema(){return{type:z51.enum(REMOVE_TYPES2).describe("Kind of probe to remove: tracepoint, logpoint, or watch"),id:z51.string().describe("Probe or watch expression ID to remove")}}outputSchema(){return{removed:z51.boolean().describe("Whether the probe or watch was removed"),message:z51.string().describe("Status message")}}async handle(context,args){if(args.type==="watch"){let removed2=removeWatchExpression(context.storeKey,args.id);return{removed:removed2,message:removed2?`Watch expression ${args.id} removed`:`Watch expression ${args.id} not found`}}let removed=await removeProbe(context.storeKey,args.id),label=args.type==="tracepoint"?"Tracepoint":"Logpoint";return{removed,message:removed?`${label} ${args.id} removed`:`Failed to remove ${args.type}`}}},LIST_TYPES2=["tracepoint","logpoint","watch"],ListProbes2=class{name(){return"debug_list-probes"}description(){return"Lists tracepoints, logpoints, and/or watch expressions. Optional `types`: array of `tracepoint`, `logpoint`, `watch`. If omitted or empty, returns all."}inputSchema(){return{types:z51.array(z51.enum(LIST_TYPES2)).optional().describe("Which probe types to list. If omitted or empty, all are returned.")}}outputSchema(){return{tracepoints:z51.array(z51.any()).describe("Tracepoints"),logpoints:z51.array(z51.any()).describe("Logpoints"),watches:z51.array(z51.any()).describe("Watch expressions")}}async handle(context,args){let listTracepoints=!args.types?.length||args.types.includes("tracepoint"),listLogpoints=!args.types?.length||args.types.includes("logpoint"),listWatches=!args.types?.length||args.types.includes("watch"),probes=listProbes(context.storeKey),tracepoints=listTracepoints?probes.filter(p=>p.kind==="tracepoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled})):[],logpoints=listLogpoints?probes.filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled})):[],watches=listWatches?listWatchExpressions(context.storeKey):[];return{tracepoints,logpoints,watches}}},PROBE_TYPES2=["tracepoint","logpoint","watches"],ClearProbes2=class{name(){return"debug_clear-probes"}description(){return"Removes tracepoints, logpoints, and/or watch expressions. Optional `types`: array of `tracepoint`, `logpoint`, `watches`. If omitted or empty, clears all."}inputSchema(){return{types:z51.array(z51.enum(PROBE_TYPES2)).optional().describe("Which probe types to clear. If omitted or empty, all are cleared.")}}outputSchema(){return{tracepointsCleared:z51.number().describe("Number of tracepoints cleared"),logpointsCleared:z51.number().describe("Number of logpoints cleared"),watchesCleared:z51.number().describe("Number of watch expressions cleared"),message:z51.string().describe("Status message")}}async handle(context,args){let clearTracepoints=!args.types?.length||args.types.includes("tracepoint"),clearLogpoints=!args.types?.length||args.types.includes("logpoint"),clearWatches=!args.types?.length||args.types.includes("watches"),tracepointsCleared=0,logpointsCleared=0,watchesCleared=0;if(clearTracepoints||clearLogpoints){let probes=listProbes(context.storeKey);for(let p of probes)p.kind==="tracepoint"&&clearTracepoints?await removeProbe(context.storeKey,p.id)&&tracepointsCleared++:p.kind==="logpoint"&&clearLogpoints&&await removeProbe(context.storeKey,p.id)&&logpointsCleared++}clearWatches&&(watchesCleared=clearWatchExpressions(context.storeKey));let parts=[];tracepointsCleared&&parts.push(`${tracepointsCleared} tracepoint(s)`),logpointsCleared&&parts.push(`${logpointsCleared} logpoint(s)`),watchesCleared&&parts.push(`${watchesCleared} watch(es)`);let message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointsCleared,logpointsCleared,watchesCleared,message}}};import{z as z52}from"zod";var PutExceptionpoint2=class{name(){return"debug_put-exceptionpoint"}description(){return`
758
+ `.trim()}inputSchema(){return{pid:z45.number().int().positive().optional(),processName:z45.string().optional().describe("Process name pattern to match."),containerId:z45.string().optional().describe("Docker container ID."),containerName:z45.string().optional().describe("Container name pattern."),host:z45.string().optional(),inspectorPort:z45.number().int().positive().optional().describe("Inspector port (default: 9229)."),wsUrl:z45.string().optional().describe("WebSocket URL when debugger already listening.")}}outputSchema(){return{connected:z45.boolean().describe("Whether connection was successful"),pid:z45.number().describe("Process ID"),command:z45.string().optional().describe("Process command line"),containerId:z45.string().optional().describe("Docker container ID when connected via container"),inspectorHost:z45.string().optional().describe("Inspector host"),inspectorPort:z45.number().optional().describe("Inspector port"),webSocketUrl:z45.string().optional().describe("WebSocket URL"),title:z45.string().describe("Target title")}}async handle(context,args){let result=await context.connect({pid:args.pid,processName:args.processName,containerId:args.containerId,containerName:args.containerName,host:args.host,port:args.inspectorPort,wsUrl:args.wsUrl,activateIfNeeded:!0});return{connected:!0,pid:result.processInfo.pid,command:result.processInfo.command,containerId:result.processInfo.containerId,inspectorHost:result.processInfo.inspectorHost,inspectorPort:result.processInfo.inspectorPort,webSocketUrl:result.processInfo.webSocketUrl,title:result.target.title}}};import{z as z46}from"zod";var Disconnect=class{name(){return"debug_disconnect"}description(){return"Disconnects from the currently connected Node.js process and cleans up debugging resources."}inputSchema(){return{}}outputSchema(){return{disconnected:z46.boolean().describe("Whether disconnection was successful")}}async handle(context,_args){return await context.disconnect(),{disconnected:!0}}};import{z as z47}from"zod";var SCOPE_TYPE_VALUES2=["global","local","with","closure","catch","block","script","eval","module","wasm-expression-stack"],NODE_DEFAULT_INCLUDE_SCOPES=["local"];function applySnapshotFilters(snapshots,fromSequence,limit){let out=snapshots;return fromSequence!==void 0&&(out=out.filter(s=>s.sequenceNumber>fromSequence)),limit!==void 0&&out.length>limit&&(out=out.slice(-limit)),out}var GET_SNAPSHOT_TYPES=["tracepoint","logpoint","exceptionpoint"],GetProbeSnapshots2=class{name(){return"debug_get-probe-snapshots"}description(){return`
759
+ Retrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.
760
+ Optional \`types\`: array of \`tracepoint\`, \`logpoint\`, \`exceptionpoint\`. If omitted or empty, returns all.
761
+ Response: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots. Optional probeId, fromSequence, limit apply per type.
762
+ Output trimming: by default only the top ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth} call stack frames are returned, only \`${NODE_DEFAULT_INCLUDE_SCOPES.join(", ")}\` scope(s) are included, and variables per scope are capped at ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}. Override with maxCallStackDepth, includeScopes, maxVariablesPerScope.
763
+ `.trim()}inputSchema(){return{types:z47.array(z47.enum(GET_SNAPSHOT_TYPES)).optional().describe("Which snapshot types to return. If omitted or empty, all are returned."),probeId:z47.string().optional().describe("Filter tracepoint or logpoint snapshots by this probe ID"),fromSequence:z47.number().int().nonnegative().optional().describe("Return snapshots with sequence number > fromSequence (per type)."),limit:z47.number().int().positive().optional().describe("Maximum number of snapshots per type."),maxCallStackDepth:z47.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxCallStackDepth).describe(`Max call stack frames per snapshot. Default ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth}.`),includeScopes:z47.array(z47.enum(SCOPE_TYPE_VALUES2)).optional().default([...NODE_DEFAULT_INCLUDE_SCOPES]).describe(`Scope types to include. Default [${NODE_DEFAULT_INCLUDE_SCOPES.join(", ")}] (local only to keep payload small).`),maxVariablesPerScope:z47.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxVariablesPerScope).describe(`Max variables per scope. Default ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}.`)}}outputSchema(){return{tracepointSnapshots:z47.array(z47.any()).describe("Tracepoint snapshots"),logpointSnapshots:z47.array(z47.any()).describe("Logpoint snapshots"),exceptionpointSnapshots:z47.array(z47.any()).describe("Exceptionpoint snapshots")}}async handle(context,args){let getTracepoint=!args.types?.length||args.types.includes("tracepoint"),getLogpoint=!args.types?.length||args.types.includes("logpoint"),getExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),trimOpts={maxCallStackDepth:args.maxCallStackDepth,includeScopes:args.includeScopes,maxVariablesPerScope:args.maxVariablesPerScope},tracepointSnapshots=[],logpointSnapshots=[],exceptionpointSnapshots=[],allSnapshots=getSnapshots(context.storeKey),probes=listProbes(context.storeKey);if(args.probeId&&(getTracepoint||getLogpoint)){let raw=getSnapshotsByProbe(context.storeKey,args.probeId),filtered=applySnapshotFilters(raw,args.fromSequence,args.limit),probe=probes.find(p=>p.id===args.probeId);probe?.kind==="tracepoint"&&getTracepoint?tracepointSnapshots=filtered:probe?.kind==="logpoint"&&getLogpoint&&(logpointSnapshots=filtered)}else{if(getTracepoint){let tracepointIds=new Set(probes.filter(p=>p.kind==="tracepoint").map(p=>p.id)),raw=allSnapshots.filter(s=>tracepointIds.has(s.probeId));tracepointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}if(getLogpoint){let logpointIds=new Set(probes.filter(p=>p.kind==="logpoint").map(p=>p.id)),raw=allSnapshots.filter(s=>logpointIds.has(s.probeId));logpointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}}if(getExceptionpoint){let raw=allSnapshots.filter(s=>s.probeId==="__exception__");exceptionpointSnapshots=applySnapshotFilters(raw,args.fromSequence,args.limit)}return{tracepointSnapshots:trimSnapshots(tracepointSnapshots,trimOpts),logpointSnapshots:trimSnapshots(logpointSnapshots,trimOpts),exceptionpointSnapshots:trimSnapshots(exceptionpointSnapshots,trimOpts)}}},SNAPSHOT_TYPES3=["tracepoint","logpoint","exceptionpoint"],ClearProbeSnapshots2=class{name(){return"debug_clear-probe-snapshots"}description(){return"\nClears snapshots captured by tracepoints, logpoints, and/or exceptionpoints.\nOptional `types`: array of `tracepoint`, `logpoint`, `exceptionpoint`. If omitted or empty, clears all.\nOptional `probeId`: clear only snapshots for this probe (tracepoint/logpoint).\n ".trim()}inputSchema(){return{types:z47.array(z47.enum(SNAPSHOT_TYPES3)).optional().describe("Which snapshot types to clear. If omitted or empty, all are cleared."),probeId:z47.string().optional().describe("Clear only snapshots for this probe ID (tracepoint or logpoint). Ignored for exceptionpoint.")}}outputSchema(){return{tracepointCleared:z47.number().describe("Tracepoint snapshots cleared"),logpointCleared:z47.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z47.number().describe("Exceptionpoint snapshots cleared"),message:z47.string().describe("Status message")}}async handle(context,args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2,listProbes:listProbes3}=await import("./core-FPD4CWST.js"),clearTracepoint=!args.types?.length||args.types.includes("tracepoint"),clearLogpoint=!args.types?.length||args.types.includes("logpoint"),clearExceptionpoint=!args.types?.length||args.types.includes("exceptionpoint"),tracepointCleared=0,logpointCleared=0,exceptionpointCleared=0;if(args.probeId&&(clearTracepoint||clearLogpoint)){let n=clearSnapshotsByProbe2(context.storeKey,args.probeId),p=listProbes3(context.storeKey).find(x=>x.id===args.probeId);clearTracepoint&&clearLogpoint?p?.kind==="tracepoint"?tracepointCleared=n:p?.kind==="logpoint"&&(logpointCleared=n):clearTracepoint?tracepointCleared=n:logpointCleared=n}else{if(clearTracepoint)for(let p of listProbes3(context.storeKey).filter(x=>x.kind==="tracepoint"))tracepointCleared+=clearSnapshotsByProbe2(context.storeKey,p.id);if(clearLogpoint)for(let p of listProbes3(context.storeKey).filter(x=>x.kind==="logpoint"))logpointCleared+=clearSnapshotsByProbe2(context.storeKey,p.id);clearExceptionpoint&&(exceptionpointCleared=clearSnapshotsByProbe2(context.storeKey,"__exception__"))}let parts=[tracepointCleared&&`${tracepointCleared} tracepoint snapshot(s)`,logpointCleared&&`${logpointCleared} logpoint snapshot(s)`,exceptionpointCleared&&`${exceptionpointCleared} exceptionpoint snapshot(s)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointCleared,logpointCleared,exceptionpointCleared,message}}};import{z as z48}from"zod";var GetLogs=class{name(){return"debug_get-logs"}description(){return"Retrieves console messages/logs from the Node.js process with filtering options."}inputSchema(){return{type:z48.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z48.string().optional().describe("Filter by message text."),timestamp:z48.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z48.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z48.object({count:z48.number().int().nonnegative().default(0).describe("0 = no limit."),from:z48.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).optional()}}outputSchema(){return{messages:z48.array(z48.object({type:z48.string().describe("Type of the console message."),text:z48.string().describe("Text of the console message."),location:z48.object({url:z48.string().describe("URL of the resource."),lineNumber:z48.number().nonnegative().describe("0-based line number."),columnNumber:z48.number().nonnegative().describe("0-based column number.")}).describe("Location of the console message.").optional(),timestamp:z48.number().int().nonnegative().describe("Unix epoch timestamp (ms)."),sequenceNumber:z48.number().int().nonnegative().describe("Monotonic sequence number.")})).describe("Retrieved console messages.")}}async handle(context,args){let levelCodeThreshold=args.type?ConsoleMessageLevel[args.type]?.code:void 0,filtered=getConsoleMessages(context.storeKey).filter(msg=>!(levelCodeThreshold!==void 0&&msg.level.code<levelCodeThreshold||args.timestamp&&msg.timestamp<args.timestamp||args.sequenceNumber&&msg.sequenceNumber<=args.sequenceNumber||args.search&&!msg.text.includes(args.search)));return{messages:(args.limit?.count?args.limit.from==="start"?filtered.slice(0,args.limit.count):filtered.slice(-args.limit.count):filtered).map(msg=>({type:msg.type,text:msg.text,location:msg.location?{url:msg.location.url,lineNumber:msg.location.lineNumber,columnNumber:msg.location.columnNumber}:void 0,timestamp:msg.timestamp,sequenceNumber:msg.sequenceNumber}))}}};import{z as z49}from"zod";var REMOVE_TYPES2=["tracepoint","logpoint","watch"],RemoveProbe2=class{name(){return"debug_remove-probe"}description(){return"Removes a tracepoint, logpoint, or watch expression by ID. `type`: tracepoint, logpoint, or watch. `id`: the probe or watch ID."}inputSchema(){return{type:z49.enum(REMOVE_TYPES2).describe("Kind of probe to remove: tracepoint, logpoint, or watch"),id:z49.string().describe("Probe or watch expression ID to remove")}}outputSchema(){return{removed:z49.boolean().describe("Whether the probe or watch was removed"),message:z49.string().describe("Status message")}}async handle(context,args){if(args.type==="watch"){let removed2=removeWatchExpression(context.storeKey,args.id);return{removed:removed2,message:removed2?`Watch expression ${args.id} removed`:`Watch expression ${args.id} not found`}}let removed=await removeProbe(context.storeKey,args.id),label=args.type==="tracepoint"?"Tracepoint":"Logpoint";return{removed,message:removed?`${label} ${args.id} removed`:`Failed to remove ${args.type}`}}},LIST_TYPES2=["tracepoint","logpoint","watch"],ListProbes2=class{name(){return"debug_list-probes"}description(){return"Lists tracepoints, logpoints, and/or watch expressions. Optional `types`: array of `tracepoint`, `logpoint`, `watch`. If omitted or empty, returns all."}inputSchema(){return{types:z49.array(z49.enum(LIST_TYPES2)).optional().describe("Which probe types to list. If omitted or empty, all are returned.")}}outputSchema(){return{tracepoints:z49.array(z49.any()).describe("Tracepoints"),logpoints:z49.array(z49.any()).describe("Logpoints"),watches:z49.array(z49.any()).describe("Watch expressions")}}async handle(context,args){let listTracepoints=!args.types?.length||args.types.includes("tracepoint"),listLogpoints=!args.types?.length||args.types.includes("logpoint"),listWatches=!args.types?.length||args.types.includes("watch"),probes=listProbes(context.storeKey),tracepoints=listTracepoints?probes.filter(p=>p.kind==="tracepoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled})):[],logpoints=listLogpoints?probes.filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression,condition:p.condition,hitCondition:p.hitCondition,hitCount:p.hitCount,resolvedLocations:p.resolvedLocations,enabled:p.enabled})):[],watches=listWatches?listWatchExpressions(context.storeKey):[];return{tracepoints,logpoints,watches}}},PROBE_TYPES2=["tracepoint","logpoint","watches"],ClearProbes2=class{name(){return"debug_clear-probes"}description(){return"Removes tracepoints, logpoints, and/or watch expressions. Optional `types`: array of `tracepoint`, `logpoint`, `watches`. If omitted or empty, clears all."}inputSchema(){return{types:z49.array(z49.enum(PROBE_TYPES2)).optional().describe("Which probe types to clear. If omitted or empty, all are cleared.")}}outputSchema(){return{tracepointsCleared:z49.number().describe("Number of tracepoints cleared"),logpointsCleared:z49.number().describe("Number of logpoints cleared"),watchesCleared:z49.number().describe("Number of watch expressions cleared"),message:z49.string().describe("Status message")}}async handle(context,args){let clearTracepoints=!args.types?.length||args.types.includes("tracepoint"),clearLogpoints=!args.types?.length||args.types.includes("logpoint"),clearWatches=!args.types?.length||args.types.includes("watches"),tracepointsCleared=0,logpointsCleared=0,watchesCleared=0;if(clearTracepoints||clearLogpoints){let probes=listProbes(context.storeKey);for(let p of probes)p.kind==="tracepoint"&&clearTracepoints?await removeProbe(context.storeKey,p.id)&&tracepointsCleared++:p.kind==="logpoint"&&clearLogpoints&&await removeProbe(context.storeKey,p.id)&&logpointsCleared++}clearWatches&&(watchesCleared=clearWatchExpressions(context.storeKey));let parts=[];tracepointsCleared&&parts.push(`${tracepointsCleared} tracepoint(s)`),logpointsCleared&&parts.push(`${logpointsCleared} logpoint(s)`),watchesCleared&&parts.push(`${watchesCleared} watch(es)`);let message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointsCleared,logpointsCleared,watchesCleared,message}}};import{z as z50}from"zod";var PutExceptionpoint2=class{name(){return"debug_put-exceptionpoint"}description(){return`
842
764
  Sets the exception tracepoint state for a Node.js process:
843
765
  - "none": Don't capture on exceptions
844
766
  - "uncaught": Capture only on uncaught exceptions
845
767
  - "all": Capture on all exceptions (caught and uncaught)
846
768
 
847
769
  When an exception occurs, a snapshot is captured with exception details.
848
- `}inputSchema(){return{state:z52.enum(["none","uncaught","all"]).describe("Exception tracepoint state")}}outputSchema(){return{state:z52.string().describe("New exception tracepoint state"),previousState:z52.string().describe("Previous state")}}async handle(context,args){let previousState=getExceptionBreakpoint(context.storeKey);return await setExceptionBreakpoint(context.storeKey,args.state),{state:args.state,previousState}}};import{z as z53}from"zod";var PutLogpoint2=class{name(){return"debug_put-logpoint"}description(){return`
770
+ `}inputSchema(){return{state:z50.enum(["none","uncaught","all"]).describe("Exception tracepoint state")}}outputSchema(){return{state:z50.string().describe("New exception tracepoint state"),previousState:z50.string().describe("Previous state")}}async handle(context,args){let previousState=getExceptionBreakpoint(context.storeKey);return await setExceptionBreakpoint(context.storeKey,args.state),{state:args.state,previousState}}};import{z as z51}from"zod";var PutLogpoint2=class{name(){return"debug_put-logpoint"}description(){return`
849
771
  Puts a logpoint on a Node.js backend process.
850
772
  When the logpoint is hit, the logExpression is evaluated and the result
851
773
  is captured in the snapshot's logResult field.
@@ -859,7 +781,7 @@ logExpression examples:
859
781
  - Simple value: "user.name"
860
782
  - Template: "\`User: \${user.name}, Age: \${user.age}\`"
861
783
  - Object: "{ user, timestamp: Date.now() }"
862
- `}inputSchema(){return{urlPattern:z53.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z53.number().int().positive().describe("Line number (1-based)"),columnNumber:z53.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),logExpression:z53.string().describe("Expression to evaluate and log when hit"),condition:z53.string().describe("Conditional expression - logpoint only hits if this evaluates to true").optional(),hitCondition:z53.string().describe("Hit count condition").optional()}}outputSchema(){return{id:z53.string().describe("Logpoint ID"),urlPattern:z53.string().describe("URL pattern"),lineNumber:z53.number().describe("Line number"),logExpression:z53.string().describe("Log expression"),resolvedLocations:z53.number().describe("Number of locations resolved")}}async handle(context,args){let probe=await createProbe(context.storeKey,{kind:"logpoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,columnNumber:args.columnNumber,logExpression:args.logExpression,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,logExpression:probe.logExpression||"",resolvedLocations:probe.resolvedLocations}}};import{z as z54}from"zod";var PutTracepoint2=class{name(){return"debug_put-tracepoint"}description(){return`
784
+ `}inputSchema(){return{urlPattern:z51.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z51.number().int().positive().describe("Line number (1-based)"),columnNumber:z51.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),logExpression:z51.string().describe("Expression to evaluate and log when hit"),condition:z51.string().describe("Conditional expression - logpoint only hits if this evaluates to true").optional(),hitCondition:z51.string().describe("Hit count condition").optional()}}outputSchema(){return{id:z51.string().describe("Logpoint ID"),urlPattern:z51.string().describe("URL pattern"),lineNumber:z51.number().describe("Line number"),logExpression:z51.string().describe("Log expression"),resolvedLocations:z51.number().describe("Number of locations resolved")}}async handle(context,args){let probe=await createProbe(context.storeKey,{kind:"logpoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,columnNumber:args.columnNumber,logExpression:args.logExpression,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,logExpression:probe.logExpression||"",resolvedLocations:probe.resolvedLocations}}};import{z as z52}from"zod";var PutTracepoint2=class{name(){return"debug_put-tracepoint"}description(){return`
863
785
  Puts a non-blocking tracepoint on a Node.js backend process.
864
786
  When hit, a snapshot of the call stack and local variables is captured
865
787
  automatically without pausing execution.
@@ -873,30 +795,26 @@ DO NOT escape characters yourself.
873
795
 
874
796
  Returns resolvedLocations: number of scripts where the tracepoint was set.
875
797
  If 0, the pattern didn't match any loaded scripts.
876
- `}inputSchema(){return{urlPattern:z54.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z54.number().int().positive().describe("Line number (1-based)"),columnNumber:z54.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),condition:z54.string().describe("Conditional expression - only triggers if this evaluates to true").optional(),hitCondition:z54.string().describe('Hit count condition, e.g., "== 5", ">= 10", "% 10 == 0"').optional()}}outputSchema(){return{id:z54.string().describe("Tracepoint ID"),urlPattern:z54.string().describe("URL pattern"),lineNumber:z54.number().describe("Line number"),columnNumber:z54.number().optional().describe("Column number"),condition:z54.string().optional().describe("Condition expression"),hitCondition:z54.string().optional().describe("Hit count condition"),resolvedLocations:z54.number().describe("Number of locations where tracepoint was resolved")}}async handle(context,args){let probe=await createProbe(context.storeKey,{kind:"tracepoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,columnNumber:args.columnNumber,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,columnNumber:probe.columnNumber,condition:probe.condition,hitCondition:probe.hitCondition,resolvedLocations:probe.resolvedLocations}}};import{z as z55}from"zod";var ResolveSourceLocation2=class{name(){return"debug_resolve-source-location"}description(){return`
798
+ `}inputSchema(){return{urlPattern:z52.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z52.number().int().positive().describe("Line number (1-based)"),columnNumber:z52.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),condition:z52.string().describe("Conditional expression - only triggers if this evaluates to true").optional(),hitCondition:z52.string().describe('Hit count condition, e.g., "== 5", ">= 10", "% 10 == 0"').optional()}}outputSchema(){return{id:z52.string().describe("Tracepoint ID"),urlPattern:z52.string().describe("URL pattern"),lineNumber:z52.number().describe("Line number"),columnNumber:z52.number().optional().describe("Column number"),condition:z52.string().optional().describe("Condition expression"),hitCondition:z52.string().optional().describe("Hit count condition"),resolvedLocations:z52.number().describe("Number of locations where tracepoint was resolved")}}async handle(context,args){let probe=await createProbe(context.storeKey,{kind:"tracepoint",urlPattern:args.urlPattern,lineNumber:args.lineNumber,columnNumber:args.columnNumber,condition:args.condition,hitCondition:args.hitCondition});return{id:probe.id,urlPattern:probe.urlPattern,lineNumber:probe.lineNumber,columnNumber:probe.columnNumber,condition:probe.condition,hitCondition:probe.hitCondition,resolvedLocations:probe.resolvedLocations}}};import{z as z53}from"zod";var ResolveSourceLocation2=class{name(){return"debug_resolve-source-location"}description(){return`
877
799
  Resolves a generated/bundled code location to its original source via source maps.
878
800
  Useful for translating minified stack traces or bundle line numbers to original TypeScript/JavaScript source.
879
801
 
880
802
  Requires an active Node.js debug connection (call debug_connect first).
881
803
  Input: generated script URL (e.g. file path), line, column (1-based).
882
804
  Output: original source path, line, column when a source map is available.
883
- `}inputSchema(){return{url:z55.string().describe("Generated script URL (e.g. file:///path/to/dist/app.js)"),line:z55.number().int().positive().describe("Line number in generated code (1-based)"),column:z55.number().int().nonnegative().describe("Column number in generated code (1-based, default 1)").optional()}}outputSchema(){return{resolved:z55.boolean().describe("Whether the location was resolved to original source"),source:z55.string().optional().describe("Original source file path"),line:z55.number().optional().describe("Line number in original source (1-based)"),column:z55.number().optional().describe("Column number in original source (1-based)"),name:z55.string().optional().describe("Original identifier name if available")}}async handle(context,args){let resolved=await resolveSourceLocation(context.storeKey,args.url,args.line,args.column??1);return resolved?{resolved:!0,source:resolved.source,line:resolved.line,column:resolved.column,name:resolved.name}:{resolved:!1}}};import{z as z56}from"zod";var Status2=class{name(){return"debug_status"}description(){return`
805
+ `}inputSchema(){return{url:z53.string().describe("Generated script URL (e.g. file:///path/to/dist/app.js)"),line:z53.number().int().positive().describe("Line number in generated code (1-based)"),column:z53.number().int().nonnegative().describe("Column number in generated code (1-based, default 1)").optional()}}outputSchema(){return{resolved:z53.boolean().describe("Whether the location was resolved to original source"),source:z53.string().optional().describe("Original source file path"),line:z53.number().optional().describe("Line number in original source (1-based)"),column:z53.number().optional().describe("Column number in original source (1-based)"),name:z53.string().optional().describe("Original identifier name if available")}}async handle(context,args){let resolved=await resolveSourceLocation(context.storeKey,args.url,args.line,args.column??1);return resolved?{resolved:!0,source:resolved.source,line:resolved.line,column:resolved.column,name:resolved.name}:{resolved:!1}}};import{z as z54}from"zod";var Status2=class{name(){return"debug_status"}description(){return`
884
806
  Returns the current Node.js debugging status including:
885
807
  - Whether connected and debugging is enabled
886
808
  - Source map status
887
809
  - Exceptionpoint state
888
810
  - Count of tracepoints, logpoints, watches
889
811
  - Snapshot statistics
890
- `}inputSchema(){return{}}outputSchema(){return{connected:z56.boolean().describe("Whether connected to a Node.js process"),enabled:z56.boolean().describe("Whether debugging is enabled"),pid:z56.number().optional().describe("Connected process PID"),hasSourceMaps:z56.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:z56.string().describe("Exceptionpoint state"),tracepointCount:z56.number().describe("Number of tracepoints"),logpointCount:z56.number().describe("Number of logpoints"),watchExpressionCount:z56.number().describe("Number of watch expressions"),snapshotStats:z56.any().nullable().describe("Snapshot statistics")}}async handle(context,_args){if(!context.isConnected)return{connected:!1,enabled:!1,hasSourceMaps:!1,exceptionBreakpoint:"none",tracepointCount:0,logpointCount:0,watchExpressionCount:0,snapshotStats:null};let key=context.storeKey,enabled=isDebuggingEnabled(key),probes=listProbes(key);return{connected:!0,enabled,pid:context.processInfo?.pid,hasSourceMaps:hasSourceMaps(key),exceptionBreakpoint:getExceptionBreakpoint(key),tracepointCount:probes.filter(p=>p.kind==="tracepoint").length,logpointCount:probes.filter(p=>p.kind==="logpoint").length,watchExpressionCount:listWatchExpressions(key).length,snapshotStats:enabled?getSnapshotStats(key):null}}};import{z as z57}from"zod";var AddWatch2=class{name(){return"debug_add-watch"}description(){return`
812
+ `}inputSchema(){return{}}outputSchema(){return{connected:z54.boolean().describe("Whether connected to a Node.js process"),enabled:z54.boolean().describe("Whether debugging is enabled"),pid:z54.number().optional().describe("Connected process PID"),hasSourceMaps:z54.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:z54.string().describe("Exceptionpoint state"),tracepointCount:z54.number().describe("Number of tracepoints"),logpointCount:z54.number().describe("Number of logpoints"),watchExpressionCount:z54.number().describe("Number of watch expressions"),snapshotStats:z54.any().nullable().describe("Snapshot statistics")}}async handle(context,_args){if(!context.isConnected)return{connected:!1,enabled:!1,hasSourceMaps:!1,exceptionBreakpoint:"none",tracepointCount:0,logpointCount:0,watchExpressionCount:0,snapshotStats:null};let key=context.storeKey,enabled=isDebuggingEnabled(key),probes=listProbes(key);return{connected:!0,enabled,pid:context.processInfo?.pid,hasSourceMaps:hasSourceMaps(key),exceptionBreakpoint:getExceptionBreakpoint(key),tracepointCount:probes.filter(p=>p.kind==="tracepoint").length,logpointCount:probes.filter(p=>p.kind==="logpoint").length,watchExpressionCount:listWatchExpressions(key).length,snapshotStats:enabled?getSnapshotStats(key):null}}};import{z as z55}from"zod";var AddWatch2=class{name(){return"debug_add-watch"}description(){return`
891
813
  Adds a watch expression to be evaluated at every tracepoint hit on the Node.js process.
892
814
  Watch expression results are included in the snapshot's watchResults field.
893
815
 
894
816
  Examples: "user.name", "this.state", "items.length", "JSON.stringify(config)"
895
- `}inputSchema(){return{expression:z57.string().describe("JavaScript expression to watch")}}outputSchema(){return{id:z57.string().describe("Watch expression ID"),expression:z57.string().describe("The expression")}}async handle(context,args){let watch=addWatchExpression(context.storeKey,args.expression);return{id:watch.id,expression:watch.expression}}};var tools13=[new Connect,new Disconnect,new Status2,new PutTracepoint2,new RemoveProbe2,new ListProbes2,new ClearProbes2,new PutLogpoint2,new PutExceptionpoint2,new GetProbeSnapshots2,new ClearProbeSnapshots2,new GetLogs,new ResolveSourceLocation2,new AddWatch2];import{z as z58}from"zod";var RunJsInNode=class{name(){return"run_js-in-node"}description(){return`
896
- Runs JavaScript in the connected Node process via CDP Runtime.evaluate. Has access to process, require, global, Node APIs.
897
- Use return to pass a value back (e.g. return 2+2; return process.memoryUsage();). Async supported: return await fetch(url).then(r=>r.json()).
898
- Requires debug_connect first. Blocks event loop until done; keep scripts short. Return value must be JSON-serializable.
899
- `.trim()}inputSchema(){return{script:z58.string().describe("JavaScript to run in Node. Use return for result; async/await supported."),timeoutMs:z58.number().int().min(100).max(3e4).optional().describe("Max eval time ms. Default 5000.")}}outputSchema(){return{result:z58.any().describe("The result of the evaluation. Can be primitives, arrays, or objects. Must be serializable (JSON-compatible).")}}async handle(context,args){return{result:await evaluateInNode(context.storeKey,args.script,args.timeoutMs??5e3)}}};var tools14=[new RunJsInNode];var tools15=[...tools13,...tools14];async function createToolSessionContext2(sessionIdProvider){let sessionId=sessionIdProvider();return debug(`Created session context for the session with id ${sessionId}`),new NodeToolSessionContext(sessionId)}function createToolExecutor2(){return new NodeToolExecutor}var NodeCliProvider=class{platform="node";cliName="node-devtools-cli";tools=tools15;sessionDescription="Manage Node.js debugging sessions";cliExamples=["node-devtools-cli interactive","node-devtools-cli connect --pid 12345"];bashCompletionOptions="";bashCompletionCommands="daemon session tools config completion interactive connect disconnect status tracepoint logpoint exceptionpoint watch";zshCompletionOptions="";zshCompletionCommands=[{name:"daemon",description:"Manage the daemon server"},{name:"session",description:"Manage Node.js debug sessions"},{name:"tools",description:"List and inspect available tools"},{name:"config",description:"Show current configuration"},{name:"completion",description:"Generate shell completion scripts"},{name:"interactive",description:"Start interactive REPL mode"},{name:"connect",description:"Connect to a Node.js process"},{name:"disconnect",description:"Disconnect from current process"},{name:"status",description:"Show connection status"},{name:"tracepoint",description:"Tracepoint commands"},{name:"logpoint",description:"Logpoint commands"},{name:"exceptionpoint",description:"Exceptionpoint commands"},{name:"watch",description:"Watch expression commands"}];replPrompt="node> ";cliDescription="Node.js DevTools MCP CLI";packageName="browser-devtools-mcp";buildEnv(_opts){return{...process.env}}addOptions(cmd){return cmd}getConfig(){return{consoleMessagesBufferSize:NODE_CONSOLE_MESSAGES_BUFFER_SIZE,nodeInspectorHost:NODE_INSPECTOR_HOST}}formatConfig(configValues){return[" Node.js:",` Console Messages Buffer: ${configValues.consoleMessagesBufferSize??NODE_CONSOLE_MESSAGES_BUFFER_SIZE}`,` Inspector Host (NODE_INSPECTOR_HOST): ${configValues.nodeInspectorHost??"(default 127.0.0.1)"}`].join(`
817
+ `}inputSchema(){return{expression:z55.string().describe("JavaScript expression to watch")}}outputSchema(){return{id:z55.string().describe("Watch expression ID"),expression:z55.string().describe("The expression")}}async handle(context,args){let watch=addWatchExpression(context.storeKey,args.expression);return{id:watch.id,expression:watch.expression}}};var tools12=[new Connect,new Disconnect,new Status2,new PutTracepoint2,new RemoveProbe2,new ListProbes2,new ClearProbes2,new PutLogpoint2,new PutExceptionpoint2,new GetProbeSnapshots2,new ClearProbeSnapshots2,new GetLogs,new ResolveSourceLocation2,new AddWatch2];var tools13=[...tools12];async function createToolSessionContext2(sessionIdProvider){let sessionId=sessionIdProvider();return debug(`Created session context for the session with id ${sessionId}`),new NodeToolSessionContext(sessionId)}function createToolExecutor2(){return new NodeToolExecutor}var NodeCliProvider=class{platform="node";cliName="node-devtools-cli";tools=tools13;sessionDescription="Manage Node.js debugging sessions";cliExamples=["node-devtools-cli interactive","node-devtools-cli connect --pid 12345"];bashCompletionOptions="";bashCompletionCommands="daemon session tools config completion interactive connect disconnect status tracepoint logpoint exceptionpoint watch";zshCompletionOptions="";zshCompletionCommands=[{name:"daemon",description:"Manage the daemon server"},{name:"session",description:"Manage Node.js debug sessions"},{name:"tools",description:"List and inspect available tools"},{name:"config",description:"Show current configuration"},{name:"completion",description:"Generate shell completion scripts"},{name:"interactive",description:"Start interactive REPL mode"},{name:"connect",description:"Connect to a Node.js process"},{name:"disconnect",description:"Disconnect from current process"},{name:"status",description:"Show connection status"},{name:"tracepoint",description:"Tracepoint commands"},{name:"logpoint",description:"Logpoint commands"},{name:"exceptionpoint",description:"Exceptionpoint commands"},{name:"watch",description:"Watch expression commands"}];replPrompt="node> ";cliDescription="Node.js DevTools MCP CLI";packageName="browser-devtools-mcp";buildEnv(_opts){return{...process.env}}addOptions(cmd){return cmd}getConfig(){return{consoleMessagesBufferSize:NODE_CONSOLE_MESSAGES_BUFFER_SIZE,nodeInspectorHost:NODE_INSPECTOR_HOST}}formatConfig(configValues){return[" Node.js:",` Console Messages Buffer: ${configValues.consoleMessagesBufferSize??NODE_CONSOLE_MESSAGES_BUFFER_SIZE}`,` Inspector Host (NODE_INSPECTOR_HOST): ${configValues.nodeInspectorHost??"(default 127.0.0.1)"}`].join(`
900
818
  `)}formatConfigForRepl(_opts){return[` console-messages-buffer = ${NODE_CONSOLE_MESSAGES_BUFFER_SIZE}`,` node-inspector-host = ${NODE_INSPECTOR_HOST??"127.0.0.1"}`].join(`
901
819
  `)}},cliProvider2=new NodeCliProvider;var SERVER_INSTRUCTIONS2=`
902
820
  Node.js Backend Debugging Platform
@@ -917,7 +835,6 @@ and provides the same debugging capabilities as browser debugging.
917
835
  - Exceptionpoints: Automatically capture snapshots when exceptions occur (uncaught or all)
918
836
  - Watch expressions: Evaluate custom expressions at every tracepoint hit
919
837
  - Source map support: Resolves bundled/compiled code to original source locations
920
- - run_js-in-node: Execute JavaScript in the connected process (e.g., process.memoryUsage(), require())
921
838
 
922
839
  **Key Features:**
923
840
  - Zero code changes required - connects to already-running processes
@@ -938,4 +855,4 @@ Node.js Debugging Best Practices:
938
855
  - Use source maps when debugging TypeScript or compiled code
939
856
  - Disconnect cleanly when done to avoid leaving inspector sessions open
940
857
  - Use exception breakpoints to catch unhandled errors
941
- `;var nodePlatformInfo={serverInfo:{instructions:SERVER_INSTRUCTIONS2,policies:[NODE_DEBUGGING_POLICY]},toolsInfo:{tools:tools15,createToolSessionContext:createToolSessionContext2,createToolExecutor:createToolExecutor2},cliInfo:{cliProvider:cliProvider2}};function _resolvePlatformInfo(){return PLATFORM==="node"?nodePlatformInfo:platformInfo}var platformInfo2=_resolvePlatformInfo();function isToolEnabled(tool){return tool.isEnabled?.()??!0}export{platformInfo2 as platformInfo,isToolEnabled};
858
+ `;var nodePlatformInfo={serverInfo:{instructions:NODE_SERVER_INSTRUCTIONS_ENABLE?SERVER_INSTRUCTIONS2:void 0,policies:NODE_POLICY_DEBUGGING_ENABLE?[NODE_DEBUGGING_POLICY]:[]},toolsInfo:{tools:tools13,createToolSessionContext:createToolSessionContext2,createToolExecutor:createToolExecutor2},cliInfo:{cliProvider:cliProvider2}};function _resolvePlatformInfo(){return PLATFORM==="node"?nodePlatformInfo:platformInfo}var platformInfo2=_resolvePlatformInfo();import{z as z56,ZodError}from"zod";function formatZodError(err){return"Validation failed: "+err.issues.map(issue=>(issue.path.length?issue.path.join(".")+": ":"")+issue.message).join("; ")}var ToolRegistry=class{tools=new Map;addTool(tool){if(this.tools.has(tool.name()))throw new Error(`Tool already registered: ${tool.name()}`);this.tools.set(tool.name(),{tool,inputSchema:z56.object(tool.inputSchema()).strict(),outputSchema:z56.object(tool.outputSchema()).strict()})}async runTool(context,toolName,toolArgs){let serverNameSeparatorIdx=toolName.indexOf(":");serverNameSeparatorIdx>=0&&(toolName=toolName.substring(serverNameSeparatorIdx+1));let toolDef=this.tools.get(toolName);if(!toolDef)throw new Error(`Tool not found: ${toolName}`);let parsedToolArgs;try{parsedToolArgs=await toolDef.inputSchema.parseAsync(toolArgs)}catch(e){throw e instanceof ZodError?new Error(formatZodError(e)):e}let rawToolResult=await toolDef.tool.handle(context,parsedToolArgs);return await toolDef.outputSchema.parseAsync(rawToolResult)}};function isToolEnabled(tool){return tool.isEnabled?.()??!0}export{platformInfo2 as platformInfo,ToolRegistry,isToolEnabled};