browser-devtools-mcp 0.5.3 → 0.6.1

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,13 +1,18 @@
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_CDP_CONNECT_URL,BROWSER_CDP_ENDPOINT_EXPLICIT,BROWSER_CDP_OPEN_INSPECT,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,TOOL_INPUT_METADATA_ENABLE,V8Api,addWatchExpression,clearWatchExpressions,createProbe,debug,denormalizeToolName,detachDebugging,enableDebugging,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-P5HBF7J2.js";var R_DISCOVERY_REMOTE="Check BROWSER_CDP_ENDPOINT_URL, host reachability, and firewall.",R_CONNECT_GENERIC="Allow the Chrome remote-debugging prompt if shown. If attach still fails, open chrome://inspect/#remote-debugging and confirm debugging is on.",CdpDiscoveryFailedError=class extends Error{constructor(detailLine,loopbackAttach){super(`[CDP discovery] ${detailLine}`);this.detailLine=detailLine;this.loopbackAttach=loopbackAttach;this.name="CdpDiscoveryFailedError"}};function cdpDiscoveryAttachHint(chromeRunning,openedInspectPage){return chromeRunning?`Chrome is running but remote debugging is not active on the probed port(s). ${openedInspectPage?"Opened chrome://inspect/#remote-debugging \u2014 enable remote debugging, then retry.":"Open chrome://inspect/#remote-debugging in Chrome, enable remote debugging, then retry."} Alternatively start Chrome with --remote-debugging-port=9222 or set BROWSER_CDP_ENDPOINT_URL.`:"Google Chrome does not appear to be running. Start Chrome, then enable remote debugging at chrome://inspect/#remote-debugging or launch with --remote-debugging-port=9222, then retry."}function cdpConnectAttachHint(chromeRunning,openedInspectPage){return chromeRunning?openedInspectPage?"If chrome://inspect/#remote-debugging opened in Chrome, enable remote debugging and retry. Approve any remote-debugging prompt.":"Open chrome://inspect/#remote-debugging, enable remote debugging, then retry. Or start Chrome with --remote-debugging-port=9222.":"Google Chrome does not appear to be running. Start Chrome, then enable remote debugging (chrome://inspect/#remote-debugging or --remote-debugging-port=9222), then retry."}function cdpDiscoveryError(detail){return new Error(`[CDP discovery] ${detail}
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_CDP_CONNECT_URL,BROWSER_CDP_ENDPOINT_EXPLICIT,BROWSER_CDP_OPEN_INSPECT,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,COLLECTOR_API_KEY,COLLECTOR_URL,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,SCENARIO_SEARCH_STRATEGY,SEARCH_STRATEGY,SourceMapResolver,TOOL_INPUT_METADATA_ENABLE,V8Api,WORKING_DIR,__esm,__export,__toCommonJS,addWatchExpression,clearWatchExpressions,createProbe,debug,denormalizeToolName,detachDebugging,enableDebugging,error,getConsoleMessages,getExceptionBreakpoint,getSnapshotStats,getSnapshots,getSnapshotsByProbe,getStoreKey,hasSourceMaps,init_logger,isDebuggingEnabled,listProbes,listWatchExpressions,removeProbe,removeWatchExpression,resolveSourceLocation,setExceptionBreakpoint,toJson,warn}from"./core-S5JHUB3Z.js";var fts5_strategy_exports={};__export(fts5_strategy_exports,{Fts5Strategy:()=>Fts5Strategy});import{createRequire as createRequire2}from"node:module";function _loadBetterSqlite3(){try{return require3("better-sqlite3")}catch{throw new Error("better-sqlite3 is not installed. Install it with: npm install better-sqlite3")}}var require3,Fts5Strategy,init_fts5_strategy=__esm({"src/search/strategies/fts5-strategy.ts"(){"use strict";require3=createRequire2(import.meta.url);Fts5Strategy=class{_db;_fieldNames=[];_initialized=!1;constructor(){let BetterSqlite3=_loadBetterSqlite3();this._db=new BetterSqlite3(":memory:")}_bm25Weights="";_ensureTable(fields){if(this._initialized)return;this._fieldNames=Object.keys(fields);let columns=this._fieldNames.join(", ");this._db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS documents USING fts5(id, ${columns}, tokenize='porter unicode61')`);let weights=[0,...this._fieldNames.map((_,i)=>i===0?5:1)];this._bm25Weights=weights.join(", "),this._initialized=!0}index(documents){if(documents.length===0)return;this._ensureTable(documents[0].fields);let columns=["id",...this._fieldNames].join(", "),placeholders=["id",...this._fieldNames].map(()=>"?").join(", "),stmt=this._db.prepare(`INSERT INTO documents (${columns}) VALUES (${placeholders})`);this._db.exec("BEGIN");try{for(let doc of documents){let values=[doc.id,...this._fieldNames.map(f=>doc.fields[f]??"")];stmt.run(...values)}this._db.exec("COMMIT")}catch(err){throw this._db.exec("ROLLBACK"),err}}add(document2){this._ensureTable(document2.fields);let columns=["id",...this._fieldNames].join(", "),placeholders=["id",...this._fieldNames].map(()=>"?").join(", "),values=[document2.id,...this._fieldNames.map(f=>document2.fields[f]??"")];this._db.prepare(`INSERT INTO documents (${columns}) VALUES (${placeholders})`).run(...values)}update(id,document2){this.remove(id),this.add(document2)}remove(id){this._initialized&&this._db.prepare("DELETE FROM documents WHERE id = ?").run(id)}search(query,limit){if(!this._initialized||!query.trim())return[];let FTS5_RESERVED=new Set(["AND","OR","NOT","NEAR"]),tokens=query.replace(/['"\[\]{}\*:^~()\-]/g," ").split(/\s+/).filter(t=>t.length>0&&!FTS5_RESERVED.has(t.toUpperCase()));if(tokens.length===0)return[];let ftsQuery=tokens.map(t=>`"${t}"`).join(" OR "),safeLimit=limit?Math.max(1,Math.floor(limit)):100;return this._db.prepare(`SELECT id, bm25(documents, ${this._bm25Weights}) AS rank FROM documents WHERE documents MATCH ? ORDER BY rank LIMIT ?`).all(ftsQuery,safeLimit).map(row=>({id:row.id,score:-row.rank}))}close(){this._db.close()}}}});var minisearch_strategy_exports={};__export(minisearch_strategy_exports,{MiniSearchStrategy:()=>MiniSearchStrategy});import MiniSearch from"minisearch";import{stemmer}from"stemmer";var MiniSearchStrategy,init_minisearch_strategy=__esm({"src/search/strategies/minisearch-strategy.ts"(){"use strict";init_logger();MiniSearchStrategy=class{_index;_fieldNames=[];constructor(){this._index=this._createIndex([])}_createIndex(fieldNames){return new MiniSearch({fields:fieldNames,storeFields:[],processTerm:term=>stemmer(term.toLowerCase()),searchOptions:{fuzzy:.2,prefix:!0}})}_ensureFields(fields){let names=Object.keys(fields);names.some(n=>!this._fieldNames.includes(n))&&(this._fieldNames.length===0?(this._fieldNames=names,this._index=this._createIndex(this._fieldNames)):debug(`minisearch: document has new fields [${names.join(", ")}] not in index [${this._fieldNames.join(", ")}] \u2014 extra fields ignored`))}index(documents){if(documents.length===0)return;this._ensureFields(documents[0].fields);let docs=documents.map(doc=>({id:doc.id,...doc.fields}));this._index.addAll(docs)}add(document2){this._ensureFields(document2.fields),this._index.add({id:document2.id,...document2.fields})}update(id,document2){try{this._index.discard(id)}catch{}this._index.add({id:document2.id,...document2.fields})}remove(id){try{this._index.discard(id)}catch{}}search(query,limit){let results=this._index.search(query);return(limit?results.slice(0,limit):results).map(r=>({id:r.id,score:r.score}))}close(){}}}});import{z}from"zod";function augmentToolInputSchema(shape){if(!TOOL_INPUT_METADATA_ENABLE)return shape;let extra={};return Object.prototype.hasOwnProperty.call(shape,"_metadata")||(extra._metadata=z.object({projectName:z.string().optional(),sessionId:z.string().optional(),verificationId:z.string().optional(),traceId:z.string().optional(),traceState:z.string().optional(),collectorUrl:z.string().optional(),collectorApiKey:z.string().optional()}).catchall(z.unknown()).optional()),Object.keys(extra).length===0?shape:{...shape,...extra}}import{z as z2,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:z2.object(augmentToolInputSchema(tool.inputSchema())).strict(),outputSchema:z2.object(tool.outputSchema()).strict()})}async runTool(context,toolName,toolArgs){let canonicalKey=denormalizeToolName(toolName),toolDef=this.tools.get(canonicalKey);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}init_logger();import fs from"node:fs";import os from"node:os";import path from"node:path";var DIR_NAME=".browser-devtools-mcp",FILE_NAME="scenarios.json";function _scenariosPath(scope){let baseDir=scope==="project"?WORKING_DIR:os.homedir();return path.join(baseDir,DIR_NAME,FILE_NAME)}function _isValidScenario(value){return typeof value=="object"&&value!==null&&typeof value.name=="string"&&typeof value.description=="string"&&typeof value.script=="string"}function _readFile(filePath){try{if(!fs.existsSync(filePath))return{};let content=fs.readFileSync(filePath,"utf-8"),parsed=JSON.parse(content);if(typeof parsed!="object"||parsed===null||Array.isArray(parsed))return debug(`scenario-store: invalid format in ${filePath}, expected object`),{};let result={};for(let[key,value]of Object.entries(parsed))_isValidScenario(value)?result[key]=value:debug(`scenario-store: skipping invalid scenario "${key}" in ${filePath}`);return result}catch(err){return debug(`scenario-store: failed to read ${filePath}: ${err}`),{}}}function _writeFile(filePath,scenarios){let dir=path.dirname(filePath);fs.existsSync(dir)||fs.mkdirSync(dir,{recursive:!0}),fs.writeFileSync(filePath,JSON.stringify(scenarios,null,2),"utf-8")}function _validateName(name){if(!name||name.length>200)throw new Error("Scenario name must be between 1 and 200 characters");if(/[/\\:*?"<>|.]/.test(name)||name.includes(".."))throw new Error('Scenario name contains invalid characters (no /, \\, :, *, ?, ", <, >, |, .)')}var _fileLocks=new Map;function _withFileLock(filePath,fn){let prev=_fileLocks.get(filePath)??Promise.resolve(),resolve,next=new Promise(r=>{resolve=r});_fileLocks.set(filePath,next);let result;try{result=fn()}finally{resolve()}return result}var ScenarioStore=class{_scope;_filePath;constructor(scope){this._scope=scope,this._filePath=_scenariosPath(scope)}get(name){return _readFile(this._filePath)[name]}add(scenario){_validateName(scenario.name),_withFileLock(this._filePath,()=>{let scenarios=_readFile(this._filePath);if(scenarios[scenario.name])throw new Error(`Scenario "${scenario.name}" already exists in ${this._scope} scope`);scenarios[scenario.name]=scenario,_writeFile(this._filePath,scenarios)})}update(name,updates){return _validateName(name),_withFileLock(this._filePath,()=>{let scenarios=_readFile(this._filePath),existing=scenarios[name];if(!existing)throw new Error(`Scenario "${name}" not found in ${this._scope} scope`);let updated={...existing,...updates,name};return scenarios[name]=updated,_writeFile(this._filePath,scenarios),updated})}delete(name){return _withFileLock(this._filePath,()=>{let scenarios=_readFile(this._filePath);return scenarios[name]?(delete scenarios[name],_writeFile(this._filePath,scenarios),!0):!1})}list(){let scenarios=_readFile(this._filePath);return Object.values(scenarios)}getAll(){return _readFile(this._filePath)}};function resolveScenario(name){let projectScenario=new ScenarioStore("project").get(name);return projectScenario||new ScenarioStore("global").get(name)}function listAllScenarios(){let globalStore=new ScenarioStore("global"),projectStore=new ScenarioStore("project"),merged={...globalStore.getAll(),...projectStore.getAll()};return Object.values(merged)}import{z as z3}from"zod";var MAX_RECURSION_DEPTH=5,ScenarioRun=class{_toolRegistry;constructor(toolRegistry){this._toolRegistry=toolRegistry}name(){return"scenario-run"}description(){return`
2
+ Runs a saved scenario by name. Looks up the scenario in project scope first, then global.
3
+ The scenario's JS script runs in the same sandbox as <execute>: callTool(), console, sleep are available.
4
+ Scenarios can compose other scenarios via callTool('scenario-run', { name: '...' }).
5
+ Max recursion depth: ${MAX_RECURSION_DEPTH}.
6
+ `.trim()}inputSchema(){return{name:z3.string().describe("Name of the scenario to run."),timeoutMs:z3.number().int().optional().describe("Wall-clock timeout in ms (default: 30000).")}}outputSchema(){return{scenarioName:z3.string(),toolOutputs:z3.array(z3.object({name:z3.string(),output:z3.any()})),logs:z3.array(z3.object({level:z3.string(),message:z3.string()})),result:z3.any().optional(),error:z3.string().optional(),failedTool:z3.object({name:z3.string(),error:z3.string()}).optional()}}async handle(context,args){if(context.scenarioDepth()>=MAX_RECURSION_DEPTH)return{scenarioName:args.name,toolOutputs:[],logs:[],error:`Max scenario recursion depth (${MAX_RECURSION_DEPTH}) exceeded.`};let scenario=resolveScenario(args.name);if(!scenario)return{scenarioName:args.name,toolOutputs:[],logs:[],error:`Scenario "${args.name}" not found in project or global scope.`};context.incrementScenarioDepth();try{let executeInput={code:scenario.script,timeoutMs:args.timeoutMs,...TOOL_INPUT_METADATA_ENABLE&&args._metadata?{_metadata:args._metadata}:{}},result=await this._toolRegistry.runTool(context,"execute",executeInput);return{scenarioName:args.name,toolOutputs:Array.isArray(result.toolOutputs)?result.toolOutputs:[],logs:Array.isArray(result.logs)?result.logs:[],result:result.result,error:typeof result.error=="string"?result.error:void 0,failedTool:result.failedTool&&typeof result.failedTool=="object"?result.failedTool:void 0}}catch(err){return{scenarioName:args.name,toolOutputs:[],logs:[],error:err instanceof Error?err.message:String(err)}}finally{context.decrementScenarioDepth()}}};var R_DISCOVERY_REMOTE="Check BROWSER_CDP_ENDPOINT_URL, host reachability, and firewall.",R_CONNECT_GENERIC="Allow the Chrome remote-debugging prompt if shown. If attach still fails, open chrome://inspect/#remote-debugging and confirm debugging is on.",CdpDiscoveryFailedError=class extends Error{constructor(detailLine,loopbackAttach){super(`[CDP discovery] ${detailLine}`);this.detailLine=detailLine;this.loopbackAttach=loopbackAttach;this.name="CdpDiscoveryFailedError"}};function cdpDiscoveryAttachHint(chromeRunning,openedInspectPage){return chromeRunning?`Chrome is running but remote debugging is not active on the probed port(s). ${openedInspectPage?"Opened chrome://inspect/#remote-debugging \u2014 enable remote debugging, then retry.":"Open chrome://inspect/#remote-debugging in Chrome, enable remote debugging, then retry."} Alternatively start Chrome with --remote-debugging-port=9222 or set BROWSER_CDP_ENDPOINT_URL.`:"Google Chrome does not appear to be running. Start Chrome, then enable remote debugging at chrome://inspect/#remote-debugging or launch with --remote-debugging-port=9222, then retry."}function cdpConnectAttachHint(chromeRunning,openedInspectPage){return chromeRunning?openedInspectPage?"If chrome://inspect/#remote-debugging opened in Chrome, enable remote debugging and retry. Approve any remote-debugging prompt.":"Open chrome://inspect/#remote-debugging, enable remote debugging, then retry. Or start Chrome with --remote-debugging-port=9222.":"Google Chrome does not appear to be running. Start Chrome, then enable remote debugging (chrome://inspect/#remote-debugging or --remote-debugging-port=9222), then retry."}function cdpDiscoveryError(detail){return new Error(`[CDP discovery] ${detail}
2
7
  \u2192 ${R_DISCOVERY_REMOTE}`)}function cdpConnectError(endpointSummary,cause,chromeRunning,openedInspectPage){let hint=chromeRunning===void 0?R_CONNECT_GENERIC:cdpConnectAttachHint(chromeRunning,openedInspectPage??!1);return new Error(`[CDP connect] ${cause}
3
8
  \u2192 endpoint: ${endpointSummary}
4
- \u2192 ${hint}`)}import net from"node:net";import{execSync}from"node:child_process";function hostPortFromCdpConnectRef(urlStr){let raw=typeof urlStr=="string"?urlStr.trim():"";if(!raw)throw cdpDiscoveryError("Connect URL is empty after endpoint resolution.");let withScheme=raw.startsWith("ws:")||raw.startsWith("wss:")?`http${raw.slice(raw.indexOf(":"))}`:raw.startsWith("http")?raw:`http://${raw}`,u;try{u=new URL(withScheme)}catch{throw cdpDiscoveryError(`Invalid URL "${raw.slice(0,120)}". Expected http://host:port or ws://host:port/...`)}let port=u.port?parseInt(u.port,10):u.protocol==="https:"?443:80;return{host:u.hostname,port}}function isLoopbackHost(host){let h=host.toLowerCase();return h==="localhost"||h==="127.0.0.1"||h==="::1"||h==="[::1]"}function isChromeRunning(){try{return process.platform==="win32"?execSync('tasklist /FI "IMAGENAME eq chrome.exe" /NH',{stdio:"pipe",encoding:"utf8"}).toLowerCase().includes("chrome.exe"):execSync("pgrep -x 'Google Chrome' || pgrep -x chrome || pgrep -x chromium || true",{stdio:"pipe",encoding:"utf8"}).trim().length>0}catch{return!1}}function openChromeRemoteDebuggingPage(){let url="chrome://inspect/#remote-debugging";try{if(process.platform==="darwin")execSync(`open -a "Google Chrome" "${url}"`,{stdio:"pipe",timeout:5e3});else if(process.platform==="win32")execSync(`start chrome "${url}"`,{stdio:"pipe",timeout:5e3});else try{execSync("google-chrome chrome://inspect/#remote-debugging",{stdio:"pipe",timeout:5e3})}catch{execSync("chromium chrome://inspect/#remote-debugging",{stdio:"pipe",timeout:5e3})}return!0}catch{return!1}}var DEFAULT_TIMEOUT_MS=5e3,CDP_PROBE_PORTS=[9222,9229];function _bracketIpv6(host){return host.includes(":")&&!host.startsWith("[")?`[${host}]`:host}function _rewriteWsHost(wsUrl,host,port){try{let u=new URL(wsUrl);return u.hostname=host.includes(":")?_bracketIpv6(host):host,u.port=String(port),u.toString()}catch{return wsUrl}}function _parseHttpCdpRoot(urlStr){try{let u=new URL(urlStr);if(u.protocol!=="http:"&&u.protocol!=="https:")return null;let port=u.port?parseInt(u.port,10):u.protocol==="https:"?443:80;return{host:u.hostname,port}}catch{return null}}async function _fetchText(url,timeoutMs){let ctrl=new AbortController,id=setTimeout(()=>{ctrl.abort()},timeoutMs);try{return await(await fetch(url,{signal:ctrl.signal})).text()}finally{clearTimeout(id)}}function _browserWsUrl(host,port){return`ws://${_bracketIpv6(host)}:${port}/devtools/browser`}function _tcpPortOpen(host,port,timeoutMs){return new Promise(resolve=>{let settled=!1,done=ok=>{settled||(settled=!0,clearTimeout(timer),resolve(ok))},sock=net.createConnection({host,port},()=>{sock.end(),done(!0)});sock.on("error",()=>{done(!1)});let timer=setTimeout(()=>{sock.destroy(),done(!1)},timeoutMs)})}async function _httpDiscoverWsUrl(host,port,timeoutMs){let b=_bracketIpv6(host);try{let body=await _fetchText(`http://${b}:${port}/json/version`,timeoutMs),info=JSON.parse(body);if(info.webSocketDebuggerUrl)return _rewriteWsHost(info.webSocketDebuggerUrl,host,port)}catch{}try{let body=await _fetchText(`http://${b}:${port}/json/list`,timeoutMs),targets=JSON.parse(body),ws=(targets.find(t=>t.type==="browser")??targets[0])?.webSocketDebuggerUrl;if(ws)return _rewriteWsHost(ws,host,port)}catch{}return null}async function _connectUrlForHostPort(host,port,timeoutMs=DEFAULT_TIMEOUT_MS){let fromHttp=await _httpDiscoverWsUrl(host,port,timeoutMs);return fromHttp||_browserWsUrl(host,port)}async function _tryLoopbackProbePorts(timeoutMs=DEFAULT_TIMEOUT_MS){let host="127.0.0.1";for(let port of CDP_PROBE_PORTS){let fromHttp=await _httpDiscoverWsUrl(host,port,timeoutMs);if(fromHttp)return{connectUrl:fromHttp,port}}for(let port of CDP_PROBE_PORTS)if(await _tcpPortOpen(host,port,timeoutMs))return{connectUrl:_browserWsUrl(host,port),port};return null}async function resolveCdpConnectEndpoint(options){let configuredHttpOrWsUrl=options.configuredHttpOrWsUrl;if(!options.explicitEndpointUrl){let found=await _tryLoopbackProbePorts();if(found)return{connectUrl:found.connectUrl,cacheKey:`cdp:127.0.0.1:${found.port}`};throw new CdpDiscoveryFailedError("No CDP on 127.0.0.1:9222 or :9229.",!0)}let url=configuredHttpOrWsUrl;if(url.startsWith("ws://")||url.startsWith("wss://"))return{connectUrl:url,cacheKey:url};let parsed=_parseHttpCdpRoot(url);if(!parsed)throw cdpDiscoveryError(`BROWSER_CDP_ENDPOINT_URL must be http(s)://host:port or ws(s)://\u2026, got: ${configuredHttpOrWsUrl}`);try{return{connectUrl:await _connectUrlForHostPort(parsed.host,parsed.port),cacheKey:url}}catch(e){let detail=e instanceof Error?e.message:String(e),line=`No DevTools at ${parsed.host}:${parsed.port}. ${detail}`;throw isLoopbackHost(parsed.host)?new CdpDiscoveryFailedError(line,!0):cdpDiscoveryError(line)}}import fs from"node:fs";import{chromium,firefox,webkit}from"playwright";var DEFAULT_BROWSER_TYPE="chromium",browsers=new Map,persistenceBrowserContexts=new Map,cdpByEndpoint=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}function _openInspectIfChromeRunningOnLoopback(endpointHint){if(!BROWSER_CDP_OPEN_INSPECT||!isChromeRunning())return!1;try{let{host}=hostPortFromCdpConnectRef(endpointHint);if(isLoopbackHost(host))return openChromeRemoteDebuggingPage()}catch{return openChromeRemoteDebuggingPage()}return!1}async function _createCdpBrowserContext(connectUrl){let browser=await chromium.connectOverCDP(connectUrl),contexts=browser.contexts();if(contexts.length===0){let opened=_openInspectIfChromeRunningOnLoopback(connectUrl),running=isChromeRunning();throw cdpConnectError(connectUrl,"Attached but browser reported zero contexts (close other debug clients or re-enable remote debugging).",running,opened)}return{browser,context:contexts[0]}}async function _getCdpBrowserContext(){let resolvedRef,resolvedCacheKey;try{let r=await resolveCdpConnectEndpoint({configuredHttpOrWsUrl:BROWSER_CDP_CONNECT_URL,explicitEndpointUrl:BROWSER_CDP_ENDPOINT_EXPLICIT});resolvedRef=r.connectUrl,resolvedCacheKey=r.cacheKey}catch(e){if(e instanceof CdpDiscoveryFailedError&&e.loopbackAttach){let running=isChromeRunning(),openedInspect=BROWSER_CDP_OPEN_INSPECT&&running&&openChromeRemoteDebuggingPage();throw new Error(`${e.message}
5
- \u2192 ${cdpDiscoveryAttachHint(running,openedInspect)}`)}throw e}let connectUrl=resolvedRef,entry=cdpByEndpoint.get(resolvedCacheKey);if(entry&&!entry.browser.isConnected()&&(cdpByEndpoint.delete(resolvedCacheKey),entry=void 0),!entry){try{entry=await _createCdpBrowserContext(connectUrl)}catch(e){if(e instanceof Error&&e.message.startsWith("[CDP "))throw e;let runningGen=isChromeRunning(),openedGen=_openInspectIfChromeRunningOnLoopback(connectUrl);throw cdpConnectError(connectUrl,e instanceof Error?e.message:String(e),runningGen,openedGen)}cdpByEndpoint.set(resolvedCacheKey,entry)}return{browserContext:entry.context,dontCloseBrowserContext:!0}}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}){if(BROWSER_CDP_CONNECT_URL){if(browserContextOptions.persistent)throw new Error("BROWSER_CDP_ENDPOINT_URL / BROWSER_CDP_ENABLE cannot be used with BROWSER_PERSISTENT_ENABLE.");if(browserContextOptions.browserOptions.browserType!=="chromium")throw new Error("CDP attach mode supports Chromium-based browsers only (Chrome/Edge).");return _getCdpBrowserContext()}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={};_consoleSeq=0;_httpSeq=0;browserContext;_page;get page(){return this._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 ensureSessionPageOpen(){if(this.closed)throw new Error("Session context is already closed");this._page.isClosed()&&(debug(`Session ${this._sessionId}: page closed; opening new tab as session page.`),this._refMap={},this._numOfInFlightRequests=0,this._lastNetworkActivityTimestamp=0,this._page=await this.browserContext.newPage(),this._attachPageListeners(this._page))}_attachPageListeners(page){let me=this;page.on("console",msg=>{me.consoleMessages.push(me._toConsoleMessage(msg,++me._consoleSeq)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),page.on("pageerror",err=>{me.consoleMessages.push(me._errorToConsoleMessage(err,++me._consoleSeq)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),page.on("request",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests++,me._lastNetworkActivityTimestamp=Date.now())}),page.on("requestfinished",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++me._httpSeq)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),page.on("requestfailed",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++me._httpSeq)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))})}async init(){if(this.closed)throw new Error("Session context is already closed");if(this.initialized)throw new Error("Session context is already initialized");this._attachPageListeners(this._page),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{this.options.dontCloseBrowserContext?(debug(`Closing session page only (CDP attach) for session ${this._sessionId} ...`),await this.page.close({runBeforeUnload:!1}).catch(()=>{})):(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{await context.ensureSessionPageOpen();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(`
9
+ \u2192 ${hint}`)}import net from"node:net";import{execSync}from"node:child_process";function hostPortFromCdpConnectRef(urlStr){let raw=typeof urlStr=="string"?urlStr.trim():"";if(!raw)throw cdpDiscoveryError("Connect URL is empty after endpoint resolution.");let withScheme=raw.startsWith("ws:")||raw.startsWith("wss:")?`http${raw.slice(raw.indexOf(":"))}`:raw.startsWith("http")?raw:`http://${raw}`,u;try{u=new URL(withScheme)}catch{throw cdpDiscoveryError(`Invalid URL "${raw.slice(0,120)}". Expected http://host:port or ws://host:port/...`)}let port=u.port?parseInt(u.port,10):u.protocol==="https:"?443:80;return{host:u.hostname,port}}function isLoopbackHost(host){let h=host.toLowerCase();return h==="localhost"||h==="127.0.0.1"||h==="::1"||h==="[::1]"}function isChromeRunning(){try{return process.platform==="win32"?execSync('tasklist /FI "IMAGENAME eq chrome.exe" /NH',{stdio:"pipe",encoding:"utf8"}).toLowerCase().includes("chrome.exe"):execSync("pgrep -x 'Google Chrome' || pgrep -x chrome || pgrep -x chromium || true",{stdio:"pipe",encoding:"utf8"}).trim().length>0}catch{return!1}}function openChromeRemoteDebuggingPage(){let url="chrome://inspect/#remote-debugging";try{if(process.platform==="darwin")execSync(`open -a "Google Chrome" "${url}"`,{stdio:"pipe",timeout:5e3});else if(process.platform==="win32")execSync(`start chrome "${url}"`,{stdio:"pipe",timeout:5e3});else try{execSync("google-chrome chrome://inspect/#remote-debugging",{stdio:"pipe",timeout:5e3})}catch{execSync("chromium chrome://inspect/#remote-debugging",{stdio:"pipe",timeout:5e3})}return!0}catch{return!1}}var DEFAULT_TIMEOUT_MS=5e3,CDP_PROBE_PORTS=[9222,9229];function _bracketIpv6(host){return host.includes(":")&&!host.startsWith("[")?`[${host}]`:host}function _rewriteWsHost(wsUrl,host,port){try{let u=new URL(wsUrl);return u.hostname=host.includes(":")?_bracketIpv6(host):host,u.port=String(port),u.toString()}catch{return wsUrl}}function _parseHttpCdpRoot(urlStr){try{let u=new URL(urlStr);if(u.protocol!=="http:"&&u.protocol!=="https:")return null;let port=u.port?parseInt(u.port,10):u.protocol==="https:"?443:80;return{host:u.hostname,port}}catch{return null}}async function _fetchText(url,timeoutMs){let ctrl=new AbortController,id=setTimeout(()=>{ctrl.abort()},timeoutMs);try{return await(await fetch(url,{signal:ctrl.signal})).text()}finally{clearTimeout(id)}}function _browserWsUrl(host,port){return`ws://${_bracketIpv6(host)}:${port}/devtools/browser`}function _tcpPortOpen(host,port,timeoutMs){return new Promise(resolve=>{let settled=!1,done=ok=>{settled||(settled=!0,clearTimeout(timer),resolve(ok))},sock=net.createConnection({host,port},()=>{sock.end(),done(!0)});sock.on("error",()=>{done(!1)});let timer=setTimeout(()=>{sock.destroy(),done(!1)},timeoutMs)})}async function _httpDiscoverWsUrl(host,port,timeoutMs){let b=_bracketIpv6(host);try{let body=await _fetchText(`http://${b}:${port}/json/version`,timeoutMs),info=JSON.parse(body);if(info.webSocketDebuggerUrl)return _rewriteWsHost(info.webSocketDebuggerUrl,host,port)}catch{}try{let body=await _fetchText(`http://${b}:${port}/json/list`,timeoutMs),targets=JSON.parse(body),ws=(targets.find(t=>t.type==="browser")??targets[0])?.webSocketDebuggerUrl;if(ws)return _rewriteWsHost(ws,host,port)}catch{}return null}async function _connectUrlForHostPort(host,port,timeoutMs=DEFAULT_TIMEOUT_MS){let fromHttp=await _httpDiscoverWsUrl(host,port,timeoutMs);return fromHttp||_browserWsUrl(host,port)}async function _tryLoopbackProbePorts(timeoutMs=DEFAULT_TIMEOUT_MS){let host="127.0.0.1";for(let port of CDP_PROBE_PORTS){let fromHttp=await _httpDiscoverWsUrl(host,port,timeoutMs);if(fromHttp)return{connectUrl:fromHttp,port}}for(let port of CDP_PROBE_PORTS)if(await _tcpPortOpen(host,port,timeoutMs))return{connectUrl:_browserWsUrl(host,port),port};return null}async function resolveCdpConnectEndpoint(options){let configuredHttpOrWsUrl=options.configuredHttpOrWsUrl;if(!options.explicitEndpointUrl){let found=await _tryLoopbackProbePorts();if(found)return{connectUrl:found.connectUrl,cacheKey:`cdp:127.0.0.1:${found.port}`};throw new CdpDiscoveryFailedError("No CDP on 127.0.0.1:9222 or :9229.",!0)}let url=configuredHttpOrWsUrl;if(url.startsWith("ws://")||url.startsWith("wss://"))return{connectUrl:url,cacheKey:url};let parsed=_parseHttpCdpRoot(url);if(!parsed)throw cdpDiscoveryError(`BROWSER_CDP_ENDPOINT_URL must be http(s)://host:port or ws(s)://\u2026, got: ${configuredHttpOrWsUrl}`);try{return{connectUrl:await _connectUrlForHostPort(parsed.host,parsed.port),cacheKey:url}}catch(e){let detail=e instanceof Error?e.message:String(e),line=`No DevTools at ${parsed.host}:${parsed.port}. ${detail}`;throw isLoopbackHost(parsed.host)?new CdpDiscoveryFailedError(line,!0):cdpDiscoveryError(line)}}import fs2 from"node:fs";import{chromium,firefox,webkit}from"playwright";var DEFAULT_BROWSER_TYPE="chromium",browsers=new Map,persistenceBrowserContexts=new Map,cdpByEndpoint=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;fs2.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}function _openInspectIfChromeRunningOnLoopback(endpointHint){if(!BROWSER_CDP_OPEN_INSPECT||!isChromeRunning())return!1;try{let{host}=hostPortFromCdpConnectRef(endpointHint);if(isLoopbackHost(host))return openChromeRemoteDebuggingPage()}catch{return openChromeRemoteDebuggingPage()}return!1}async function _createCdpBrowserContext(connectUrl){let browser=await chromium.connectOverCDP(connectUrl),contexts=browser.contexts();if(contexts.length===0){let opened=_openInspectIfChromeRunningOnLoopback(connectUrl),running=isChromeRunning();throw cdpConnectError(connectUrl,"Attached but browser reported zero contexts (close other debug clients or re-enable remote debugging).",running,opened)}return{browser,context:contexts[0]}}async function _getCdpBrowserContext(){let resolvedRef,resolvedCacheKey;try{let r=await resolveCdpConnectEndpoint({configuredHttpOrWsUrl:BROWSER_CDP_CONNECT_URL,explicitEndpointUrl:BROWSER_CDP_ENDPOINT_EXPLICIT});resolvedRef=r.connectUrl,resolvedCacheKey=r.cacheKey}catch(e){if(e instanceof CdpDiscoveryFailedError&&e.loopbackAttach){let running=isChromeRunning(),openedInspect=BROWSER_CDP_OPEN_INSPECT&&running&&openChromeRemoteDebuggingPage();throw new Error(`${e.message}
10
+ \u2192 ${cdpDiscoveryAttachHint(running,openedInspect)}`)}throw e}let connectUrl=resolvedRef,entry=cdpByEndpoint.get(resolvedCacheKey);if(entry&&!entry.browser.isConnected()&&(cdpByEndpoint.delete(resolvedCacheKey),entry=void 0),!entry){try{entry=await _createCdpBrowserContext(connectUrl)}catch(e){if(e instanceof Error&&e.message.startsWith("[CDP "))throw e;let runningGen=isChromeRunning(),openedGen=_openInspectIfChromeRunningOnLoopback(connectUrl);throw cdpConnectError(connectUrl,e instanceof Error?e.message:String(e),runningGen,openedGen)}cdpByEndpoint.set(resolvedCacheKey,entry)}return{browserContext:entry.context,dontCloseBrowserContext:!0}}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}){if(BROWSER_CDP_CONNECT_URL){if(browserContextOptions.persistent)throw new Error("BROWSER_CDP_ENDPOINT_URL / BROWSER_CDP_ENABLE cannot be used with BROWSER_PERSISTENT_ENABLE.");if(browserContextOptions.browserOptions.browserType!=="chromium")throw new Error("CDP attach mode supports Chromium-based browsers only (Chrome/Edge).");return _getCdpBrowserContext()}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}init_logger();init_logger();init_logger();init_logger();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/json",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 fs3 from"node:fs";import*as path2 from"node:path";import{fileURLToPath}from"node:url";var __filename=fileURLToPath(import.meta.url),__dirname=path2.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:path2.join(__dirname,"platform","browser","otel")}function _getOTELExporterConfig(){let type=OTEL_EXPORTER_TYPE,httpUrl=OTEL_EXPORTER_HTTP_URL;if(type==="console")return{type:"console"};if(type==="none"&&!httpUrl)return{type:"none"};if(type==="otlp/http"||type==="otlp/http-json"||type==="otlp/http-protobuf"||!!httpUrl){if(!httpUrl)throw new Error(`OTEL exporter HTTP url must be set when OTEL exporter type is "${type}"`);return{type:"otlp/http",traceFormat:type==="otlp/http-protobuf"?"protobuf":"json",url:OTEL_PROXY_LOCAL_PATH,upstreamURL:httpUrl,headers:OTEL_EXPORTER_HTTP_HEADERS}}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=path2.isAbsolute(assetDir)?assetDir:path2.join(process.cwd(),assetDir),filePath=path2.join(assetDirAbs,bundleFileName);if(!fs3.existsSync(filePath))throw new Error(`OTEL bundle not found at: ${filePath}`);return fs3.readFileSync(filePath,"utf-8")}async function _applyConfigToPage(page,cfg){await page.evaluate(nextCfg=>{let g=globalThis;g.__MCP_DEVTOOLS__||(g.__MCP_DEVTOOLS__={});let tid=nextCfg.traceId;typeof tid=="string"&&tid.trim()?g.__MCP_TRACE_ID__=tid.trim():delete g.__MCP_TRACE_ID__;let ts=nextCfg.traceState;typeof ts=="string"&&ts.trim()?g.__MCP_TRACE_STATE__=ts.trim():delete g.__MCP_TRACE_STATE__,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,this.config.traceState=options.traceState;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__={});let tid=cfg.traceId;typeof tid=="string"&&tid.trim()?g.__MCP_TRACE_ID__=tid.trim():delete g.__MCP_TRACE_ID__;let ts=cfg.traceState;typeof ts=="string"&&ts.trim()?g.__MCP_TRACE_STATE__=ts.trim():delete g.__MCP_TRACE_STATE__,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 getTraceState(page){return await page.evaluate(()=>{let g=globalThis;if(g.__mcpOtel&&typeof g.__mcpOtel.getTraceState=="function"){let ts=g.__mcpOtel.getTraceState();if(typeof ts=="string"&&ts.trim())return ts.trim()}let fallback=g.__MCP_TRACE_STATE__;if(typeof fallback=="string"&&fallback.trim())return fallback.trim()})}async setTraceId(page,traceId){let trimmed=traceId.trim();this.config.traceId=trimmed||void 0,await page.evaluate(tid=>{let g=globalThis;g.__mcpOtel&&typeof g.__mcpOtel.setTraceId=="function"?g.__mcpOtel.setTraceId(tid):tid.trim()?g.__MCP_TRACE_ID__=tid.trim():delete g.__MCP_TRACE_ID__},traceId)}async setTraceState(page,traceState){let trimmed=traceState.trim();this.config.traceState=trimmed||void 0,await page.evaluate(ts=>{let g=globalThis;g.__mcpOtel&&typeof g.__mcpOtel.setTraceState=="function"?g.__mcpOtel.setTraceState(ts):g.__MCP_TRACE_STATE__=ts.trim()||void 0},traceState)}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)}};import crypto from"node:crypto";function newTraceId(){return crypto.randomBytes(16).toString("hex")}function normalizeTraceId(traceId){let cleaned=traceId.trim().toLowerCase();if(!(/^[0-9a-f]{32}$/.test(cleaned)&&cleaned!=="0".repeat(32)))throw new Error("trace id must be 32 lowercase hex chars (not all zeros)");return cleaned}var MAX_TRACE_STATE_LEN=512,MAX_TRACE_STATE_ITEMS=32,VALID_KEY_CHAR_RANGE="[_0-9a-z-*/]",VALID_KEY=`[a-z]${VALID_KEY_CHAR_RANGE}{0,255}`,VALID_VENDOR_KEY=`[a-z0-9]${VALID_KEY_CHAR_RANGE}{0,240}@[a-z]${VALID_KEY_CHAR_RANGE}{0,13}`,VALID_KEY_REGEX=new RegExp(`^(?:${VALID_KEY}|${VALID_VENDOR_KEY})$`),VALID_VALUE_BASE_REGEX=/^[ -~]{0,255}[!-~]$/;function validateTracestateKey(key){return VALID_KEY_REGEX.test(key)}function validateTracestateValue(value){return VALID_VALUE_BASE_REGEX.test(value)&&!/[,=]/.test(value)}function assertValidTracestateForSet(raw){let t=raw.trim();if(!t)return;if(t.length>MAX_TRACE_STATE_LEN)throw new Error(`traceState exceeds maximum length (${MAX_TRACE_STATE_LEN} characters, W3C tracestate).`);let parts=t.split(",");if(parts.length>MAX_TRACE_STATE_ITEMS)throw new Error(`traceState has too many list members (max ${MAX_TRACE_STATE_ITEMS}, W3C tracestate).`);for(let i=0;i<parts.length;i++){let listMember=parts[i].trim();if(!listMember)throw new Error("traceState contains an empty list member (check commas / spacing).");let eq=listMember.indexOf("=");if(eq<=0||eq===listMember.length-1)throw new Error(`traceState list member must be "key=value": invalid entry at position ${i+1}.`);let key=listMember.slice(0,eq),value=listMember.slice(eq+1);if(!validateTracestateKey(key)||!validateTracestateValue(value))throw new Error(`traceState has invalid key or value at list position ${i+1} (W3C tracestate).`)}}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||{});init_logger();function getEnumKeyTuples(enumObj){let values=Object.keys(enumObj).filter(key=>isNaN(Number(key))).map(key=>enumObj[key]);if(values.length===0)throw new Error("Enum has no values");return values}function createEnumTransformer(enumObj,opts){let values=Object.keys(enumObj).filter(k=>isNaN(Number(k))).map(k=>enumObj[k]),caseInsensitive=opts?.caseInsensitive??!0,lookup=new Map(values.map(v=>[caseInsensitive?v.toLowerCase():v,v]));return value=>{let key=caseInsensitive?value.toLowerCase():value,found=lookup.get(key);if(found===void 0)throw new Error(`Invalid enum value: "${value}"`);return found}}function formattedTimeForFilename(date=new Date){let pad=n=>String(n).padStart(2,"0");return date.getFullYear()+pad(date.getMonth()+1)+pad(date.getDate())+"-"+pad(date.getHours())+pad(date.getMinutes())+pad(date.getSeconds())}import{createRequire}from"node:module";import path3 from"node:path";var require2=createRequire(import.meta.url),DEFAULT_QUALITY=90,DEFAULT_FALLBACK_SIZE=1280,DEFAULT_NAME="recording";function _resolveFfmpegPath(){let{registry}=require2("playwright-core/lib/server/registry/index"),ffmpegPath=registry.findExecutable("ffmpeg").executablePath();if(!ffmpegPath)throw new Error('ffmpeg not found. Run "npx playwright install ffmpeg" to install it.');return ffmpegPath}function _createVideoRecorder(ffmpegPath,options){let registryPath=require2.resolve("playwright-core/lib/server/registry/index"),videoRecorderPath=path3.resolve(registryPath,"../../videoRecorder.js"),{VideoRecorder}=require2(videoRecorderPath);return new VideoRecorder(ffmpegPath,options)}var ScreenRecorder=class{_cdpSession=null;_videoRecorder=null;_recording=!1;_filePath="";_pageCrashHandler=null;_page=null;isRecording(){return this._recording}async start(context,page,options){if(this._recording)throw new Error("Screen recorder is already recording");let quality=options.quality??DEFAULT_QUALITY,filename=`${options.name??DEFAULT_NAME}-${formattedTimeForFilename()}.webm`,outputFile=path3.join(options.outputDir,filename);this._cdpSession=await context.newCDPSession(page);let layoutMetrics=await this._cdpSession.send("Page.getLayoutMetrics"),rawWidth=layoutMetrics.cssVisualViewport?.clientWidth??layoutMetrics.visualViewport?.clientWidth??page.viewportSize()?.width??DEFAULT_FALLBACK_SIZE,rawHeight=layoutMetrics.cssVisualViewport?.clientHeight??layoutMetrics.visualViewport?.clientHeight??page.viewportSize()?.height??DEFAULT_FALLBACK_SIZE,width=Math.round(rawWidth/2)*2,height=Math.round(rawHeight/2)*2,ffmpegPath=_resolveFfmpegPath();this._videoRecorder=_createVideoRecorder(ffmpegPath,{width,height,outputFile}),this._cdpSession.on("Page.screencastFrame",async event=>{let buffer=Buffer.from(event.data,"base64"),timestamp=event.metadata.timestamp??Date.now()/1e3;this._videoRecorder?.writeFrame(buffer,timestamp);try{await this._cdpSession?.send("Page.screencastFrameAck",{sessionId:event.sessionId})}catch{}}),await this._cdpSession.send("Page.startScreencast",{format:"jpeg",quality,maxWidth:width,maxHeight:height}),this._filePath=outputFile,this._page=page,this._recording=!0,this._pageCrashHandler=()=>{this.stop().catch(()=>{})},page.on("crash",this._pageCrashHandler),debug(`screen-recorder: started recording ${width}x${height} \u2192 ${outputFile}`)}async stop(){if(!this._recording)return;this._recording=!1;let filePath=this._filePath;try{await this._cdpSession?.send("Page.stopScreencast")}catch{}try{await this._cdpSession?.detach()}catch{}try{await this._videoRecorder?.stop()}catch(err){debug(`screen-recorder: error stopping ffmpeg: ${err}`)}return this._page&&this._pageCrashHandler&&this._page.removeListener("crash",this._pageCrashHandler),this._cdpSession=null,this._videoRecorder=null,this._filePath="",this._page=null,this._pageCrashHandler=null,debug(`screen-recorder: stopped, saved to ${filePath}`),{filePath}}};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;traceState;_numOfInFlightRequests=0;_lastNetworkActivityTimestamp=0;_refMap={};_consoleSeq=0;_httpSeq=0;browserContext;_page;_screenRecorder=new ScreenRecorder;_ensurePagePromise=null;_scenarioDepth=0;get page(){return this._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 ensureSessionPageOpen(){if(this.closed)throw new Error("Session context is already closed");if(this._page.isClosed()){if(this._ensurePagePromise){await this._ensurePagePromise;return}this._ensurePagePromise=(async()=>{debug(`Session ${this._sessionId}: page closed; opening new tab as session page.`),this._screenRecorder.isRecording()&&await this._screenRecorder.stop(),this._refMap={},this._numOfInFlightRequests=0,this._lastNetworkActivityTimestamp=0,this._page=await this.browserContext.newPage(),this._attachPageListeners(this._page)})();try{await this._ensurePagePromise}finally{this._ensurePagePromise=null}}}_attachPageListeners(page){let me=this;page.on("console",msg=>{me.consoleMessages.push(me._toConsoleMessage(msg,++me._consoleSeq)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),page.on("pageerror",err=>{me.consoleMessages.push(me._errorToConsoleMessage(err,++me._consoleSeq)),me.consoleMessages.length>BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE&&me.consoleMessages.splice(0,me.consoleMessages.length-BROWSER_CONSOLE_MESSAGES_BUFFER_SIZE)}),page.on("request",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests++,me._lastNetworkActivityTimestamp=Date.now())}),page.on("requestfinished",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++me._httpSeq)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))}),page.on("requestfailed",async req=>{me.otelController.isOTELRequest(req)||(me._numOfInFlightRequests--,me._lastNetworkActivityTimestamp=Date.now(),me.httpRequests.push(await me._toHttpRequest(req,++me._httpSeq)),me.httpRequests.length>BROWSER_HTTP_REQUESTS_BUFFER_SIZE&&me.httpRequests.splice(0,me.httpRequests.length-BROWSER_HTTP_REQUESTS_BUFFER_SIZE))})}async init(){if(this.closed)throw new Error("Session context is already closed");if(this.initialized)throw new Error("Session context is already initialized");this._attachPageListeners(this._page),this.options.otelEnable&&(this.traceId=newTraceId(),await this.otelController.init({traceId:this.traceId,traceState:this.traceState})),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(error2,sequenceNumber){let timestamp=Date.now();return error2 instanceof Error?{type:"error",text:error2.message,level:{name:"error",code:3},timestamp,sequenceNumber}:{type:"error",text:String(error2),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 getTraceState(){return this.traceState}async getTraceContextFromBrowser(){if(!this.options.otelEnable)return{traceId:this.traceId,traceState:this.traceState};let traceId=await this.otelController.getTraceId(this.page),traceState=await this.otelController.getTraceState(this.page);return{traceId,traceState}}async setTraceId(traceId){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");let normalized=normalizeTraceId(traceId);this.traceId=normalized,await this.otelController.setTraceId(this.page,normalized)}async setTraceState(traceState){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");let trimmed=traceState.trim();trimmed&&assertValidTracestateForSet(trimmed),this.traceState=trimmed||void 0,await this.otelController.setTraceState(this.page,traceState)}async clearTraceState(){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");this.traceState=void 0,await this.otelController.setTraceState(this.page,"")}async resetTraceId(){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");let traceId=newTraceId();this.traceId=traceId,await this.otelController.setTraceId(this.page,traceId)}async clearTraceId(){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");this.traceId=void 0,await this.otelController.setTraceId(this.page,"")}async useTraceContextIfOTELEnabled(args){this.options.otelEnable&&(typeof args.traceId=="string"&&(args.traceId.trim()?normalizeTraceId(args.traceId)!==this.traceId&&await this.setTraceId(args.traceId):await this.clearTraceId()),typeof args.traceState=="string"&&(args.traceState.trim()?args.traceState.trim()!==this.traceState&&await this.setTraceState(args.traceState):await this.clearTraceState()))}getConsoleMessages(){return this.consoleMessages}getHttpRequests(){return this.httpRequests}getRefMap(){return this._refMap}setRefMap(refs){this._refMap=refs}isRecording(){return this._screenRecorder.isRecording()}async startRecording(options){await this._screenRecorder.start(this.browserContext,this._page,options)}async stopRecording(){return this._screenRecorder.stop()}scenarioDepth(){return this._scenarioDepth}incrementScenarioDepth(){this._scenarioDepth++}decrementScenarioDepth(){this._scenarioDepth--}executionContext(){return{page:this.page}}async close(){if(this.closed)return!1;this._screenRecorder.isRecording()&&await this._screenRecorder.stop(),debug(`Closing OTEL controller of the session with id ${this._sessionId} ...`),await this.otelController.close();try{this.options.dontCloseBrowserContext?(debug(`Closing session page only (CDP attach) for session ${this._sessionId} ...`),await this.page.close({runBeforeUnload:!1}).catch(()=>{})):(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}};init_logger();import{randomUUID}from"node:crypto";import{request as httpsRequest}from"node:https";import{request as httpRequest}from"node:http";var SEND_TIMEOUT_MS=3e3,EVENTS_PATH="/v1/events";async function sendToolCallToCollector(metadata,toolName,toolInput,result){if(!metadata?.sessionId||!metadata.projectName||!metadata.verificationId||!metadata.traceId)return;let collectorUrl=metadata.collectorUrl??COLLECTOR_URL,collectorApiKey=metadata.collectorApiKey??COLLECTOR_API_KEY;if(!collectorUrl||!collectorApiKey)return;let cleanToolInput=toolInput&&typeof toolInput=="object"&&!Array.isArray(toolInput)?{...toolInput,_metadata:void 0}:toolInput,event={id:randomUUID(),type:"tool_call",timestamp:Date.now(),session_id:metadata.sessionId,project_name:metadata.projectName,verification_id:metadata.verificationId,trace_id:metadata.traceId,tool_name:toolName,tool_input:cleanToolInput,tool_response:result.tool_response,duration:result.duration,...result.error?{error:result.error}:{},source:"browser-devtools-mcp"},body=JSON.stringify([event]);return new Promise(resolve=>{try{let url=new URL(EVENTS_PATH,collectorUrl),isHttps=url.protocol==="https:",doRequest=isHttps?httpsRequest:httpRequest,timeout=setTimeout(()=>{debug("collector: send timeout"),resolve()},SEND_TIMEOUT_MS),req=doRequest({hostname:url.hostname,port:url.port||(isHttps?443:80),path:url.pathname,method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(body),"X-API-Key":collectorApiKey},timeout:SEND_TIMEOUT_MS},res=>{res.on("data",()=>{}),res.on("end",()=>{clearTimeout(timeout),resolve()}),res.on("close",()=>{clearTimeout(timeout),resolve()})});req.on("error",err=>{clearTimeout(timeout),debug(`collector: send error: ${err.message}`),resolve()}),req.on("timeout",()=>{clearTimeout(timeout),req.destroy(),debug("collector: request timeout"),resolve()}),req.write(body),req.end()}catch(err){debug(`collector: sendToolCallToCollector failed: ${err}`),resolve()}})}init_logger();import{request as httpRequest2}from"node:http";import{request as httpsRequest2}from"node:https";var PRESIGN_TIMEOUT_MS=3e3,UPLOAD_TIMEOUT_MS=5e3;async function uploadArtifact(collectorUrl,apiKey,artifactType,data,contentType,sessionId){try{let presigned=await _getPresignedUrl(collectorUrl,apiKey,artifactType,contentType,sessionId);return!presigned||!await _putToS3(presigned.url,data,contentType)?void 0:{key:presigned.key}}catch(err){debug(`artifact-uploader: upload failed: ${err}`);return}}function _getPresignedUrl(collectorUrl,apiKey,artifactType,contentType,sessionId){return new Promise(resolve=>{try{let url=new URL(`/v1/artifacts/${artifactType}/upload-url`,collectorUrl);url.searchParams.set("contentType",contentType),url.searchParams.set("sessionId",sessionId);let isHttps=url.protocol==="https:",doRequest=isHttps?httpsRequest2:httpRequest2,req,timeout=setTimeout(()=>{debug("artifact-uploader: presign timeout"),req?.destroy(),resolve(void 0)},PRESIGN_TIMEOUT_MS);req=doRequest({hostname:url.hostname,port:url.port||(isHttps?443:80),path:url.pathname+url.search,method:"GET",headers:{"X-API-Key":apiKey},timeout:PRESIGN_TIMEOUT_MS},res=>{let chunks=[];res.on("data",chunk=>{chunks.push(chunk)}),res.on("end",()=>{if(clearTimeout(timeout),res.statusCode!==200){debug(`artifact-uploader: presign returned ${res.statusCode}`),resolve(void 0);return}try{let body=JSON.parse(Buffer.concat(chunks).toString("utf-8"));typeof body.url=="string"&&typeof body.key=="string"?resolve({url:body.url,key:body.key}):(debug("artifact-uploader: presign response missing url/key"),resolve(void 0))}catch{debug("artifact-uploader: presign response parse error"),resolve(void 0)}}),res.on("close",()=>{clearTimeout(timeout)})}),req.on("error",err=>{clearTimeout(timeout),debug(`artifact-uploader: presign error: ${err.message}`),resolve(void 0)}),req.on("timeout",()=>{clearTimeout(timeout),req.destroy(),debug("artifact-uploader: presign request timeout"),resolve(void 0)}),req.end()}catch(err){debug(`artifact-uploader: presign failed: ${err}`),resolve(void 0)}})}function _putToS3(presignedUrl,data,contentType){return new Promise(resolve=>{try{let url=new URL(presignedUrl),isHttps=url.protocol==="https:",doRequest=isHttps?httpsRequest2:httpRequest2,req,timeout=setTimeout(()=>{debug("artifact-uploader: S3 upload timeout"),req?.destroy(),resolve(!1)},UPLOAD_TIMEOUT_MS);req=doRequest({hostname:url.hostname,port:url.port||(isHttps?443:80),path:url.pathname+url.search,method:"PUT",headers:{"Content-Type":contentType,"Content-Length":data.length},timeout:UPLOAD_TIMEOUT_MS},res=>{res.on("data",()=>{}),res.on("end",()=>{clearTimeout(timeout),res.statusCode&&res.statusCode>=200&&res.statusCode<300?resolve(!0):(debug(`artifact-uploader: S3 upload returned ${res.statusCode}`),resolve(!1))}),res.on("close",()=>{clearTimeout(timeout)})}),req.on("error",err=>{clearTimeout(timeout),debug(`artifact-uploader: S3 upload error: ${err.message}`),resolve(!1)}),req.on("timeout",()=>{clearTimeout(timeout),req.destroy(),debug("artifact-uploader: S3 upload request timeout"),resolve(!1)}),req.write(data),req.end()}catch(err){debug(`artifact-uploader: S3 upload failed: ${err}`),resolve(!1)}})}init_logger();import fs4 from"node:fs/promises";var BrowserToolExecutor=class{async executeTool(context,tool,args){debug(`Executing tool ${tool.name()} with input: ${toJson(args)}`);let startTime=Date.now();try{args._metadata&&await context.useTraceContextIfOTELEnabled({traceId:typeof args._metadata.traceId=="string"?args._metadata.traceId:void 0,traceState:typeof args._metadata.traceState=="string"?args._metadata.traceState:void 0}),await context.ensureSessionPageOpen();let result=await tool.handle(context,args);return debug(`Executed tool ${tool.name()} and got output: ${toJson(result)}`),this._uploadArtifactsAndReport(args._metadata,tool.name(),args,result,Date.now()-startTime),result}catch(err){throw debug(`Error occurred while executing ${tool.name()}: ${err}`),sendToolCallToCollector(args._metadata,tool.name(),args,{duration:Date.now()-startTime,error:err instanceof Error?err.message:String(err)}),err}}async _uploadArtifactsAndReport(metadata,toolName,toolInput,result,duration){let collectorUrl=metadata?.collectorUrl??COLLECTOR_URL,collectorApiKey=metadata?.collectorApiKey??COLLECTOR_API_KEY,sessionId=metadata?.sessionId??"",canUpload=!!(collectorUrl&&collectorApiKey&&sessionId),uploadedArtifacts=[];if(canUpload){if(result._artifacts&&result._artifacts.length>0){let uploads=result._artifacts.map(async artifact=>{try{let data=await fs4.readFile(artifact.filePath),uploaded=await uploadArtifact(collectorUrl,collectorApiKey,artifact.type,data,artifact.mimeType,sessionId);uploaded&&uploadedArtifacts.push({type:artifact.type,key:uploaded.key})}catch(err){debug(`artifact-upload: failed to read/upload ${artifact.filePath}: ${err}`)}});await Promise.all(uploads)}let imageHolder=result;if(imageHolder.image?.data&&Buffer.isBuffer(imageHolder.image.data)&&uploadedArtifacts.length===0){let uploaded=await uploadArtifact(collectorUrl,collectorApiKey,"IMAGE",imageHolder.image.data,imageHolder.image.mimeType,sessionId);uploaded&&uploadedArtifacts.push({type:"IMAGE",key:uploaded.key})}}let collectorResponse={...result,_artifacts:void 0,image:void 0};uploadedArtifacts.length>0&&(collectorResponse.artifacts=uploadedArtifacts),sendToolCallToCollector(metadata,toolName,toolInput,{duration,tool_response:collectorResponse})}};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(`
6
11
  `),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(`
7
12
  `)}function processAriaTreeWithRefs(ariaTree,options={}){refCounter=0;let refs={};if(!ariaTree||!ariaTree.trim())return{tree:"(empty)",refs:{}};let lines=ariaTree.split(`
8
13
  `),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(`
9
14
  `):"(no interactive elements)",refs}}for(let line of lines){let processed=processLine(line,refs,options,tracker);processed!==null&&result.push(processed)}removeNthFromNonDuplicates(refs,tracker);let tree=result.join(`
10
- `);return options.compact&&(tree=compactTree(tree)),{tree,refs}}var NATIVE_INTERACTIVE_TAGS=new Set(["BUTTON","A","INPUT","SELECT","TEXTAREA"]),INTERACTIVE_ARIA_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]);async function findCursorInteractiveElements(page,rootSelector){let interactiveRolesList=[...INTERACTIVE_ARIA_ROLES],nativeTagsList=[...NATIVE_INTERACTIVE_TAGS],rootSel=rootSelector??null,result=await page.evaluate(args=>{let root=args.rootSel!=null?document.querySelector(args.rootSel):document.body;if(!root)return[];let interactiveRoles=new Set(args.interactiveRoles.map(r=>r.toLowerCase())),nativeTags=new Set(args.nativeTags.map(t=>t.toUpperCase())),candidates=[];function isCursorInteractive(el){let style=getComputedStyle(el);if(style.visibility==="hidden"||style.display==="none")return!1;let tag=el.tagName.toUpperCase();if(nativeTags.has(tag))return!1;let role=(el.getAttribute("role")||"").toLowerCase();if(role&&interactiveRoles.has(role))return!1;if(style.cursor==="pointer"||el.hasAttribute("onclick")||typeof el.onclick=="function")return!0;let tabindex=el.getAttribute("tabindex");return tabindex!=null&&parseInt(tabindex,10)>=0}function buildSelector(el){let tid=el.getAttribute("data-testid");if(tid&&tid.trim())try{return`[data-testid="${CSS.escape(tid)}"]`}catch{return`[data-testid="${tid.replace(/"/g,"\\22 ")}"]`}let id=el.id;if(id&&typeof document.querySelectorAll=="function")try{let escaped=CSS.escape(id);if(document.querySelectorAll(`#${escaped}`).length===1)return`#${escaped}`}catch{}let tag=el.tagName.toLowerCase(),cn=el.className&&typeof el.className=="string"?el.className.trim().split(/\s+/).filter(Boolean).slice(0,3).map(c=>"."+CSS.escape(c)).join(""):"";return tag+cn||tag}function getName(el){return(el.getAttribute("aria-label")||el.getAttribute("title")||el.textContent||"").trim().slice(0,80)||void 0}let all=root.querySelectorAll("*");for(let i=0;i<all.length;i++){let el=all[i];if(!isCursorInteractive(el))continue;let rect=el.getBoundingClientRect();if(rect.width===0&&rect.height===0)continue;let role=getComputedStyle(el).cursor==="pointer"||el.onclick!=null||el.hasAttribute("onclick")?"clickable":"focusable";candidates.push({selector:buildSelector(el),role,name:getName(el)})}let bySelector={};candidates.forEach(c=>{bySelector[c.selector]||(bySelector[c.selector]=[]),bySelector[c.selector].push(c)});let out=[];return candidates.forEach(c=>{let arr=bySelector[c.selector],idx=arr.indexOf(c);out.push({selector:c.selector,role:c.role,name:c.name,nth:arr.length>1?idx:void 0})}),out},{rootSel,interactiveRoles:interactiveRolesList,nativeTags:nativeTagsList});return Array.isArray(result)?result:[]}import{z}from"zod";async function takeAriaSnapshotWithRefs(context,options={}){let rawSnapshot=await context.page.locator(options.selector||"body").first().ariaSnapshot(),{tree,refs}=processAriaTreeWithRefs(rawSnapshot,{interactiveOnly:options.interactiveOnly,maxDepth:options.maxDepth,compact:options.compact}),mergedTree=tree,mergedRefs={...refs};if(options.cursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,options.selector||null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>{let n=parseInt(k.replace(/^e/,""),10)||0;return n>m?n:m},0),lines=[];cursorEntries.forEach((entry,i)=>{let ref=`e${maxNum+1+i}`;mergedRefs[ref]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth};let line=`- ${entry.role}`;entry.name&&(line+=` "${entry.name}"`),line+=` [ref=${ref}]`,entry.nth!==void 0&&entry.nth>0&&(line+=` [nth=${entry.nth}]`),lines.push(line)}),mergedTree=mergedTree+(mergedTree?`
15
+ `);return options.compact&&(tree=compactTree(tree)),{tree,refs}}var NATIVE_INTERACTIVE_TAGS=new Set(["BUTTON","A","INPUT","SELECT","TEXTAREA"]),INTERACTIVE_ARIA_ROLES=new Set(["button","link","textbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","searchbox","slider","spinbutton","switch","tab","treeitem"]);async function findCursorInteractiveElements(page,rootSelector){let interactiveRolesList=[...INTERACTIVE_ARIA_ROLES],nativeTagsList=[...NATIVE_INTERACTIVE_TAGS],rootSel=rootSelector??null,result=await page.evaluate(args=>{let root=args.rootSel!=null?document.querySelector(args.rootSel):document.body;if(!root)return[];let interactiveRoles=new Set(args.interactiveRoles.map(r=>r.toLowerCase())),nativeTags=new Set(args.nativeTags.map(t=>t.toUpperCase())),candidates=[];function isCursorInteractive(el){let style=getComputedStyle(el);if(style.visibility==="hidden"||style.display==="none")return!1;let tag=el.tagName.toUpperCase();if(nativeTags.has(tag))return!1;let role=(el.getAttribute("role")||"").toLowerCase();if(role&&interactiveRoles.has(role))return!1;if(style.cursor==="pointer"||el.hasAttribute("onclick")||typeof el.onclick=="function")return!0;let tabindex=el.getAttribute("tabindex");return tabindex!=null&&parseInt(tabindex,10)>=0}function buildSelector(el){let tid=el.getAttribute("data-testid");if(tid&&tid.trim())try{return`[data-testid="${CSS.escape(tid)}"]`}catch{return`[data-testid="${tid.replace(/"/g,"\\22 ")}"]`}let id=el.id;if(id&&typeof document.querySelectorAll=="function")try{let escaped=CSS.escape(id);if(document.querySelectorAll(`#${escaped}`).length===1)return`#${escaped}`}catch{}let tag=el.tagName.toLowerCase(),cn=el.className&&typeof el.className=="string"?el.className.trim().split(/\s+/).filter(Boolean).slice(0,3).map(c=>"."+CSS.escape(c)).join(""):"";return tag+cn||tag}function getName(el){return(el.getAttribute("aria-label")||el.getAttribute("title")||el.textContent||"").trim().slice(0,80)||void 0}let all=root.querySelectorAll("*");for(let i=0;i<all.length;i++){let el=all[i];if(!isCursorInteractive(el))continue;let rect=el.getBoundingClientRect();if(rect.width===0&&rect.height===0)continue;let role=getComputedStyle(el).cursor==="pointer"||el.onclick!=null||el.hasAttribute("onclick")?"clickable":"focusable";candidates.push({selector:buildSelector(el),role,name:getName(el)})}let bySelector={};candidates.forEach(c=>{bySelector[c.selector]||(bySelector[c.selector]=[]),bySelector[c.selector].push(c)});let out=[];return candidates.forEach(c=>{let arr=bySelector[c.selector],idx=arr.indexOf(c);out.push({selector:c.selector,role:c.role,name:c.name,nth:arr.length>1?idx:void 0})}),out},{rootSel,interactiveRoles:interactiveRolesList,nativeTags:nativeTagsList});return Array.isArray(result)?result:[]}import{z as z4}from"zod";async function takeAriaSnapshotWithRefs(context,options={}){let rawSnapshot=await context.page.locator(options.selector||"body").first().ariaSnapshot(),{tree,refs}=processAriaTreeWithRefs(rawSnapshot,{interactiveOnly:options.interactiveOnly,maxDepth:options.maxDepth,compact:options.compact}),mergedTree=tree,mergedRefs={...refs};if(options.cursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,options.selector||null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>{let n=parseInt(k.replace(/^e/,""),10)||0;return n>m?n:m},0),lines=[];cursorEntries.forEach((entry,i)=>{let ref=`e${maxNum+1+i}`;mergedRefs[ref]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth};let line=`- ${entry.role}`;entry.name&&(line+=` "${entry.name}"`),line+=` [ref=${ref}]`,entry.nth!==void 0&&entry.nth>0&&(line+=` [nth=${entry.nth}]`),lines.push(line)}),mergedTree=mergedTree+(mergedTree?`
11
16
  `:"")+`# Cursor-interactive elements
12
17
  `+lines.join(`
13
18
  `)}}return context.setRefMap(mergedRefs),{output:`
@@ -17,19 +22,19 @@ import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_
17
22
  \`\`\`yaml
18
23
  ${mergedTree}
19
24
  \`\`\`
20
- `.trim(),refs:mergedRefs}}var refMapEntrySchema=z.object({role:z.string(),name:z.string().optional(),selector:z.string(),nth:z.number().optional()}),TakeAriaSnapshot=class{name(){return"a11y_take-aria-snapshot"}description(){return`
25
+ `.trim(),refs:mergedRefs}}var refMapEntrySchema=z4.object({role:z4.string(),name:z4.string().optional(),selector:z4.string(),nth:z4.number().optional()}),TakeAriaSnapshot=class{name(){return"a11y_take-aria-snapshot"}description(){return`
21
26
  ARIA snapshot of the page or a scoped element. Returns a tree with refs (e1, e2, ...) and a refs map.
22
27
  Use refs in interaction tools: selector "e1" or "@e1" to click/fill that element. Output includes URL, title, and YAML tree.
23
28
  Refs are valid until next snapshot or navigation. interactiveOnly: only interactive elements get refs; omit for content roles (headings, etc.) too.
24
29
  cursorInteractive: true adds refs for clickable elements without ARIA (e.g. div with cursor:pointer/onclick).
25
30
  Use with <a11y_take-ax-tree-snapshot> for full UI analysis.
26
- `.trim()}inputSchema(){return{selector:z.string().optional().describe("Scope to this element; omit for full page."),interactiveOnly:z.boolean().optional().describe("Only interactive elements get refs."),maxDepth:z.number().int().min(0).optional().describe("Max tree depth; 0 = root only."),compact:z.boolean().optional().describe("Omit structural nodes without content."),cursorInteractive:z.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements.")}}outputSchema(){return{output:z.string().describe("Includes the page URL, title, and a YAML-formatted accessibility tree with [ref=e1] etc."),refs:z.record(z.string(),refMapEntrySchema).describe('Map of ref id to { role, name?, selector, nth? }. Use selector "e1" or "@e1" in interaction tools.')}}async handle(context,args){return takeAriaSnapshotWithRefs(context,{selector:args.selector,interactiveOnly:args.interactiveOnly,maxDepth:args.maxDepth,compact:args.compact,cursorInteractive:args.cursorInteractive})}};import{z as z2}from"zod";var DEFAULT_INCLUDE_STYLES=!0,DEFAULT_INCLUDE_RUNTIME_VISUAL=!0,DEFAULT_CHECK_OCCLUSION=!1,DEFAULT_ONLY_VISIBLE=!1,DEFAULT_ONLY_IN_VIEWPORT=!1,DEFAULT_TEXT_PREVIEW_MAX_LENGTH=80,DEFAULT_STYLE_PROPERTIES=["display","visibility","opacity","pointer-events","position","z-index","color","background-color","border-color","border-width","border-style","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-decoration-line","white-space","overflow","overflow-x","overflow-y","transform","cursor"],DEFAULT_INTERESTING_ROLES=new Set(["button","link","textbox","textField","checkbox","radio","combobox","switch","tab","tabList","tabPanel","menuitem","menu","menubar","dialog","alertDialog","heading","staticText","text","listbox","listitem","list","option","image","img","search","searchBox","navigation","main","banner","contentInfo","complementary","region","article","form","group","progressIndicator","slider","spinButton","status","alert","log","row","columnHeader","rowHeader","cell","grid","table","tooltip","figure","figcaption"]),INTERNAL_CONCURRENCY=12,INTERNAL_SAFETY_CAP=2e3;function attrsToObj(attrs){let result={};if(!attrs)return result;for(let i=0;i<attrs.length;i+=2){let key=String(attrs[i]),value=String(attrs[i+1]??"");result[key]=value}return result}var TakeAxTreeSnapshot=class{name(){return"a11y_take-ax-tree-snapshot"}description(){return`
31
+ `.trim()}inputSchema(){return{selector:z4.string().optional().describe("Scope to this element; omit for full page."),interactiveOnly:z4.boolean().optional().describe("Only interactive elements get refs."),maxDepth:z4.number().int().min(0).optional().describe("Max tree depth; 0 = root only."),compact:z4.boolean().optional().describe("Omit structural nodes without content."),cursorInteractive:z4.boolean().optional().default(!1).describe("Include cursor:pointer / onclick elements.")}}outputSchema(){return{output:z4.string().describe("Includes the page URL, title, and a YAML-formatted accessibility tree with [ref=e1] etc."),refs:z4.record(z4.string(),refMapEntrySchema).describe('Map of ref id to { role, name?, selector, nth? }. Use selector "e1" or "@e1" in interaction tools.')}}async handle(context,args){return takeAriaSnapshotWithRefs(context,{selector:args.selector,interactiveOnly:args.interactiveOnly,maxDepth:args.maxDepth,compact:args.compact,cursorInteractive:args.cursorInteractive})}};import{z as z5}from"zod";var DEFAULT_INCLUDE_STYLES=!0,DEFAULT_INCLUDE_RUNTIME_VISUAL=!0,DEFAULT_CHECK_OCCLUSION=!1,DEFAULT_ONLY_VISIBLE=!1,DEFAULT_ONLY_IN_VIEWPORT=!1,DEFAULT_TEXT_PREVIEW_MAX_LENGTH=80,DEFAULT_STYLE_PROPERTIES=["display","visibility","opacity","pointer-events","position","z-index","color","background-color","border-color","border-width","border-style","font-family","font-size","font-weight","line-height","letter-spacing","text-align","text-decoration-line","white-space","overflow","overflow-x","overflow-y","transform","cursor"],DEFAULT_INTERESTING_ROLES=new Set(["button","link","textbox","textField","checkbox","radio","combobox","switch","tab","tabList","tabPanel","menuitem","menu","menubar","dialog","alertDialog","heading","staticText","text","listbox","listitem","list","option","image","img","search","searchBox","navigation","main","banner","contentInfo","complementary","region","article","form","group","progressIndicator","slider","spinButton","status","alert","log","row","columnHeader","rowHeader","cell","grid","table","tooltip","figure","figcaption"]),INTERNAL_CONCURRENCY=12,INTERNAL_SAFETY_CAP=2e3;function attrsToObj(attrs){let result={};if(!attrs)return result;for(let i=0;i<attrs.length;i+=2){let key=String(attrs[i]),value=String(attrs[i+1]??"");result[key]=value}return result}var TakeAxTreeSnapshot=class{name(){return"a11y_take-ax-tree-snapshot"}description(){return`
27
32
  Combines Chromium AX tree with runtime visual diagnostics (bounding box, visibility, viewport).
28
33
  Use to detect: elements with role/name but hidden or off-screen; layout/geometry issues; overlap/occlusion (enable checkOcclusion).
29
34
  When investigating UI/layout or when clicks fail on seemingly visible elements, set checkOcclusion:true\u2014it uses elementFromPoint()
30
35
  at center+corners to find what is actually on top. boundingBox is from getBoundingClientRect() (viewport coords; layout box only).
31
36
  selectorHint is best-effort (data-testid/data-selector/id). Use with <a11y_take-aria-snapshot> for full UI analysis.
32
- `.trim()}inputSchema(){return{roles:z2.array(z2.string().min(1)).optional().describe('ARIA/AX role names to include; omit for default set. Standard roles: ARIA https://w3c.github.io/aria/#role_definitions, Chromium AX https://chromium.googlesource.com/chromium/src/+/main/ui/accessibility/ax_enum_util.cc (e.g. button, heading, staticText, image, main, navigation). Matching is case-insensitive; "text" also matches staticText, "img" matches image.'),includeStyles:z2.boolean().optional().default(DEFAULT_INCLUDE_STYLES),includeRuntimeVisual:z2.boolean().optional().default(DEFAULT_INCLUDE_RUNTIME_VISUAL).describe("Include bounding box and visibility."),checkOcclusion:z2.boolean().optional().default(DEFAULT_CHECK_OCCLUSION).describe("Use elementFromPoint to detect what is on top."),onlyVisible:z2.boolean().optional().default(DEFAULT_ONLY_VISIBLE),onlyInViewport:z2.boolean().optional().default(DEFAULT_ONLY_IN_VIEWPORT),textPreviewMaxLength:z2.number().int().positive().optional().default(DEFAULT_TEXT_PREVIEW_MAX_LENGTH),styleProperties:z2.array(z2.string()).optional().default([...DEFAULT_STYLE_PROPERTIES]).describe("CSS property names when includeStyles.")}}outputSchema(){return{url:z2.string().describe("The current page URL at the time the AX snapshot was captured."),title:z2.string().describe("The document title of the page at the time of the snapshot."),axNodeCount:z2.number().int().nonnegative().describe("Total number of nodes returned by Chromium Accessibility.getFullAXTree before filtering."),candidateCount:z2.number().int().nonnegative().describe("Number of DOM-backed AX nodes that passed role filtering before enrichment."),enrichedCount:z2.number().int().nonnegative().describe("Number of nodes included in the final enriched snapshot output."),truncatedBySafetyCap:z2.boolean().describe("Indicates whether the result set was truncated by an internal safety cap to prevent excessive output size."),nodes:z2.array(z2.object({axNodeId:z2.string().describe("Unique identifier of the accessibility node within the AX tree."),parentAxNodeId:z2.string().nullable().describe("Parent AX node id in the full AX tree. Null if this node is a root."),childAxNodeIds:z2.array(z2.string()).describe("Child AX node ids in the full AX tree (may include nodes not present in the filtered output)."),role:z2.string().nullable().describe("ARIA role of the accessibility node (e.g. button, link, textbox)."),name:z2.string().nullable().describe("Accessible name computed by the browser accessibility engine."),ignored:z2.boolean().nullable().describe("Whether the accessibility node is marked as ignored."),backendDOMNodeId:z2.number().int().describe("Chromium backend DOM node identifier used to map AX nodes to DOM elements."),domNodeId:z2.number().int().nullable().describe("Resolved DOM nodeId from CDP if available; may be null because nodeId is not guaranteed to be stable/resolved."),frameId:z2.string().nullable().describe("Frame identifier if the node belongs to an iframe or subframe."),localName:z2.string().nullable().describe("Lowercased DOM tag name of the mapped element (e.g. div, button, input)."),id:z2.string().nullable().describe("DOM id attribute of the mapped element."),className:z2.string().nullable().describe("DOM class attribute of the mapped element."),selectorHint:z2.string().nullable().describe("Best-effort selector hint for targeting this element (prefers data-testid/data-selector/id)."),textPreview:z2.string().nullable().describe("Short preview of rendered text content or aria-label, truncated to the configured maximum length."),value:z2.any().nullable().describe("Raw AX value payload associated with the node, if present."),description:z2.any().nullable().describe("Raw AX description payload associated with the node, if present."),properties:z2.array(z2.any()).nullable().describe("Additional AX properties exposed by the accessibility tree."),styles:z2.record(z2.string(),z2.string()).optional().describe("Subset of computed CSS styles for the element as string key-value pairs."),runtime:z2.object({boundingBox:z2.object({x:z2.number().describe("X coordinate of the element relative to the viewport."),y:z2.number().describe("Y coordinate of the element relative to the viewport."),width:z2.number().describe("Width of the element in CSS pixels."),height:z2.number().describe("Height of the element in CSS pixels.")}).nullable().describe("Bounding box computed at runtime using getBoundingClientRect."),isVisible:z2.boolean().describe("Whether the element is considered visually visible (display, visibility, opacity, and size)."),isInViewport:z2.boolean().describe("Whether the element intersects the current viewport."),occlusion:z2.object({samplePoints:z2.array(z2.object({x:z2.number().describe("Sample point X (viewport coordinates) used for occlusion testing."),y:z2.number().describe("Sample point Y (viewport coordinates) used for occlusion testing."),hit:z2.boolean().describe("True if elementFromPoint at this point returned a different element that is not a descendant.")})).describe("Sample points used for occlusion detection (center + corners)."),isOccluded:z2.boolean().describe("True if at least one sample point is covered by another element."),topElement:z2.object({localName:z2.string().nullable().describe("Tag name of the occluding element."),id:z2.string().nullable().describe("DOM id of the occluding element (may be null if none)."),className:z2.string().nullable().describe("DOM class of the occluding element (may be null if none)."),selectorHint:z2.string().nullable().describe("Best-effort selector hint for the occluding element."),boundingBox:z2.object({x:z2.number().describe("X coordinate of the occluding element bounding box."),y:z2.number().describe("Y coordinate of the occluding element bounding box."),width:z2.number().describe("Width of the occluding element bounding box."),height:z2.number().describe("Height of the occluding element bounding box.")}).nullable().describe("Bounding box of the occluding element (if available).")}).nullable().describe("Identity and geometry of the occluding element. Null when not occluded."),intersection:z2.object({x:z2.number().describe("Intersection rect X."),y:z2.number().describe("Intersection rect Y."),width:z2.number().describe("Intersection rect width."),height:z2.number().describe("Intersection rect height."),area:z2.number().describe("Intersection rect area in CSS pixels squared.")}).nullable().describe("Intersection box between this element and the occluding element. Null if not occluded or cannot compute.")}).optional().describe("Occlusion detection results. Only present when checkOcclusion=true.")}).optional().describe("Runtime-derived visual information representing how the element is actually rendered.")})).describe("List of enriched DOM-backed AX nodes combining accessibility metadata with visual diagnostics.")}}async handle(context,args){let page=context.page,includeRuntimeVisual=args.includeRuntimeVisual??DEFAULT_INCLUDE_RUNTIME_VISUAL,includeStyles=args.includeStyles??DEFAULT_INCLUDE_STYLES,checkOcclusion=args.checkOcclusion??DEFAULT_CHECK_OCCLUSION,onlyVisible=args.onlyVisible??DEFAULT_ONLY_VISIBLE,onlyInViewport=args.onlyInViewport??DEFAULT_ONLY_IN_VIEWPORT;if((onlyVisible||onlyInViewport)&&!includeRuntimeVisual)throw new Error("onlyVisible/onlyInViewport require includeRuntimeVisual=true.");if(checkOcclusion&&!includeRuntimeVisual)throw new Error("checkOcclusion requires includeRuntimeVisual=true.");let textMax=args.textPreviewMaxLength??DEFAULT_TEXT_PREVIEW_MAX_LENGTH,stylePropsRaw=args.styleProperties&&args.styleProperties.length>0?args.styleProperties:DEFAULT_STYLE_PROPERTIES,stylePropsLower=Array.from(stylePropsRaw).map(p=>p.toLowerCase()),roleAllow=args.roles&&args.roles.length>0?new Set(args.roles.flatMap(r=>{let lower=r.toLowerCase();return lower==="text"?["text","statictext"]:lower==="img"?["img","image"]:[lower]})):new Set(Array.from(DEFAULT_INTERESTING_ROLES).map(r=>r.toLowerCase())),cdp=await page.context().newCDPSession(page);try{await cdp.send("DOM.enable"),await cdp.send("Accessibility.enable"),includeRuntimeVisual&&await cdp.send("Runtime.enable");let axResponse=await cdp.send("Accessibility.getFullAXTree"),axNodes=axResponse.nodes??axResponse,parentByChildId=new Map,childIdsByNodeId=new Map;for(let n of axNodes){let nodeIdStr=String(n.nodeId),childIds=(n.childIds??[]).map(cid=>String(cid));childIdsByNodeId.set(nodeIdStr,childIds);for(let childIdStr of childIds)parentByChildId.set(childIdStr,nodeIdStr)}let candidates=axNodes.filter(n=>{if(typeof n.backendDOMNodeId!="number")return!1;let roleValue=n.role?.value??null;if(!roleValue)return!1;let roleStr=String(roleValue);return roleAllow.has(roleStr)||roleAllow.has(roleStr.toLowerCase())}),candidateCount=candidates.length,truncatedBySafetyCap=candidates.length>INTERNAL_SAFETY_CAP;truncatedBySafetyCap&&(candidates=candidates.slice(0,INTERNAL_SAFETY_CAP));let queue=[...candidates],nodesOut=[],objectIds=[],worker=async()=>{for(;queue.length>0;){let ax=queue.shift();if(!ax)return;let axNodeIdStr=String(ax.nodeId),parentAxNodeId=parentByChildId.get(axNodeIdStr)??null,childAxNodeIds=childIdsByNodeId.get(axNodeIdStr)??[],backendDOMNodeId=ax.backendDOMNodeId,domNodeId=null,localName=null,id=null,className=null,objectId=null;try{let node=(await cdp.send("DOM.describeNode",{backendNodeId:backendDOMNodeId}))?.node;if(node){let nodeIdCandidate=node.nodeId;typeof nodeIdCandidate=="number"&&nodeIdCandidate>0?domNodeId=nodeIdCandidate:domNodeId=null,localName=node.localName??(node.nodeName?String(node.nodeName).toLowerCase():null);let attrObj=attrsToObj(node.attributes);id=attrObj.id??null,className=attrObj.class??null}}catch{}if(includeRuntimeVisual)try{objectId=(await cdp.send("DOM.resolveNode",{backendNodeId:backendDOMNodeId}))?.object?.objectId??null}catch{}let roleStr=ax.role?.value?String(ax.role.value):null,nameStr=ax.name?.value?String(ax.name.value):null,ignoredVal=typeof ax.ignored=="boolean"?ax.ignored:null,item={axNodeId:axNodeIdStr,parentAxNodeId,childAxNodeIds,role:roleStr,name:nameStr,ignored:ignoredVal,backendDOMNodeId,domNodeId,frameId:ax.frameId??null,localName,id,className,selectorHint:null,textPreview:null,value:ax.value??null,description:ax.description??null,properties:Array.isArray(ax.properties)?ax.properties:null},index=nodesOut.push(item)-1;objectIds[index]=objectId}},workers=Array.from({length:INTERNAL_CONCURRENCY},()=>worker());if(await Promise.all(workers),includeRuntimeVisual){let globalObjectId=(await cdp.send("Runtime.evaluate",{expression:"globalThis",returnByValue:!1}))?.result?.objectId;if(globalObjectId){let runtimeArgs=objectIds.map(oid=>oid?{objectId:oid}:{value:null}),values=(await cdp.send("Runtime.callFunctionOn",{objectId:globalObjectId,returnByValue:!0,functionDeclaration:`
37
+ `.trim()}inputSchema(){return{roles:z5.array(z5.string().min(1)).optional().describe('ARIA/AX role names to include; omit for default set. Standard roles: ARIA https://w3c.github.io/aria/#role_definitions, Chromium AX https://chromium.googlesource.com/chromium/src/+/main/ui/accessibility/ax_enum_util.cc (e.g. button, heading, staticText, image, main, navigation). Matching is case-insensitive; "text" also matches staticText, "img" matches image.'),includeStyles:z5.boolean().optional().default(DEFAULT_INCLUDE_STYLES),includeRuntimeVisual:z5.boolean().optional().default(DEFAULT_INCLUDE_RUNTIME_VISUAL).describe("Include bounding box and visibility."),checkOcclusion:z5.boolean().optional().default(DEFAULT_CHECK_OCCLUSION).describe("Use elementFromPoint to detect what is on top."),onlyVisible:z5.boolean().optional().default(DEFAULT_ONLY_VISIBLE),onlyInViewport:z5.boolean().optional().default(DEFAULT_ONLY_IN_VIEWPORT),textPreviewMaxLength:z5.number().int().positive().optional().default(DEFAULT_TEXT_PREVIEW_MAX_LENGTH),styleProperties:z5.array(z5.string()).optional().default([...DEFAULT_STYLE_PROPERTIES]).describe("CSS property names when includeStyles.")}}outputSchema(){return{url:z5.string().describe("The current page URL at the time the AX snapshot was captured."),title:z5.string().describe("The document title of the page at the time of the snapshot."),axNodeCount:z5.number().int().nonnegative().describe("Total number of nodes returned by Chromium Accessibility.getFullAXTree before filtering."),candidateCount:z5.number().int().nonnegative().describe("Number of DOM-backed AX nodes that passed role filtering before enrichment."),enrichedCount:z5.number().int().nonnegative().describe("Number of nodes included in the final enriched snapshot output."),truncatedBySafetyCap:z5.boolean().describe("Indicates whether the result set was truncated by an internal safety cap to prevent excessive output size."),nodes:z5.array(z5.object({axNodeId:z5.string().describe("Unique identifier of the accessibility node within the AX tree."),parentAxNodeId:z5.string().nullable().describe("Parent AX node id in the full AX tree. Null if this node is a root."),childAxNodeIds:z5.array(z5.string()).describe("Child AX node ids in the full AX tree (may include nodes not present in the filtered output)."),role:z5.string().nullable().describe("ARIA role of the accessibility node (e.g. button, link, textbox)."),name:z5.string().nullable().describe("Accessible name computed by the browser accessibility engine."),ignored:z5.boolean().nullable().describe("Whether the accessibility node is marked as ignored."),backendDOMNodeId:z5.number().int().describe("Chromium backend DOM node identifier used to map AX nodes to DOM elements."),domNodeId:z5.number().int().nullable().describe("Resolved DOM nodeId from CDP if available; may be null because nodeId is not guaranteed to be stable/resolved."),frameId:z5.string().nullable().describe("Frame identifier if the node belongs to an iframe or subframe."),localName:z5.string().nullable().describe("Lowercased DOM tag name of the mapped element (e.g. div, button, input)."),id:z5.string().nullable().describe("DOM id attribute of the mapped element."),className:z5.string().nullable().describe("DOM class attribute of the mapped element."),selectorHint:z5.string().nullable().describe("Best-effort selector hint for targeting this element (prefers data-testid/data-selector/id)."),textPreview:z5.string().nullable().describe("Short preview of rendered text content or aria-label, truncated to the configured maximum length."),value:z5.any().nullable().describe("Raw AX value payload associated with the node, if present."),description:z5.any().nullable().describe("Raw AX description payload associated with the node, if present."),properties:z5.array(z5.any()).nullable().describe("Additional AX properties exposed by the accessibility tree."),styles:z5.record(z5.string(),z5.string()).optional().describe("Subset of computed CSS styles for the element as string key-value pairs."),runtime:z5.object({boundingBox:z5.object({x:z5.number().describe("X coordinate of the element relative to the viewport."),y:z5.number().describe("Y coordinate of the element relative to the viewport."),width:z5.number().describe("Width of the element in CSS pixels."),height:z5.number().describe("Height of the element in CSS pixels.")}).nullable().describe("Bounding box computed at runtime using getBoundingClientRect."),isVisible:z5.boolean().describe("Whether the element is considered visually visible (display, visibility, opacity, and size)."),isInViewport:z5.boolean().describe("Whether the element intersects the current viewport."),occlusion:z5.object({samplePoints:z5.array(z5.object({x:z5.number().describe("Sample point X (viewport coordinates) used for occlusion testing."),y:z5.number().describe("Sample point Y (viewport coordinates) used for occlusion testing."),hit:z5.boolean().describe("True if elementFromPoint at this point returned a different element that is not a descendant.")})).describe("Sample points used for occlusion detection (center + corners)."),isOccluded:z5.boolean().describe("True if at least one sample point is covered by another element."),topElement:z5.object({localName:z5.string().nullable().describe("Tag name of the occluding element."),id:z5.string().nullable().describe("DOM id of the occluding element (may be null if none)."),className:z5.string().nullable().describe("DOM class of the occluding element (may be null if none)."),selectorHint:z5.string().nullable().describe("Best-effort selector hint for the occluding element."),boundingBox:z5.object({x:z5.number().describe("X coordinate of the occluding element bounding box."),y:z5.number().describe("Y coordinate of the occluding element bounding box."),width:z5.number().describe("Width of the occluding element bounding box."),height:z5.number().describe("Height of the occluding element bounding box.")}).nullable().describe("Bounding box of the occluding element (if available).")}).nullable().describe("Identity and geometry of the occluding element. Null when not occluded."),intersection:z5.object({x:z5.number().describe("Intersection rect X."),y:z5.number().describe("Intersection rect Y."),width:z5.number().describe("Intersection rect width."),height:z5.number().describe("Intersection rect height."),area:z5.number().describe("Intersection rect area in CSS pixels squared.")}).nullable().describe("Intersection box between this element and the occluding element. Null if not occluded or cannot compute.")}).optional().describe("Occlusion detection results. Only present when checkOcclusion=true.")}).optional().describe("Runtime-derived visual information representing how the element is actually rendered.")})).describe("List of enriched DOM-backed AX nodes combining accessibility metadata with visual diagnostics.")}}async handle(context,args){let page=context.page,includeRuntimeVisual=args.includeRuntimeVisual??DEFAULT_INCLUDE_RUNTIME_VISUAL,includeStyles=args.includeStyles??DEFAULT_INCLUDE_STYLES,checkOcclusion=args.checkOcclusion??DEFAULT_CHECK_OCCLUSION,onlyVisible=args.onlyVisible??DEFAULT_ONLY_VISIBLE,onlyInViewport=args.onlyInViewport??DEFAULT_ONLY_IN_VIEWPORT;if((onlyVisible||onlyInViewport)&&!includeRuntimeVisual)throw new Error("onlyVisible/onlyInViewport require includeRuntimeVisual=true.");if(checkOcclusion&&!includeRuntimeVisual)throw new Error("checkOcclusion requires includeRuntimeVisual=true.");let textMax=args.textPreviewMaxLength??DEFAULT_TEXT_PREVIEW_MAX_LENGTH,stylePropsRaw=args.styleProperties&&args.styleProperties.length>0?args.styleProperties:DEFAULT_STYLE_PROPERTIES,stylePropsLower=Array.from(stylePropsRaw).map(p=>p.toLowerCase()),roleAllow=args.roles&&args.roles.length>0?new Set(args.roles.flatMap(r=>{let lower=r.toLowerCase();return lower==="text"?["text","statictext"]:lower==="img"?["img","image"]:[lower]})):new Set(Array.from(DEFAULT_INTERESTING_ROLES).map(r=>r.toLowerCase())),cdp=await page.context().newCDPSession(page);try{await cdp.send("DOM.enable"),await cdp.send("Accessibility.enable"),includeRuntimeVisual&&await cdp.send("Runtime.enable");let axResponse=await cdp.send("Accessibility.getFullAXTree"),axNodes=axResponse.nodes??axResponse,parentByChildId=new Map,childIdsByNodeId=new Map;for(let n of axNodes){let nodeIdStr=String(n.nodeId),childIds=(n.childIds??[]).map(cid=>String(cid));childIdsByNodeId.set(nodeIdStr,childIds);for(let childIdStr of childIds)parentByChildId.set(childIdStr,nodeIdStr)}let candidates=axNodes.filter(n=>{if(typeof n.backendDOMNodeId!="number")return!1;let roleValue=n.role?.value??null;if(!roleValue)return!1;let roleStr=String(roleValue);return roleAllow.has(roleStr)||roleAllow.has(roleStr.toLowerCase())}),candidateCount=candidates.length,truncatedBySafetyCap=candidates.length>INTERNAL_SAFETY_CAP;truncatedBySafetyCap&&(candidates=candidates.slice(0,INTERNAL_SAFETY_CAP));let queue=[...candidates],nodesOut=[],objectIds=[],worker=async()=>{for(;queue.length>0;){let ax=queue.shift();if(!ax)return;let axNodeIdStr=String(ax.nodeId),parentAxNodeId=parentByChildId.get(axNodeIdStr)??null,childAxNodeIds=childIdsByNodeId.get(axNodeIdStr)??[],backendDOMNodeId=ax.backendDOMNodeId,domNodeId=null,localName=null,id=null,className=null,objectId=null;try{let node=(await cdp.send("DOM.describeNode",{backendNodeId:backendDOMNodeId}))?.node;if(node){let nodeIdCandidate=node.nodeId;typeof nodeIdCandidate=="number"&&nodeIdCandidate>0?domNodeId=nodeIdCandidate:domNodeId=null,localName=node.localName??(node.nodeName?String(node.nodeName).toLowerCase():null);let attrObj=attrsToObj(node.attributes);id=attrObj.id??null,className=attrObj.class??null}}catch{}if(includeRuntimeVisual)try{objectId=(await cdp.send("DOM.resolveNode",{backendNodeId:backendDOMNodeId}))?.object?.objectId??null}catch{}let roleStr=ax.role?.value?String(ax.role.value):null,nameStr=ax.name?.value?String(ax.name.value):null,ignoredVal=typeof ax.ignored=="boolean"?ax.ignored:null,item={axNodeId:axNodeIdStr,parentAxNodeId,childAxNodeIds,role:roleStr,name:nameStr,ignored:ignoredVal,backendDOMNodeId,domNodeId,frameId:ax.frameId??null,localName,id,className,selectorHint:null,textPreview:null,value:ax.value??null,description:ax.description??null,properties:Array.isArray(ax.properties)?ax.properties:null},index=nodesOut.push(item)-1;objectIds[index]=objectId}},workers=Array.from({length:INTERNAL_CONCURRENCY},()=>worker());if(await Promise.all(workers),includeRuntimeVisual){let globalObjectId=(await cdp.send("Runtime.evaluate",{expression:"globalThis",returnByValue:!1}))?.result?.objectId;if(globalObjectId){let runtimeArgs=objectIds.map(oid=>oid?{objectId:oid}:{value:null}),values=(await cdp.send("Runtime.callFunctionOn",{objectId:globalObjectId,returnByValue:!0,functionDeclaration:`
33
38
  function(textMax, includeStyles, styleProps, checkOcclusion, ...els) {
34
39
  function selectorHintFor(el) {
35
40
  if (!(el instanceof Element)) { return null; }
@@ -252,18 +257,26 @@ function(textMax, includeStyles, styleProps, checkOcclusion, ...els) {
252
257
  };
253
258
  });
254
259
  }
255
- `,arguments:[{value:textMax},{value:includeStyles},{value:stylePropsLower},{value:checkOcclusion},...runtimeArgs]}))?.result?.value??[];for(let i=0;i<nodesOut.length;i++){let v=values[i];v&&(nodesOut[i].selectorHint=v.selectorHint??null,nodesOut[i].textPreview=v.textPreview??null,v.styles&&(nodesOut[i].styles=v.styles),v.runtime&&(nodesOut[i].runtime=v.runtime))}}}let finalNodes=nodesOut;return(onlyVisible||onlyInViewport)&&(finalNodes=finalNodes.filter(n=>!(!n.runtime||onlyVisible&&!n.runtime.isVisible||onlyInViewport&&!n.runtime.isInViewport))),{url:String(page.url()),title:String(await page.title()),axNodeCount:axNodes.length,candidateCount,enrichedCount:finalNodes.length,truncatedBySafetyCap,nodes:finalNodes}}finally{await cdp.detach().catch(()=>{})}}};var tools=[new TakeAriaSnapshot,new TakeAxTreeSnapshot];import{z as z3}from"zod";var DEFAULT_MAX_HTML_LENGTH=5e4,GetAsHtml=class{name(){return"content_get-as-html"}description(){return`
260
+ `,arguments:[{value:textMax},{value:includeStyles},{value:stylePropsLower},{value:checkOcclusion},...runtimeArgs]}))?.result?.value??[];for(let i=0;i<nodesOut.length;i++){let v=values[i];v&&(nodesOut[i].selectorHint=v.selectorHint??null,nodesOut[i].textPreview=v.textPreview??null,v.styles&&(nodesOut[i].styles=v.styles),v.runtime&&(nodesOut[i].runtime=v.runtime))}}}let finalNodes=nodesOut;return(onlyVisible||onlyInViewport)&&(finalNodes=finalNodes.filter(n=>!(!n.runtime||onlyVisible&&!n.runtime.isVisible||onlyInViewport&&!n.runtime.isInViewport))),{url:String(page.url()),title:String(await page.title()),axNodeCount:axNodes.length,candidateCount,enrichedCount:finalNodes.length,truncatedBySafetyCap,nodes:finalNodes}}finally{await cdp.detach().catch(()=>{})}}};var tools=[new TakeAriaSnapshot,new TakeAxTreeSnapshot];import{z as z6}from"zod";var DEFAULT_MAX_HTML_LENGTH=5e4,GetAsHtml=class{name(){return"content_get-as-html"}description(){return`
256
261
  Gets the HTML content of the current page.
257
262
  By default, all <script> tags are removed from the output unless "removeScripts" is explicitly set to "false".
258
- `}inputSchema(){return{selector:z3.string().optional().describe("Selector or ref; omit for full document."),removeScripts:z3.boolean().optional().default(!0),removeComments:z3.boolean().optional().default(!1),removeStyles:z3.boolean().optional().default(!1),removeMeta:z3.boolean().optional().default(!1),cleanHtml:z3.boolean().optional().default(!1),minify:z3.boolean().optional().default(!1),maxLength:z3.number().int().positive().optional().default(DEFAULT_MAX_HTML_LENGTH)}}outputSchema(){return{output:z3.string().describe("The requested HTML content of the page.")}}async handle(context,args){let{selector,removeScripts,removeComments,removeStyles,removeMeta,minify,cleanHtml,maxLength}=args,htmlContent;if(selector){let element=await context.page.$(selector);if(!element)throw new Error(`Element with selector "${selector}" not found`);htmlContent=await element.evaluate(el=>el.outerHTML)}else htmlContent=await context.page.content();let shouldRemoveScripts=removeScripts||cleanHtml,shouldRemoveComments=removeComments||cleanHtml,shouldRemoveStyles=removeStyles||cleanHtml,shouldRemoveMeta=removeMeta||cleanHtml;(shouldRemoveScripts||shouldRemoveComments||shouldRemoveStyles||shouldRemoveMeta||minify)&&(htmlContent=await context.page.evaluate(params=>{let html=params.html,removeScripts2=params.removeScripts,removeComments2=params.removeComments,removeStyles2=params.removeStyles,removeMeta2=params.removeMeta,minify2=params.minify,template=document.createElement("template");template.innerHTML=html;let root=template.content;if(removeScripts2&&root.querySelectorAll("script").forEach(script=>script.remove()),removeStyles2&&root.querySelectorAll("style").forEach(style=>style.remove()),removeMeta2&&root.querySelectorAll("meta").forEach(meta=>meta.remove()),removeComments2){let removeComments3=node=>{let childNodes=node.childNodes;for(let i=childNodes.length-1;i>=0;i--){let child=childNodes[i];child.nodeType===8?node.removeChild(child):child.nodeType===1&&removeComments3(child)}};removeComments3(root)}let result=template.innerHTML;return minify2&&(result=result.replace(/>\s+</g,"><").trim()),result},{html:htmlContent,removeScripts:shouldRemoveScripts,removeComments:shouldRemoveComments,removeStyles:shouldRemoveStyles,removeMeta:shouldRemoveMeta,minify}));let output=htmlContent;return output.length>maxLength&&(output=output.slice(0,maxLength)+`
259
- <!-- Output truncated due to size limits -->`),{output}}};import{z as z4}from"zod";var DEFAULT_MAX_TEXT_LENGTH=5e4,GetAsText=class{name(){return"content_get-as-text"}description(){return"Gets the visible text content of the current page."}inputSchema(){return{selector:z4.string().describe("Limit text to this container; omit for full page.").optional(),maxLength:z4.number().int().positive().optional().default(DEFAULT_MAX_TEXT_LENGTH).describe("Truncate after this many characters.")}}outputSchema(){return{output:z4.string().describe("The requested text content of the page.")}}async handle(context,args){let{selector,maxLength}=args,output=await context.page.evaluate(params=>{let selector2=params.selector,root=selector2?document.querySelector(selector2):document.body;if(!root)throw new Error(`Element with selector "${selector2}" not found`);let walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,{acceptNode:node2=>{let style=window.getComputedStyle(node2.parentElement);return style.display!=="none"&&style.visibility!=="hidden"?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}}),text="",node;for(;node=walker.nextNode();){let trimmedText=node.textContent?.trim();trimmedText&&(text+=trimmedText+`
263
+ `}inputSchema(){return{selector:z6.string().optional().describe("Selector or ref; omit for full document."),removeScripts:z6.boolean().optional().default(!0),removeComments:z6.boolean().optional().default(!1),removeStyles:z6.boolean().optional().default(!1),removeMeta:z6.boolean().optional().default(!1),cleanHtml:z6.boolean().optional().default(!1),minify:z6.boolean().optional().default(!1),maxLength:z6.number().int().positive().optional().default(DEFAULT_MAX_HTML_LENGTH)}}outputSchema(){return{output:z6.string().describe("The requested HTML content of the page.")}}async handle(context,args){let{selector,removeScripts,removeComments,removeStyles,removeMeta,minify,cleanHtml,maxLength}=args,htmlContent;if(selector){let element=await context.page.$(selector);if(!element)throw new Error(`Element with selector "${selector}" not found`);htmlContent=await element.evaluate(el=>el.outerHTML)}else htmlContent=await context.page.content();let shouldRemoveScripts=removeScripts||cleanHtml,shouldRemoveComments=removeComments||cleanHtml,shouldRemoveStyles=removeStyles||cleanHtml,shouldRemoveMeta=removeMeta||cleanHtml;(shouldRemoveScripts||shouldRemoveComments||shouldRemoveStyles||shouldRemoveMeta||minify)&&(htmlContent=await context.page.evaluate(params=>{let html=params.html,removeScripts2=params.removeScripts,removeComments2=params.removeComments,removeStyles2=params.removeStyles,removeMeta2=params.removeMeta,minify2=params.minify,template=document.createElement("template");template.innerHTML=html;let root=template.content;if(removeScripts2&&root.querySelectorAll("script").forEach(script=>script.remove()),removeStyles2&&root.querySelectorAll("style").forEach(style=>style.remove()),removeMeta2&&root.querySelectorAll("meta").forEach(meta=>meta.remove()),removeComments2){let removeComments3=node=>{let childNodes=node.childNodes;for(let i=childNodes.length-1;i>=0;i--){let child=childNodes[i];child.nodeType===8?node.removeChild(child):child.nodeType===1&&removeComments3(child)}};removeComments3(root)}let result=template.innerHTML;return minify2&&(result=result.replace(/>\s+</g,"><").trim()),result},{html:htmlContent,removeScripts:shouldRemoveScripts,removeComments:shouldRemoveComments,removeStyles:shouldRemoveStyles,removeMeta:shouldRemoveMeta,minify}));let output=htmlContent;return output.length>maxLength&&(output=output.slice(0,maxLength)+`
264
+ <!-- Output truncated due to size limits -->`),{output}}};import{z as z7}from"zod";var DEFAULT_MAX_TEXT_LENGTH=5e4,GetAsText=class{name(){return"content_get-as-text"}description(){return"Gets the visible text content of the current page."}inputSchema(){return{selector:z7.string().describe("Limit text to this container; omit for full page.").optional(),maxLength:z7.number().int().positive().optional().default(DEFAULT_MAX_TEXT_LENGTH).describe("Truncate after this many characters.")}}outputSchema(){return{output:z7.string().describe("The requested text content of the page.")}}async handle(context,args){let{selector,maxLength}=args,output=await context.page.evaluate(params=>{let selector2=params.selector,root=selector2?document.querySelector(selector2):document.body;if(!root)throw new Error(`Element with selector "${selector2}" not found`);let walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,{acceptNode:node2=>{let style=window.getComputedStyle(node2.parentElement);return style.display!=="none"&&style.visibility!=="hidden"?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}}),text="",node;for(;node=walker.nextNode();){let trimmedText=node.textContent?.trim();trimmedText&&(text+=trimmedText+`
260
265
  `)}return text.trim()},{selector});return output.length>maxLength&&(output=output.slice(0,maxLength)+`
261
- [Output truncated due to size limits]`),{output}}};function getEnumKeyTuples(enumObj){let values=Object.keys(enumObj).filter(key=>isNaN(Number(key))).map(key=>enumObj[key]);if(values.length===0)throw new Error("Enum has no values");return values}function createEnumTransformer(enumObj,opts){let values=Object.keys(enumObj).filter(k=>isNaN(Number(k))).map(k=>enumObj[k]),caseInsensitive=opts?.caseInsensitive??!0,lookup=new Map(values.map(v=>[caseInsensitive?v.toLowerCase():v,v]));return value=>{let key=caseInsensitive?value.toLowerCase():value,found=lookup.get(key);if(found===void 0)throw new Error(`Invalid enum value: "${value}"`);return found}}function formattedTimeForFilename(date=new Date){let pad=n=>String(n).padStart(2,"0");return date.getFullYear()+pad(date.getMonth()+1)+pad(date.getDate())+"-"+pad(date.getHours())+pad(date.getMinutes())+pad(date.getSeconds())}import os from"node:os";import path2 from"node:path";import{z as z5}from"zod";var PageFormat=(PageFormat2=>(PageFormat2.LETTER="Letter",PageFormat2.LEGAL="Legal",PageFormat2.TABLOID="Tabloid",PageFormat2.LEDGER="Ledger",PageFormat2.A0="A0",PageFormat2.A1="A1",PageFormat2.A2="A2",PageFormat2.A3="A3",PageFormat2.A4="A4",PageFormat2.A5="A5",PageFormat2.A6="A6",PageFormat2))(PageFormat||{}),DEFAULT_NAME="page",DEFAULT_MARGIN="1cm",DEFAULT_FORMAT="A4",DEFAULT_MARGINS={top:DEFAULT_MARGIN,right:DEFAULT_MARGIN,bottom:DEFAULT_MARGIN,left:DEFAULT_MARGIN},SaveAsPdf=class{name(){return"content_save-as-pdf"}description(){return"Saves the current page as a PDF file."}inputSchema(){return{outputPath:z5.string().optional().default(os.tmpdir()),name:z5.string().optional().default(DEFAULT_NAME),format:z5.enum(getEnumKeyTuples(PageFormat)).transform(createEnumTransformer(PageFormat)).optional().default(DEFAULT_FORMAT).describe("Page size."),printBackground:z5.boolean().optional().default(!1).describe("Background."),margin:z5.object({top:z5.string().default(DEFAULT_MARGIN),right:z5.string().default(DEFAULT_MARGIN),bottom:z5.string().default(DEFAULT_MARGIN),left:z5.string().default(DEFAULT_MARGIN)}).optional().describe("Margin (e.g. 1cm).")}}outputSchema(){return{filePath:z5.string().describe("Full path of the saved PDF file.")}}async handle(context,args){let filename=`${args.name||DEFAULT_NAME}-${formattedTimeForFilename()}.pdf`,filePath=path2.resolve(args.outputPath,filename),options={path:filePath,format:args.format,printBackground:args.printBackground,margin:args.margin||DEFAULT_MARGINS};return await context.page.pdf(options),{filePath}}};var REF_ROLES_INTERACTIVE=new Set(["button","link","textbox","searchbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","slider","spinbutton","switch","tab","tabpanel","treeitem","listitem","row","cell","gridcell","columnheader","rowheader","separator","dialog","alertdialog","clickable","focusable"]);function parseQuotedString(s,i){let q=s[i];if(q!=="'"&&q!=='"')return null;let j=i+1,parts=[];for(;j<s.length;){let c=s[j];if(c==="\\"){j++,j<s.length&&parts.push(s[j]),j++;continue}if(c===q)return{value:parts.join(""),end:j+1};parts.push(c),j++}return null}function parseRegexLiteral(s,i){if(s[i]!=="/")return null;let j=i+1;for(;j<s.length;){if(s[j]==="\\"){j+=2;continue}if(s[j]==="/")break;j++}if(j>=s.length)return null;let pattern=s.slice(i+1,j);j++;let flags="";for(;j<s.length&&/[gimsuy]/.test(s[j])&&!flags.includes(s[j]);)flags+=s[j],j++;try{return{value:new RegExp(pattern,flags),end:j}}catch{return null}}function parseOptionsObject(s,i){for(;i<s.length&&/\s/.test(s[i]);)i++;if(s[i]!=="{")return null;i++;let obj={};for(;i<s.length;){for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;if(s[i]==="}")return{value:obj,end:i+1};let keyMatch=s.slice(i).match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:/);if(!keyMatch)return null;let key=keyMatch[1];for(i+=keyMatch[0].length;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;if(s[i]==="'"||s[i]==='"'){let parsed=parseQuotedString(s,i);if(!parsed)return null;obj[key]=parsed.value,i=parsed.end}else if(s[i]==="/"){let parsed=parseRegexLiteral(s,i);if(!parsed)return null;obj[key]=parsed.value,i=parsed.end}else if(s.slice(i).startsWith("true"))obj[key]=!0,i+=4;else if(s.slice(i).startsWith("false"))obj[key]=!1,i+=5;else{let numMatch=s.slice(i).match(/^-?\d+/);if(numMatch)obj[key]=parseInt(numMatch[0],10),i+=numMatch[0].length;else return null}for(;i<s.length&&/\s/.test(s[i]);)i++;if(i<s.length&&s[i]===","){i++;continue}break}for(;i<s.length&&/\s/.test(s[i]);)i++;return s[i]!=="}"?null:{value:obj,end:i+1}}function parseGetByArgs(inner){let s=inner.trim(),i=0;for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;let first;if(s[i]==="'"||s[i]==='"'){let firstParsed=parseQuotedString(s,i);if(!firstParsed)return null;first=firstParsed.value,i=firstParsed.end}else if(s[i]==="/"){let regexParsed=parseRegexLiteral(s,i);if(!regexParsed)return null;first=regexParsed.value,i=regexParsed.end}else return null;for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return{first};if(s[i]!==",")return{first};i++;let optParsed=parseOptionsObject(s,i);return optParsed?{first,options:optParsed.value}:{first}}function tryParsePlaywrightLocatorExpression(page,selectorOrExpression){let match=selectorOrExpression.trim().match(/^\s*(getByRole|getByLabel|getByText|getByPlaceholder|getByAltText|getByTitle|getByTestId)\s*\(\s*([\s\S]*)\s*\)\s*$/);if(!match)return null;let method=match[1],argsInner=match[2],parsed=parseGetByArgs(argsInner);if(!parsed)return null;let{first,options}=parsed;if(method==="getByTestId")return page.getByTestId(first).first();if(method==="getByRole"){if(typeof first!="string")return null;let opts=options??{},roleOpts={};return opts.name!==void 0&&(roleOpts.name=typeof opts.name=="string"||opts.name instanceof RegExp?opts.name:String(opts.name)),opts.exact!==void 0&&(roleOpts.exact=!!opts.exact),opts.checked!==void 0&&(roleOpts.checked=!!opts.checked),opts.disabled!==void 0&&(roleOpts.disabled=!!opts.disabled),opts.expanded!==void 0&&(roleOpts.expanded=!!opts.expanded),opts.includeHidden!==void 0&&(roleOpts.includeHidden=!!opts.includeHidden),opts.level!==void 0&&(roleOpts.level=Number(opts.level)),opts.pressed!==void 0&&(roleOpts.pressed=!!opts.pressed),opts.selected!==void 0&&(roleOpts.selected=!!opts.selected),page.getByRole(first,Object.keys(roleOpts).length>0?roleOpts:void 0).first()}let exactOpts=options&&options.exact!==void 0?{exact:!!options.exact}:void 0;switch(method){case"getByLabel":return page.getByLabel(first,exactOpts).first();case"getByText":return page.getByText(first,exactOpts).first();case"getByPlaceholder":return page.getByPlaceholder(first,exactOpts).first();case"getByAltText":return page.getByAltText(first,exactOpts).first();case"getByTitle":return page.getByTitle(first,exactOpts).first();default:return null}}function parseRef(arg){let s=arg.trim();return s.startsWith("@")?s.slice(1):s.toLowerCase().startsWith("ref=")?s.slice(4):/^e\d+$/.test(s)?s:null}function getLocatorFromRef(page,refMap,ref){let data=refMap[ref];if(!data)return null;if(data.role==="clickable"||data.role==="focusable"){let locator2=page.locator(data.selector);return data.nth!==void 0?locator2.nth(data.nth):locator2.first()}let locator;return data.name!==void 0&&data.name!==""?locator=page.getByRole(data.role,{name:data.name,exact:!0}):locator=page.getByRole(data.role),data.nth!==void 0?locator.nth(data.nth):locator.first()}function assertRefRole(context,selectorOrRef,allowedRoles,actionLabel,hint){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(!ref||!refMap[ref])return;let entry=refMap[ref];if(allowedRoles.has(entry.role))return;let namePart=entry.name?` "${entry.name}"`:"";throw new Error(`Ref ${ref} points to a ${entry.role}${namePart}, which is not valid for ${actionLabel}. ${hint}`)}function resolveSelectorOrRef(context,selectorOrRef){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(ref&&Object.keys(refMap).length>0){let locator=getLocatorFromRef(context.page,refMap,ref);if(locator)return locator}let playwrightLocator=tryParsePlaywrightLocatorExpression(context.page,selectorOrRef);return playwrightLocator||context.page.locator(selectorOrRef).first()}import jpegjs from"jpeg-js";import{PNG}from"pngjs";var ANNOTATION_OVERLAY_ID="__browser_devtools_mcp_annotations__",ANNOTATE_BOX_TIMEOUT_MS=2e3,ScreenshotType=(ScreenshotType2=>(ScreenshotType2.PNG="png",ScreenshotType2.JPEG="jpeg",ScreenshotType2))(ScreenshotType||{}),MAX_BUFFER_SIZE=800*1024,MAX_PIXELS=1.15*1024*1024,MAX_LINEAR_SIZE=1568;function scaleImageToSize(image,size){let{data:src,width:w1,height:h1}=image,w2=Math.max(1,Math.floor(size.width)),h2=Math.max(1,Math.floor(size.height));if(w1===w2&&h1===h2)return image;if(w1<=0||h1<=0)throw new Error("Invalid input image");if(size.width<=0||size.height<=0||!isFinite(size.width)||!isFinite(size.height))throw new Error("Invalid output dimensions");let clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v,weights=(t,o)=>{let t2=t*t,t3=t2*t;o[0]=-.5*t+1*t2-.5*t3,o[1]=1-2.5*t2+1.5*t3,o[2]=.5*t+2*t2-1.5*t3,o[3]=-.5*t2+.5*t3},srcRowStride=w1*4,dstRowStride=w2*4,xOff=new Int32Array(w2*4),xW=new Float32Array(w2*4),wx=new Float32Array(4),xScale=w1/w2;for(let x=0;x<w2;x++){let sx=(x+.5)*xScale-.5,sxi=Math.floor(sx),t=sx-sxi;weights(t,wx);let b=x*4,i0=clamp(sxi-1,0,w1-1),i1=clamp(sxi+0,0,w1-1),i2=clamp(sxi+1,0,w1-1),i3=clamp(sxi+2,0,w1-1);xOff[b+0]=i0<<2,xOff[b+1]=i1<<2,xOff[b+2]=i2<<2,xOff[b+3]=i3<<2,xW[b+0]=wx[0],xW[b+1]=wx[1],xW[b+2]=wx[2],xW[b+3]=wx[3]}let yRow=new Int32Array(h2*4),yW=new Float32Array(h2*4),wy=new Float32Array(4),yScale=h1/h2;for(let y=0;y<h2;y++){let sy=(y+.5)*yScale-.5,syi=Math.floor(sy),t=sy-syi;weights(t,wy);let b=y*4,j0=clamp(syi-1,0,h1-1),j1=clamp(syi+0,0,h1-1),j2=clamp(syi+1,0,h1-1),j3=clamp(syi+2,0,h1-1);yRow[b+0]=j0*srcRowStride,yRow[b+1]=j1*srcRowStride,yRow[b+2]=j2*srcRowStride,yRow[b+3]=j3*srcRowStride,yW[b+0]=wy[0],yW[b+1]=wy[1],yW[b+2]=wy[2],yW[b+3]=wy[3]}let dst=new Uint8Array(w2*h2*4);for(let y=0;y<h2;y++){let yb=y*4,rb0=yRow[yb+0],rb1=yRow[yb+1],rb2=yRow[yb+2],rb3=yRow[yb+3],wy0=yW[yb+0],wy1=yW[yb+1],wy2=yW[yb+2],wy3=yW[yb+3],dstBase=y*dstRowStride;for(let x=0;x<w2;x++){let xb=x*4,xo0=xOff[xb+0],xo1=xOff[xb+1],xo2=xOff[xb+2],xo3=xOff[xb+3],wx0=xW[xb+0],wx1=xW[xb+1],wx2=xW[xb+2],wx3=xW[xb+3],di=dstBase+(x<<2);for(let c=0;c<4;c++){let r0=src[rb0+xo0+c]*wx0+src[rb0+xo1+c]*wx1+src[rb0+xo2+c]*wx2+src[rb0+xo3+c]*wx3,r1=src[rb1+xo0+c]*wx0+src[rb1+xo1+c]*wx1+src[rb1+xo2+c]*wx2+src[rb1+xo3+c]*wx3,r2=src[rb2+xo0+c]*wx0+src[rb2+xo1+c]*wx1+src[rb2+xo2+c]*wx2+src[rb2+xo3+c]*wx3,r3=src[rb3+xo0+c]*wx0+src[rb3+xo1+c]*wx1+src[rb3+xo2+c]*wx2+src[rb3+xo3+c]*wx3,v=r0*wy0+r1*wy1+r2*wy2+r3*wy3;dst[di+c]=v<0?0:v>255?255:v|0}}}return{data:Buffer.from(dst.buffer),width:w2,height:h2}}function scaleImageToFitMessage(buffer,screenshotType){let image=screenshotType==="png"?PNG.sync.read(buffer):jpegjs.decode(buffer,{maxMemoryUsageInMB:512}),pixels=image.width*image.height,shrink=Math.min(MAX_LINEAR_SIZE/image.width,MAX_LINEAR_SIZE/image.height,Math.sqrt(MAX_PIXELS/pixels));shrink>1&&(shrink=1);let width=image.width*shrink|0,height=image.height*shrink|0,scaledImage=scaleImageToSize(image,{width,height}),quality=screenshotType==="png"?75:70,result=screenshotType==="png"?jpegjs.encode(scaledImage,quality).data:jpegjs.encode(scaledImage,quality).data,iterations=0,MAX_ITERATIONS=5;for(;result.length>MAX_BUFFER_SIZE&&iterations<MAX_ITERATIONS;)quality=Math.max(50,quality-10),quality<=50&&result.length>MAX_BUFFER_SIZE&&(shrink*=.85,width=Math.max(200,image.width*shrink|0),height=Math.max(200,image.height*shrink|0),scaledImage=scaleImageToSize(image,{width,height})),result=jpegjs.encode(scaledImage,quality).data,iterations++;return result}async function captureScreenshot(context,options){let fullPage=options.fullPage??!1,screenshotType=options.type??"png",annotate=options.annotate??!1,annotateContent=options.annotateContent??!1,annotateCursorInteractive=options.annotateCursorInteractive??!1,quality=options.quality??(screenshotType==="png"?void 0:100),overlayInjected=!1,annotations,scopedElement=null;if(options.selector&&(scopedElement=await resolveSelectorOrRef(context,options.selector).elementHandle({timeout:1e4}),!scopedElement))throw new Error(`Element not found: ${options.selector}`);if(annotate){let refMap=context.getRefMap(),wantContent=annotateContent,wantCursorInteractive=annotateCursorInteractive;if(Object.keys(refMap).length===0||wantCursorInteractive||wantContent){let raw=await context.page.locator("body").ariaSnapshot(),{refs}=processAriaTreeWithRefs(raw,{interactiveOnly:!wantContent}),mergedRefs={...refs};if(wantCursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>Math.max(m,parseInt(k.replace(/^e/,""),10)||0),0);cursorEntries.forEach((entry,i)=>{mergedRefs[`e${maxNum+1+i}`]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth}})}}context.setRefMap(mergedRefs),refMap=mergedRefs}let entries=Object.entries(refMap),allBoxes=[];for(let[ref,data]of entries){let locator=getLocatorFromRef(context.page,refMap,ref);if(!locator)continue;let box=await locator.boundingBox({timeout:ANNOTATE_BOX_TIMEOUT_MS}).catch(()=>null);if(!box||box.width===0||box.height===0)continue;let num=parseInt(ref.replace(/^e/,""),10)||0;allBoxes.push({ref,number:num,role:data.role,name:data.name,box:{x:Math.round(box.x),y:Math.round(box.y),width:Math.round(box.width),height:Math.round(box.height)}})}allBoxes.sort((a,b)=>a.number-b.number);let boxes=allBoxes,overlayBoxesViewport=allBoxes;if(scopedElement){let elementBox=await scopedElement.boundingBox();if(elementBox&&elementBox.width>0&&elementBox.height>0){let overlapping=allBoxes.filter(a=>{let b=a.box;return b.x<elementBox.x+elementBox.width&&b.x+b.width>elementBox.x&&b.y<elementBox.y+elementBox.height&&b.y+b.height>elementBox.y});overlayBoxesViewport=overlapping,boxes=overlapping.map(a=>{let b=a.box,relX=Math.max(0,b.x-elementBox.x),relY=Math.max(0,b.y-elementBox.y),relRight=Math.min(b.x+b.width-elementBox.x,elementBox.width),relBottom=Math.min(b.y+b.height-elementBox.y,elementBox.height);return{...a,box:{x:Math.round(relX),y:Math.round(relY),width:Math.round(Math.max(0,relRight-relX)),height:Math.round(Math.max(0,relBottom-relY))}}})}}if(boxes.length>0){let overlayPayload={data:overlayBoxesViewport.map(a=>({number:a.number,x:a.box.x,y:a.box.y,width:a.box.width,height:a.box.height})),id:ANNOTATION_OVERLAY_ID};if(await context.page.evaluate(({data,id})=>{let sx=window.scrollX||0,sy=window.scrollY||0,c=document.createElement("div");c.id=id,c.style.cssText="position:absolute;top:0;left:0;width:0;height:0;pointer-events:none;z-index:2147483647;";for(let it of data){let dx=it.x+sx,dy=it.y+sy,b=document.createElement("div");b.style.cssText=`position:absolute;left:${dx}px;top:${dy}px;width:${it.width}px;height:${it.height}px;border:2px solid rgba(255,0,0,0.8);box-sizing:border-box;pointer-events:none;`;let l=document.createElement("div");l.textContent=String(it.number);let labelTop=dy<14?"2px":"-14px";l.style.cssText=`position:absolute;top:${labelTop};left:-2px;background:rgba(255,0,0,0.9);color:#fff;font:bold 11px/14px monospace;padding:0 4px;border-radius:2px;white-space:nowrap;`,b.appendChild(l),c.appendChild(b)}document.documentElement.appendChild(c)},overlayPayload),overlayInjected=!0,fullPage&&!scopedElement&&boxes.length>0){let scroll=await context.page.evaluate(()=>({x:window.scrollX||0,y:window.scrollY||0}));annotations=boxes.map(a=>({...a,box:{x:a.box.x+scroll.x,y:a.box.y+scroll.y,width:a.box.width,height:a.box.height}}))}else annotations=boxes}}try{let screenshotOptions={type:screenshotType,fullPage,quality};scopedElement&&(screenshotOptions.element=scopedElement);let rawBuffer=await context.page.screenshot(screenshotOptions),scaledBuffer=scaleImageToFitMessage(rawBuffer,screenshotType);return{rawBuffer,image:{data:scaledBuffer,mimeType:`image/${screenshotType}`},annotations,screenshotType}}finally{overlayInjected&&await context.page.evaluate(id=>{let el=document.getElementById(id);el&&el.remove()},ANNOTATION_OVERLAY_ID).catch(()=>{})}}import fs3 from"node:fs/promises";import os2 from"node:os";import path3 from"node:path";import{z as z6}from"zod";var DEFAULT_SCREENSHOT_NAME="screenshot",DEFAULT_SCREENSHOT_TYPE="png",DEFAULT_SCREENSHOT_QUALITY=100,TakeScreenshot=class{name(){return"content_take-screenshot"}description(){return`
266
+ [Output truncated due to size limits]`),{output}}};import os2 from"node:os";import path4 from"node:path";import{z as z8}from"zod";var PageFormat=(PageFormat2=>(PageFormat2.LETTER="Letter",PageFormat2.LEGAL="Legal",PageFormat2.TABLOID="Tabloid",PageFormat2.LEDGER="Ledger",PageFormat2.A0="A0",PageFormat2.A1="A1",PageFormat2.A2="A2",PageFormat2.A3="A3",PageFormat2.A4="A4",PageFormat2.A5="A5",PageFormat2.A6="A6",PageFormat2))(PageFormat||{}),DEFAULT_NAME2="page",DEFAULT_MARGIN="1cm",DEFAULT_FORMAT="A4",DEFAULT_MARGINS={top:DEFAULT_MARGIN,right:DEFAULT_MARGIN,bottom:DEFAULT_MARGIN,left:DEFAULT_MARGIN},SaveAsPdf=class{name(){return"content_save-as-pdf"}description(){return"Saves the current page as a PDF file."}inputSchema(){return{outputPath:z8.string().optional().default(os2.tmpdir()),name:z8.string().optional().default(DEFAULT_NAME2),format:z8.enum(getEnumKeyTuples(PageFormat)).transform(createEnumTransformer(PageFormat)).optional().default(DEFAULT_FORMAT).describe("Page size."),printBackground:z8.boolean().optional().default(!1).describe("Background."),margin:z8.object({top:z8.string().default(DEFAULT_MARGIN),right:z8.string().default(DEFAULT_MARGIN),bottom:z8.string().default(DEFAULT_MARGIN),left:z8.string().default(DEFAULT_MARGIN)}).optional().describe("Margin (e.g. 1cm).")}}outputSchema(){return{filePath:z8.string().describe("Full path of the saved PDF file.")}}async handle(context,args){let filename=`${args.name||DEFAULT_NAME2}-${formattedTimeForFilename()}.pdf`,filePath=path4.resolve(args.outputPath,filename),options={path:filePath,format:args.format,printBackground:args.printBackground,margin:args.margin||DEFAULT_MARGINS};return await context.page.pdf(options),{filePath}}};import os3 from"node:os";import{z as z9}from"zod";var StartRecording=class{name(){return"content_start-recording"}description(){return`
267
+ Starts video recording of the browser page.
268
+ Recording captures all page interactions until <content_stop-recording> is called.
269
+ Uses CDP screencast \u2014 works in all modes (headless, headed, persistent, CDP attach).
270
+ Only supported on Chromium-based browsers.
271
+ `.trim()}inputSchema(){return{outputDir:z9.string().optional().default(os3.tmpdir()).describe("Directory where the video file will be saved."),name:z9.string().optional().describe('Name for the video file (without extension). Defaults to "recording".')}}outputSchema(){return{message:z9.string().describe("Status message.")}}async handle(context,args){return context.isRecording()?{message:"Recording is already in progress."}:(await context.startRecording({outputDir:args.outputDir??os3.tmpdir(),name:args.name}),{message:"Recording started."})}};init_logger();import{z as z10}from"zod";var StopRecording=class{name(){return"content_stop-recording"}description(){return`
272
+ Stops video recording of the browser page and saves the video file.
273
+ Must be called after <content_start-recording>. The video is saved as a WebM file.
274
+ `.trim()}inputSchema(){return{}}outputSchema(){return{filePath:z10.string().optional().describe("Full path of the saved video file.")}}async handle(context,_args){if(!context.isRecording())return debug("stop-recording: isRecording() returned false"),{};let result=await context.stopRecording();if(!result)return debug("stop-recording: stopRecording() returned undefined"),{};let artifacts=[{type:"VIDEO",filePath:result.filePath,mimeType:"video/webm"}];return{filePath:result.filePath,_artifacts:artifacts}}};var REF_ROLES_INTERACTIVE=new Set(["button","link","textbox","searchbox","checkbox","radio","combobox","listbox","menuitem","menuitemcheckbox","menuitemradio","option","slider","spinbutton","switch","tab","tabpanel","treeitem","listitem","row","cell","gridcell","columnheader","rowheader","separator","dialog","alertdialog","clickable","focusable"]);function parseQuotedString(s,i){let q=s[i];if(q!=="'"&&q!=='"')return null;let j=i+1,parts=[];for(;j<s.length;){let c=s[j];if(c==="\\"){j++,j<s.length&&parts.push(s[j]),j++;continue}if(c===q)return{value:parts.join(""),end:j+1};parts.push(c),j++}return null}function parseRegexLiteral(s,i){if(s[i]!=="/")return null;let j=i+1;for(;j<s.length;){if(s[j]==="\\"){j+=2;continue}if(s[j]==="/")break;j++}if(j>=s.length)return null;let pattern=s.slice(i+1,j);j++;let flags="";for(;j<s.length&&/[gimsuy]/.test(s[j])&&!flags.includes(s[j]);)flags+=s[j],j++;try{return{value:new RegExp(pattern,flags),end:j}}catch{return null}}function parseOptionsObject(s,i){for(;i<s.length&&/\s/.test(s[i]);)i++;if(s[i]!=="{")return null;i++;let obj={};for(;i<s.length;){for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;if(s[i]==="}")return{value:obj,end:i+1};let keyMatch=s.slice(i).match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:/);if(!keyMatch)return null;let key=keyMatch[1];for(i+=keyMatch[0].length;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;if(s[i]==="'"||s[i]==='"'){let parsed=parseQuotedString(s,i);if(!parsed)return null;obj[key]=parsed.value,i=parsed.end}else if(s[i]==="/"){let parsed=parseRegexLiteral(s,i);if(!parsed)return null;obj[key]=parsed.value,i=parsed.end}else if(s.slice(i).startsWith("true"))obj[key]=!0,i+=4;else if(s.slice(i).startsWith("false"))obj[key]=!1,i+=5;else{let numMatch=s.slice(i).match(/^-?\d+/);if(numMatch)obj[key]=parseInt(numMatch[0],10),i+=numMatch[0].length;else return null}for(;i<s.length&&/\s/.test(s[i]);)i++;if(i<s.length&&s[i]===","){i++;continue}break}for(;i<s.length&&/\s/.test(s[i]);)i++;return s[i]!=="}"?null:{value:obj,end:i+1}}function parseGetByArgs(inner){let s=inner.trim(),i=0;for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return null;let first;if(s[i]==="'"||s[i]==='"'){let firstParsed=parseQuotedString(s,i);if(!firstParsed)return null;first=firstParsed.value,i=firstParsed.end}else if(s[i]==="/"){let regexParsed=parseRegexLiteral(s,i);if(!regexParsed)return null;first=regexParsed.value,i=regexParsed.end}else return null;for(;i<s.length&&/\s/.test(s[i]);)i++;if(i>=s.length)return{first};if(s[i]!==",")return{first};i++;let optParsed=parseOptionsObject(s,i);return optParsed?{first,options:optParsed.value}:{first}}function tryParsePlaywrightLocatorExpression(page,selectorOrExpression){let match=selectorOrExpression.trim().match(/^\s*(getByRole|getByLabel|getByText|getByPlaceholder|getByAltText|getByTitle|getByTestId)\s*\(\s*([\s\S]*)\s*\)\s*$/);if(!match)return null;let method=match[1],argsInner=match[2],parsed=parseGetByArgs(argsInner);if(!parsed)return null;let{first,options}=parsed;if(method==="getByTestId")return page.getByTestId(first).first();if(method==="getByRole"){if(typeof first!="string")return null;let opts=options??{},roleOpts={};return opts.name!==void 0&&(roleOpts.name=typeof opts.name=="string"||opts.name instanceof RegExp?opts.name:String(opts.name)),opts.exact!==void 0&&(roleOpts.exact=!!opts.exact),opts.checked!==void 0&&(roleOpts.checked=!!opts.checked),opts.disabled!==void 0&&(roleOpts.disabled=!!opts.disabled),opts.expanded!==void 0&&(roleOpts.expanded=!!opts.expanded),opts.includeHidden!==void 0&&(roleOpts.includeHidden=!!opts.includeHidden),opts.level!==void 0&&(roleOpts.level=Number(opts.level)),opts.pressed!==void 0&&(roleOpts.pressed=!!opts.pressed),opts.selected!==void 0&&(roleOpts.selected=!!opts.selected),page.getByRole(first,Object.keys(roleOpts).length>0?roleOpts:void 0).first()}let exactOpts=options&&options.exact!==void 0?{exact:!!options.exact}:void 0;switch(method){case"getByLabel":return page.getByLabel(first,exactOpts).first();case"getByText":return page.getByText(first,exactOpts).first();case"getByPlaceholder":return page.getByPlaceholder(first,exactOpts).first();case"getByAltText":return page.getByAltText(first,exactOpts).first();case"getByTitle":return page.getByTitle(first,exactOpts).first();default:return null}}function parseRef(arg){let s=arg.trim();return s.startsWith("@")?s.slice(1):s.toLowerCase().startsWith("ref=")?s.slice(4):/^e\d+$/.test(s)?s:null}function getLocatorFromRef(page,refMap,ref){let data=refMap[ref];if(!data)return null;if(data.role==="clickable"||data.role==="focusable"){let locator2=page.locator(data.selector);return data.nth!==void 0?locator2.nth(data.nth):locator2.first()}let locator;return data.name!==void 0&&data.name!==""?locator=page.getByRole(data.role,{name:data.name,exact:!0}):locator=page.getByRole(data.role),data.nth!==void 0?locator.nth(data.nth):locator.first()}function assertRefRole(context,selectorOrRef,allowedRoles,actionLabel,hint){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(!ref||!refMap[ref])return;let entry=refMap[ref];if(allowedRoles.has(entry.role))return;let namePart=entry.name?` "${entry.name}"`:"";throw new Error(`Ref ${ref} points to a ${entry.role}${namePart}, which is not valid for ${actionLabel}. ${hint}`)}function resolveSelectorOrRef(context,selectorOrRef){let ref=parseRef(selectorOrRef),refMap=context.getRefMap();if(ref&&Object.keys(refMap).length>0){let locator=getLocatorFromRef(context.page,refMap,ref);if(locator)return locator}let playwrightLocator=tryParsePlaywrightLocatorExpression(context.page,selectorOrRef);return playwrightLocator||context.page.locator(selectorOrRef).first()}import jpegjs from"jpeg-js";import{PNG}from"pngjs";var ANNOTATION_OVERLAY_ID="__browser_devtools_mcp_annotations__",ANNOTATE_BOX_TIMEOUT_MS=2e3,ScreenshotType=(ScreenshotType2=>(ScreenshotType2.PNG="png",ScreenshotType2.JPEG="jpeg",ScreenshotType2))(ScreenshotType||{}),MAX_BUFFER_SIZE=800*1024,MAX_PIXELS=1.15*1024*1024,MAX_LINEAR_SIZE=1568;function scaleImageToSize(image,size){let{data:src,width:w1,height:h1}=image,w2=Math.max(1,Math.floor(size.width)),h2=Math.max(1,Math.floor(size.height));if(w1===w2&&h1===h2)return image;if(w1<=0||h1<=0)throw new Error("Invalid input image");if(size.width<=0||size.height<=0||!isFinite(size.width)||!isFinite(size.height))throw new Error("Invalid output dimensions");let clamp=(v,lo,hi)=>v<lo?lo:v>hi?hi:v,weights=(t,o)=>{let t2=t*t,t3=t2*t;o[0]=-.5*t+1*t2-.5*t3,o[1]=1-2.5*t2+1.5*t3,o[2]=.5*t+2*t2-1.5*t3,o[3]=-.5*t2+.5*t3},srcRowStride=w1*4,dstRowStride=w2*4,xOff=new Int32Array(w2*4),xW=new Float32Array(w2*4),wx=new Float32Array(4),xScale=w1/w2;for(let x=0;x<w2;x++){let sx=(x+.5)*xScale-.5,sxi=Math.floor(sx),t=sx-sxi;weights(t,wx);let b=x*4,i0=clamp(sxi-1,0,w1-1),i1=clamp(sxi+0,0,w1-1),i2=clamp(sxi+1,0,w1-1),i3=clamp(sxi+2,0,w1-1);xOff[b+0]=i0<<2,xOff[b+1]=i1<<2,xOff[b+2]=i2<<2,xOff[b+3]=i3<<2,xW[b+0]=wx[0],xW[b+1]=wx[1],xW[b+2]=wx[2],xW[b+3]=wx[3]}let yRow=new Int32Array(h2*4),yW=new Float32Array(h2*4),wy=new Float32Array(4),yScale=h1/h2;for(let y=0;y<h2;y++){let sy=(y+.5)*yScale-.5,syi=Math.floor(sy),t=sy-syi;weights(t,wy);let b=y*4,j0=clamp(syi-1,0,h1-1),j1=clamp(syi+0,0,h1-1),j2=clamp(syi+1,0,h1-1),j3=clamp(syi+2,0,h1-1);yRow[b+0]=j0*srcRowStride,yRow[b+1]=j1*srcRowStride,yRow[b+2]=j2*srcRowStride,yRow[b+3]=j3*srcRowStride,yW[b+0]=wy[0],yW[b+1]=wy[1],yW[b+2]=wy[2],yW[b+3]=wy[3]}let dst=new Uint8Array(w2*h2*4);for(let y=0;y<h2;y++){let yb=y*4,rb0=yRow[yb+0],rb1=yRow[yb+1],rb2=yRow[yb+2],rb3=yRow[yb+3],wy0=yW[yb+0],wy1=yW[yb+1],wy2=yW[yb+2],wy3=yW[yb+3],dstBase=y*dstRowStride;for(let x=0;x<w2;x++){let xb=x*4,xo0=xOff[xb+0],xo1=xOff[xb+1],xo2=xOff[xb+2],xo3=xOff[xb+3],wx0=xW[xb+0],wx1=xW[xb+1],wx2=xW[xb+2],wx3=xW[xb+3],di=dstBase+(x<<2);for(let c=0;c<4;c++){let r0=src[rb0+xo0+c]*wx0+src[rb0+xo1+c]*wx1+src[rb0+xo2+c]*wx2+src[rb0+xo3+c]*wx3,r1=src[rb1+xo0+c]*wx0+src[rb1+xo1+c]*wx1+src[rb1+xo2+c]*wx2+src[rb1+xo3+c]*wx3,r2=src[rb2+xo0+c]*wx0+src[rb2+xo1+c]*wx1+src[rb2+xo2+c]*wx2+src[rb2+xo3+c]*wx3,r3=src[rb3+xo0+c]*wx0+src[rb3+xo1+c]*wx1+src[rb3+xo2+c]*wx2+src[rb3+xo3+c]*wx3,v=r0*wy0+r1*wy1+r2*wy2+r3*wy3;dst[di+c]=v<0?0:v>255?255:v|0}}}return{data:Buffer.from(dst.buffer),width:w2,height:h2}}function scaleImageToFitMessage(buffer,screenshotType){let image=screenshotType==="png"?PNG.sync.read(buffer):jpegjs.decode(buffer,{maxMemoryUsageInMB:512}),pixels=image.width*image.height,shrink=Math.min(MAX_LINEAR_SIZE/image.width,MAX_LINEAR_SIZE/image.height,Math.sqrt(MAX_PIXELS/pixels));shrink>1&&(shrink=1);let width=image.width*shrink|0,height=image.height*shrink|0,scaledImage=scaleImageToSize(image,{width,height}),quality=screenshotType==="png"?75:70,result=screenshotType==="png"?jpegjs.encode(scaledImage,quality).data:jpegjs.encode(scaledImage,quality).data,iterations=0,MAX_ITERATIONS=5;for(;result.length>MAX_BUFFER_SIZE&&iterations<MAX_ITERATIONS;)quality=Math.max(50,quality-10),quality<=50&&result.length>MAX_BUFFER_SIZE&&(shrink*=.85,width=Math.max(200,image.width*shrink|0),height=Math.max(200,image.height*shrink|0),scaledImage=scaleImageToSize(image,{width,height})),result=jpegjs.encode(scaledImage,quality).data,iterations++;return result}async function captureScreenshot(context,options){let fullPage=options.fullPage??!1,screenshotType=options.type??"png",annotate=options.annotate??!1,annotateContent=options.annotateContent??!1,annotateCursorInteractive=options.annotateCursorInteractive??!1,quality=options.quality??(screenshotType==="png"?void 0:100),overlayInjected=!1,annotations,scopedElement=null;if(options.selector&&(scopedElement=await resolveSelectorOrRef(context,options.selector).elementHandle({timeout:1e4}),!scopedElement))throw new Error(`Element not found: ${options.selector}`);if(annotate){let refMap=context.getRefMap(),wantContent=annotateContent,wantCursorInteractive=annotateCursorInteractive;if(Object.keys(refMap).length===0||wantCursorInteractive||wantContent){let raw=await context.page.locator("body").ariaSnapshot(),{refs}=processAriaTreeWithRefs(raw,{interactiveOnly:!wantContent}),mergedRefs={...refs};if(wantCursorInteractive){let cursorEntries=await findCursorInteractiveElements(context.page,null);if(cursorEntries.length>0){let maxNum=Object.keys(mergedRefs).reduce((m,k)=>Math.max(m,parseInt(k.replace(/^e/,""),10)||0),0);cursorEntries.forEach((entry,i)=>{mergedRefs[`e${maxNum+1+i}`]={role:entry.role,name:entry.name,selector:entry.selector,nth:entry.nth}})}}context.setRefMap(mergedRefs),refMap=mergedRefs}let entries=Object.entries(refMap),allBoxes=[];for(let[ref,data]of entries){let locator=getLocatorFromRef(context.page,refMap,ref);if(!locator)continue;let box=await locator.boundingBox({timeout:ANNOTATE_BOX_TIMEOUT_MS}).catch(()=>null);if(!box||box.width===0||box.height===0)continue;let num=parseInt(ref.replace(/^e/,""),10)||0;allBoxes.push({ref,number:num,role:data.role,name:data.name,box:{x:Math.round(box.x),y:Math.round(box.y),width:Math.round(box.width),height:Math.round(box.height)}})}allBoxes.sort((a,b)=>a.number-b.number);let boxes=allBoxes,overlayBoxesViewport=allBoxes;if(scopedElement){let elementBox=await scopedElement.boundingBox();if(elementBox&&elementBox.width>0&&elementBox.height>0){let overlapping=allBoxes.filter(a=>{let b=a.box;return b.x<elementBox.x+elementBox.width&&b.x+b.width>elementBox.x&&b.y<elementBox.y+elementBox.height&&b.y+b.height>elementBox.y});overlayBoxesViewport=overlapping,boxes=overlapping.map(a=>{let b=a.box,relX=Math.max(0,b.x-elementBox.x),relY=Math.max(0,b.y-elementBox.y),relRight=Math.min(b.x+b.width-elementBox.x,elementBox.width),relBottom=Math.min(b.y+b.height-elementBox.y,elementBox.height);return{...a,box:{x:Math.round(relX),y:Math.round(relY),width:Math.round(Math.max(0,relRight-relX)),height:Math.round(Math.max(0,relBottom-relY))}}})}}if(boxes.length>0){let overlayPayload={data:overlayBoxesViewport.map(a=>({number:a.number,x:a.box.x,y:a.box.y,width:a.box.width,height:a.box.height})),id:ANNOTATION_OVERLAY_ID};if(await context.page.evaluate(({data,id})=>{let sx=window.scrollX||0,sy=window.scrollY||0,c=document.createElement("div");c.id=id,c.style.cssText="position:absolute;top:0;left:0;width:0;height:0;pointer-events:none;z-index:2147483647;";for(let it of data){let dx=it.x+sx,dy=it.y+sy,b=document.createElement("div");b.style.cssText=`position:absolute;left:${dx}px;top:${dy}px;width:${it.width}px;height:${it.height}px;border:2px solid rgba(255,0,0,0.8);box-sizing:border-box;pointer-events:none;`;let l=document.createElement("div");l.textContent=String(it.number);let labelTop=dy<14?"2px":"-14px";l.style.cssText=`position:absolute;top:${labelTop};left:-2px;background:rgba(255,0,0,0.9);color:#fff;font:bold 11px/14px monospace;padding:0 4px;border-radius:2px;white-space:nowrap;`,b.appendChild(l),c.appendChild(b)}document.documentElement.appendChild(c)},overlayPayload),overlayInjected=!0,fullPage&&!scopedElement&&boxes.length>0){let scroll=await context.page.evaluate(()=>({x:window.scrollX||0,y:window.scrollY||0}));annotations=boxes.map(a=>({...a,box:{x:a.box.x+scroll.x,y:a.box.y+scroll.y,width:a.box.width,height:a.box.height}}))}else annotations=boxes}}try{let screenshotOptions={type:screenshotType,fullPage,quality};scopedElement&&(screenshotOptions.element=scopedElement);let rawBuffer=await context.page.screenshot(screenshotOptions),scaledBuffer=scaleImageToFitMessage(rawBuffer,screenshotType);return{rawBuffer,image:{data:scaledBuffer,mimeType:`image/${screenshotType}`},annotations,screenshotType}}finally{overlayInjected&&await context.page.evaluate(id=>{let el=document.getElementById(id);el&&el.remove()},ANNOTATION_OVERLAY_ID).catch(()=>{})}}import fs5 from"node:fs/promises";import os4 from"node:os";import path5 from"node:path";import{z as z11}from"zod";var DEFAULT_SCREENSHOT_NAME="screenshot",DEFAULT_SCREENSHOT_TYPE="png",DEFAULT_SCREENSHOT_QUALITY=100,TakeScreenshot=class{name(){return"content_take-screenshot"}description(){return`
262
275
  Takes a screenshot of the current page or a specific element.
263
276
  Do NOT use for page structure\u2014use ARIA/AX snapshots instead.
264
277
  Use only for visual verification (design check, visual bug, contrast, layout).
265
278
  Screenshot is saved to disk; use includeBase64 only when the file cannot be read from the returned path (e.g. remote, container).
266
- `.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=`(
279
+ `.trim()}inputSchema(){return{outputPath:z11.string().optional().default(os4.tmpdir()),name:z11.string().optional().default(DEFAULT_SCREENSHOT_NAME),selector:z11.string().optional().describe("Selector/ref; omit=viewport."),fullPage:z11.boolean().optional().default(!1),type:z11.enum(getEnumKeyTuples(ScreenshotType)).transform(createEnumTransformer(ScreenshotType)).optional().default(DEFAULT_SCREENSHOT_TYPE),quality:z11.number().int().min(0).max(DEFAULT_SCREENSHOT_QUALITY).optional().describe("JPEG quality 0\u2013100."),includeBase64:z11.boolean().optional().default(!1).describe("Base64 fallback."),annotate:z11.boolean().optional().default(!1).describe("Overlay ARIA ref labels."),annotateContent:z11.boolean().optional().default(!1).describe("Annotate headings."),annotateCursorInteractive:z11.boolean().optional().default(!1).describe("Annotate cursor:pointer elements.")}}outputSchema(){return{filePath:z11.string().describe("Full path of the saved screenshot file."),image:z11.object({data:z11.any().describe("Base64-encoded image data."),mimeType:z11.string().describe("MIME type of the image.")}).optional().describe('Image data included only when "includeBase64" input parameter is set to true.'),annotations:z11.array(z11.object({ref:z11.string(),number:z11.number(),role:z11.string(),name:z11.string().optional(),box:z11.object({x:z11.number(),y:z11.number(),width:z11.number(),height:z11.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=path5.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 fs5.writeFile(filePath,result.rawBuffer);let output={filePath,_artifacts:[{type:"IMAGE",filePath,mimeType:result.image.mimeType}],...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 StartRecording,new StopRecording,new TakeScreenshot];import{z as z12}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=`(
267
280
  ${probe.logExpression}
268
281
  )`,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`
269
282
  Returns the current debugging status including:
@@ -272,14 +285,14 @@ Returns the current debugging status including:
272
285
  - Exceptionpoint state
273
286
  - Count of tracepoints, logpoints, and watches
274
287
  - Snapshot statistics
275
- `}inputSchema(){return{}}outputSchema(){return{enabled:z7.boolean().describe("Whether debugging is enabled"),hasSourceMaps:z7.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:z7.string().describe("Exceptionpoint state (none, uncaught, all)"),tracepointCount:z7.number().describe("Number of tracepoints"),logpointCount:z7.number().describe("Number of logpoints"),watchExpressionCount:z7.number().describe("Number of watch expressions"),snapshotStats:z7.object({totalSnapshots:z7.number(),snapshotsByProbe:z7.record(z7.number()),oldestTimestamp:z7.number().optional(),newestTimestamp:z7.number().optional(),averageCaptureTimeMs:z7.number()}).nullable().describe("Snapshot statistics")}}async handle(context,_args){if(!isDebuggingEnabled2(context.browserContext))return{enabled:!1,hasSourceMaps:!1,exceptionBreakpoint:"none",tracepointCount:0,logpointCount:0,watchExpressionCount:0,snapshotStats:null};let probes=listProbes2(context.browserContext),tracepointCount=probes.filter(p=>p.kind==="tracepoint").length,logpointCount=probes.filter(p=>p.kind==="logpoint").length;return{enabled:!0,hasSourceMaps:hasSourceMaps2(context.browserContext),exceptionBreakpoint:getExceptionBreakpoint2(context.browserContext),tracepointCount,logpointCount,watchExpressionCount:listWatchExpressions2(context.browserContext).length,snapshotStats:getSnapshotStats2(context.browserContext)}}};import{z as z8}from"zod";var ResolveSourceLocation=class{name(){return"debug_resolve-source-location"}description(){return`
288
+ `}inputSchema(){return{}}outputSchema(){return{enabled:z12.boolean().describe("Whether debugging is enabled"),hasSourceMaps:z12.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:z12.string().describe("Exceptionpoint state (none, uncaught, all)"),tracepointCount:z12.number().describe("Number of tracepoints"),logpointCount:z12.number().describe("Number of logpoints"),watchExpressionCount:z12.number().describe("Number of watch expressions"),snapshotStats:z12.object({totalSnapshots:z12.number(),snapshotsByProbe:z12.record(z12.number()),oldestTimestamp:z12.number().optional(),newestTimestamp:z12.number().optional(),averageCaptureTimeMs:z12.number()}).nullable().describe("Snapshot statistics")}}async handle(context,_args){if(!isDebuggingEnabled2(context.browserContext))return{enabled:!1,hasSourceMaps:!1,exceptionBreakpoint:"none",tracepointCount:0,logpointCount:0,watchExpressionCount:0,snapshotStats:null};let probes=listProbes2(context.browserContext),tracepointCount=probes.filter(p=>p.kind==="tracepoint").length,logpointCount=probes.filter(p=>p.kind==="logpoint").length;return{enabled:!0,hasSourceMaps:hasSourceMaps2(context.browserContext),exceptionBreakpoint:getExceptionBreakpoint2(context.browserContext),tracepointCount,logpointCount,watchExpressionCount:listWatchExpressions2(context.browserContext).length,snapshotStats:getSnapshotStats2(context.browserContext)}}};import{z as z13}from"zod";var ResolveSourceLocation=class{name(){return"debug_resolve-source-location"}description(){return`
276
289
  Resolves a generated/bundled code location to its original source via source maps.
277
290
  Useful for translating minified stack traces or bundle line numbers to original TypeScript/JavaScript source.
278
291
 
279
292
  Requires a page with debugging context (debugging is auto-enabled on first use).
280
293
  Input: generated script URL, line, column (1-based).
281
294
  Output: original source path, line, column when a source map is available.
282
- `}inputSchema(){return{url:z8.string().describe("Generated script URL (bundle)."),line:z8.number().int().positive().describe("1-based line in generated code."),column:z8.number().int().nonnegative().optional().describe("1-based column; default 1.")}}outputSchema(){return{resolved:z8.boolean().describe("Whether the location was resolved to original source"),source:z8.string().optional().describe("Original source file path"),line:z8.number().optional().describe("Line number in original source (1-based)"),column:z8.number().optional().describe("Column number in original source (1-based)"),name:z8.string().optional().describe("Original identifier name if available")}}async handle(context,args){let resolved=await resolveSourceLocation2(context.browserContext,context.page,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 z9}from"zod";var PutTracepoint=class{name(){return"debug_put-tracepoint"}description(){return`
295
+ `}inputSchema(){return{url:z13.string().describe("Generated script URL (bundle)."),line:z13.number().int().positive().describe("1-based line in generated code."),column:z13.number().int().nonnegative().optional().describe("1-based column; default 1.")}}outputSchema(){return{resolved:z13.boolean().describe("Whether the location was resolved to original source"),source:z13.string().optional().describe("Original source file path"),line:z13.number().optional().describe("Line number in original source (1-based)"),column:z13.number().optional().describe("Column number in original source (1-based)"),name:z13.string().optional().describe("Original identifier name if available")}}async handle(context,args){let resolved=await resolveSourceLocation2(context.browserContext,context.page,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 z14}from"zod";var PutTracepoint=class{name(){return"debug_put-tracepoint"}description(){return`
283
296
  Puts a non-blocking tracepoint at the specified location.
284
297
  When hit, a snapshot of the call stack and local variables is captured
285
298
  automatically without pausing execution.
@@ -293,7 +306,7 @@ DO NOT escape characters yourself (e.g., don't use "app\\.js").
293
306
 
294
307
  Returns resolvedLocations: number of scripts where the tracepoint was set.
295
308
  If 0, the pattern didn't match any loaded scripts.
296
- `}inputSchema(){return{urlPattern:z9.string().describe("Glob pattern for script URL (e.g. **/app.js)."),lineNumber:z9.number().int().positive().describe("1-based line."),columnNumber:z9.number().int().nonnegative().optional().describe("1-based column."),condition:z9.string().optional().describe("Only trigger when this expression is true."),hitCondition:z9.string().optional().describe("Hit count (e.g. == 5, >= 10, % 10 == 0).")}}outputSchema(){return{id:z9.string().describe("Tracepoint ID"),urlPattern:z9.string().describe("URL pattern"),lineNumber:z9.number().describe("Line number"),columnNumber:z9.number().optional().describe("Column number"),condition:z9.string().optional().describe("Condition expression"),hitCondition:z9.string().optional().describe("Hit count condition"),resolvedLocations:z9.number().describe("Number of locations where tracepoint was resolved")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let probe=await createProbe2(context.browserContext,{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 z10}from"zod";var REMOVE_TYPES=["tracepoint","logpoint","watch"],RemoveProbe=class{name(){return"debug_remove-probe"}description(){return"\nRemoves a tracepoint, logpoint, or watch expression by ID.\n`type`: `tracepoint`, `logpoint`, or `watch`. `id`: the probe or watch ID (from list-probes).\n ".trim()}inputSchema(){return{type:z10.enum(REMOVE_TYPES),id:z10.string().describe("Probe or watch ID from list-probes.")}}outputSchema(){return{removed:z10.boolean().describe("Whether the probe or watch was removed"),message:z10.string().describe("Status message")}}async handle(context,args){if(!isDebuggingEnabled2(context.browserContext))return{removed:!1,message:"Debugging is not active"};if(args.type==="watch"){let removed2=removeWatchExpression2(context.browserContext,args.id);return{removed:removed2,message:removed2?`Watch expression ${args.id} removed`:`Watch expression ${args.id} not found`}}let kind=args.type==="tracepoint"?"tracepoint":"logpoint",probe=getProbe(context.browserContext,args.id),label=args.type==="tracepoint"?"Tracepoint":"Logpoint";if(!probe||probe.kind!==kind)return{removed:!1,message:`${label} ${args.id} not found`};let removed=await removeProbe2(context.browserContext,args.id);return{removed,message:removed?`${label} ${args.id} removed`:`Failed to remove ${args.type}`}}};import{z as z11}from"zod";var LIST_TYPES=["tracepoint","logpoint","watch"],tracepointItem=z11.object({id:z11.string(),urlPattern:z11.string(),lineNumber:z11.number(),columnNumber:z11.number().optional(),condition:z11.string().optional(),hitCondition:z11.string().optional(),enabled:z11.boolean(),resolvedLocations:z11.number(),hitCount:z11.number(),lastHitAt:z11.number().optional()}),logpointItem=z11.object({id:z11.string(),urlPattern:z11.string(),lineNumber:z11.number(),logExpression:z11.string(),columnNumber:z11.number().optional(),condition:z11.string().optional(),hitCondition:z11.string().optional(),enabled:z11.boolean(),resolvedLocations:z11.number(),hitCount:z11.number(),lastHitAt:z11.number().optional()}),watchItem=z11.object({id:z11.string(),expression:z11.string(),createdAt:z11.number()}),ListProbes=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:z11.array(z11.enum(LIST_TYPES)).optional().describe("List only these types; omit for all.")}}outputSchema(){return{tracepoints:z11.array(tracepointItem).describe("Tracepoints"),logpoints:z11.array(logpointItem).describe("Logpoints"),watches:z11.array(watchItem).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"),tracepoints=[],logpoints=[],watches=[];if(!isDebuggingEnabled2(context.browserContext))return{tracepoints,logpoints,watches};if(listTracepoints||listLogpoints){let probes=listProbes2(context.browserContext);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,enabled:p.enabled,resolvedLocations:p.resolvedLocations,hitCount:p.hitCount,lastHitAt:p.lastHitAt})):[],logpoints=listLogpoints?probes.filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression??"",columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,enabled:p.enabled,resolvedLocations:p.resolvedLocations,hitCount:p.hitCount,lastHitAt:p.lastHitAt})):[]}return listWatches&&(watches=listWatchExpressions2(context.browserContext).map(w=>({id:w.id,expression:w.expression,createdAt:w.createdAt}))),{tracepoints,logpoints,watches}}};import{z as z12}from"zod";var PROBE_TYPES=["tracepoint","logpoint","watches"],ClearProbes=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:z12.array(z12.enum(PROBE_TYPES)).optional().describe("Which probe types to clear: tracepoint, logpoint, watches. If omitted or empty, all are cleared.")}}outputSchema(){return{tracepointsCleared:z12.number().describe("Number of tracepoints cleared"),logpointsCleared:z12.number().describe("Number of logpoints cleared"),watchesCleared:z12.number().describe("Number of watch expressions cleared"),message:z12.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(!isDebuggingEnabled2(context.browserContext))return{tracepointsCleared:0,logpointsCleared:0,watchesCleared:0,message:"Debugging not enabled; nothing to clear"};if(clearTracepoints||clearLogpoints){let probes=listProbes2(context.browserContext);for(let p of probes)p.kind==="tracepoint"&&clearTracepoints?await removeProbe2(context.browserContext,p.id)&&tracepointsCleared++:p.kind==="logpoint"&&clearLogpoints&&await removeProbe2(context.browserContext,p.id)&&logpointsCleared++}clearWatches&&(watchesCleared=clearWatchExpressions2(context.browserContext));let parts=[tracepointsCleared&&`${tracepointsCleared} tracepoint(s)`,logpointsCleared&&`${logpointsCleared} logpoint(s)`,watchesCleared&&`${watchesCleared} watch(es)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointsCleared,logpointsCleared,watchesCleared,message}}};import{z as z13}from"zod";var PutLogpoint=class{name(){return"debug_put-logpoint"}description(){return`
309
+ `}inputSchema(){return{urlPattern:z14.string().describe("Glob pattern for script URL (e.g. **/app.js)."),lineNumber:z14.number().int().positive().describe("1-based line."),columnNumber:z14.number().int().nonnegative().optional().describe("1-based column."),condition:z14.string().optional().describe("Only trigger when this expression is true."),hitCondition:z14.string().optional().describe("Hit count (e.g. == 5, >= 10, % 10 == 0).")}}outputSchema(){return{id:z14.string().describe("Tracepoint ID"),urlPattern:z14.string().describe("URL pattern"),lineNumber:z14.number().describe("Line number"),columnNumber:z14.number().optional().describe("Column number"),condition:z14.string().optional().describe("Condition expression"),hitCondition:z14.string().optional().describe("Hit count condition"),resolvedLocations:z14.number().describe("Number of locations where tracepoint was resolved")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let probe=await createProbe2(context.browserContext,{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 z15}from"zod";var REMOVE_TYPES=["tracepoint","logpoint","watch"],RemoveProbe=class{name(){return"debug_remove-probe"}description(){return"\nRemoves a tracepoint, logpoint, or watch expression by ID.\n`type`: `tracepoint`, `logpoint`, or `watch`. `id`: the probe or watch ID (from list-probes).\n ".trim()}inputSchema(){return{type:z15.enum(REMOVE_TYPES),id:z15.string().describe("Probe or watch ID from list-probes.")}}outputSchema(){return{removed:z15.boolean().describe("Whether the probe or watch was removed"),message:z15.string().describe("Status message")}}async handle(context,args){if(!isDebuggingEnabled2(context.browserContext))return{removed:!1,message:"Debugging is not active"};if(args.type==="watch"){let removed2=removeWatchExpression2(context.browserContext,args.id);return{removed:removed2,message:removed2?`Watch expression ${args.id} removed`:`Watch expression ${args.id} not found`}}let kind=args.type==="tracepoint"?"tracepoint":"logpoint",probe=getProbe(context.browserContext,args.id),label=args.type==="tracepoint"?"Tracepoint":"Logpoint";if(!probe||probe.kind!==kind)return{removed:!1,message:`${label} ${args.id} not found`};let removed=await removeProbe2(context.browserContext,args.id);return{removed,message:removed?`${label} ${args.id} removed`:`Failed to remove ${args.type}`}}};import{z as z16}from"zod";var LIST_TYPES=["tracepoint","logpoint","watch"],tracepointItem=z16.object({id:z16.string(),urlPattern:z16.string(),lineNumber:z16.number(),columnNumber:z16.number().optional(),condition:z16.string().optional(),hitCondition:z16.string().optional(),enabled:z16.boolean(),resolvedLocations:z16.number(),hitCount:z16.number(),lastHitAt:z16.number().optional()}),logpointItem=z16.object({id:z16.string(),urlPattern:z16.string(),lineNumber:z16.number(),logExpression:z16.string(),columnNumber:z16.number().optional(),condition:z16.string().optional(),hitCondition:z16.string().optional(),enabled:z16.boolean(),resolvedLocations:z16.number(),hitCount:z16.number(),lastHitAt:z16.number().optional()}),watchItem=z16.object({id:z16.string(),expression:z16.string(),createdAt:z16.number()}),ListProbes=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:z16.array(z16.enum(LIST_TYPES)).optional().describe("List only these types; omit for all.")}}outputSchema(){return{tracepoints:z16.array(tracepointItem).describe("Tracepoints"),logpoints:z16.array(logpointItem).describe("Logpoints"),watches:z16.array(watchItem).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"),tracepoints=[],logpoints=[],watches=[];if(!isDebuggingEnabled2(context.browserContext))return{tracepoints,logpoints,watches};if(listTracepoints||listLogpoints){let probes=listProbes2(context.browserContext);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,enabled:p.enabled,resolvedLocations:p.resolvedLocations,hitCount:p.hitCount,lastHitAt:p.lastHitAt})):[],logpoints=listLogpoints?probes.filter(p=>p.kind==="logpoint").map(p=>({id:p.id,urlPattern:p.urlPattern,lineNumber:p.lineNumber,logExpression:p.logExpression??"",columnNumber:p.columnNumber,condition:p.condition,hitCondition:p.hitCondition,enabled:p.enabled,resolvedLocations:p.resolvedLocations,hitCount:p.hitCount,lastHitAt:p.lastHitAt})):[]}return listWatches&&(watches=listWatchExpressions2(context.browserContext).map(w=>({id:w.id,expression:w.expression,createdAt:w.createdAt}))),{tracepoints,logpoints,watches}}};import{z as z17}from"zod";var PROBE_TYPES=["tracepoint","logpoint","watches"],ClearProbes=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:z17.array(z17.enum(PROBE_TYPES)).optional().describe("Which probe types to clear: tracepoint, logpoint, watches. If omitted or empty, all are cleared.")}}outputSchema(){return{tracepointsCleared:z17.number().describe("Number of tracepoints cleared"),logpointsCleared:z17.number().describe("Number of logpoints cleared"),watchesCleared:z17.number().describe("Number of watch expressions cleared"),message:z17.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(!isDebuggingEnabled2(context.browserContext))return{tracepointsCleared:0,logpointsCleared:0,watchesCleared:0,message:"Debugging not enabled; nothing to clear"};if(clearTracepoints||clearLogpoints){let probes=listProbes2(context.browserContext);for(let p of probes)p.kind==="tracepoint"&&clearTracepoints?await removeProbe2(context.browserContext,p.id)&&tracepointsCleared++:p.kind==="logpoint"&&clearLogpoints&&await removeProbe2(context.browserContext,p.id)&&logpointsCleared++}clearWatches&&(watchesCleared=clearWatchExpressions2(context.browserContext));let parts=[tracepointsCleared&&`${tracepointsCleared} tracepoint(s)`,logpointsCleared&&`${logpointsCleared} logpoint(s)`,watchesCleared&&`${watchesCleared} watch(es)`].filter(Boolean),message=parts.length>0?`Cleared: ${parts.join(", ")}`:"Nothing to clear";return{tracepointsCleared,logpointsCleared,watchesCleared,message}}};import{z as z18}from"zod";var PutLogpoint=class{name(){return"debug_put-logpoint"}description(){return`
297
310
  Puts a logpoint at the specified location.
298
311
  When the logpoint is hit, the logExpression is evaluated and the result
299
312
  is captured in the snapshot's logResult field.
@@ -306,20 +319,20 @@ urlPattern matches script URLs (e.g., "app.js"). Auto-escaped, do not add backsl
306
319
  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(...).
307
320
 
308
321
  Returns resolvedLocations: 0 means pattern didn't match any loaded scripts.
309
- `}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`
322
+ `}inputSchema(){return{urlPattern:z18.string().describe("Glob pattern for script URL (e.g. **/app.js)."),lineNumber:z18.number().int().positive().describe("1-based line in script."),logExpression:z18.string().describe("JS expression to evaluate and log (e.g. user.name, JSON.stringify({ a, b }), or { x, y })."),columnNumber:z18.number().int().positive().optional().describe("1-based column."),condition:z18.string().optional().describe("Only log when this expression is true."),hitCondition:z18.string().optional().describe("Hit-count condition (e.g. > 5).")}}outputSchema(){return{id:z18.string().describe("Debug point ID"),urlPattern:z18.string().describe("URL pattern"),lineNumber:z18.number().describe("Line number"),logExpression:z18.string().describe("Log expression"),columnNumber:z18.number().optional().describe("Column number"),condition:z18.string().optional().describe("Condition expression"),hitCondition:z18.string().optional().describe("Hit count condition"),resolvedLocations:z18.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 z19}from"zod";var PutExceptionpoint=class{name(){return"debug_put-exceptionpoint"}description(){return`
310
323
  Sets the exception tracepoint state:
311
324
  - "none": Don't capture on exceptions
312
325
  - "uncaught": Capture only on uncaught exceptions
313
326
  - "all": Capture on all exceptions (caught and uncaught)
314
327
 
315
328
  When an exception occurs, a snapshot is captured with exception details.
316
- `}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`
329
+ `}inputSchema(){return{state:z19.enum(["none","uncaught","all"])}}outputSchema(){return{previousState:z19.string().describe("Previous state"),currentState:z19.string().describe("Current state"),message:z19.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 z21}from"zod";import{z as z20}from"zod";var OriginalLocationSchema=z20.object({source:z20.string().describe("Original source file path"),line:z20.number().describe("1-based line number"),column:z20.number().optional().describe("1-based column number"),name:z20.string().optional().describe("Original identifier name")}),ScopeVariableSchema=z20.object({name:z20.string().describe("Variable name"),value:z20.any().describe("Variable value"),type:z20.string().describe("Variable type")}),ScopeSnapshotSchema=z20.object({type:z20.string().describe("Scope type (global, local, closure, etc.)"),name:z20.string().optional().describe("Scope name"),variables:z20.array(ScopeVariableSchema).describe("Variables in this scope")}),CallFrameSchema=z20.object({functionName:z20.string().describe("Function name"),url:z20.string().describe("Script URL"),lineNumber:z20.number().describe("1-based line number"),columnNumber:z20.number().optional().describe("1-based column number"),scriptId:z20.string().describe("V8 script ID"),scopes:z20.array(ScopeSnapshotSchema).describe("Variable scopes"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncCallFrameSchema=z20.object({functionName:z20.string().describe("Function name"),url:z20.string().describe("Script URL"),lineNumber:z20.number().describe("1-based line number"),columnNumber:z20.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location")}),AsyncStackSegmentSchema=z20.object({description:z20.string().optional().describe("Async boundary (Promise.then, setTimeout, etc.)"),callFrames:z20.array(AsyncCallFrameSchema).describe("Frames in this segment")}),AsyncStackTraceSchema=z20.object({segments:z20.array(AsyncStackSegmentSchema).describe("Chain of async segments")}),SnapshotBaseSchema=z20.object({id:z20.string().describe("Snapshot ID"),probeId:z20.string().describe("Probe ID that triggered this snapshot"),timestamp:z20.number().describe("Unix timestamp (ms)"),sequenceNumber:z20.number().describe("Monotonic sequence number for ordering"),url:z20.string().describe("Script URL where snapshot was taken"),lineNumber:z20.number().describe("1-based line number"),columnNumber:z20.number().optional().describe("1-based column number"),originalLocation:OriginalLocationSchema.optional().describe("Original source location"),captureTimeMs:z20.number().describe("Time taken to capture snapshot (ms)")}),TracepointSnapshotSchema=SnapshotBaseSchema.extend({callStack:z20.array(CallFrameSchema).describe("Call stack with local variables"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z20.record(z20.any()).optional().describe("Watch expression results")}),LogpointSnapshotSchema=SnapshotBaseSchema.extend({logResult:z20.any().optional().describe("Result of log expression evaluation")}),ExceptionInfoSchema=z20.object({type:z20.enum(["exception","promiseRejection"]).describe("Exception type"),message:z20.string().describe("Exception message"),name:z20.string().optional().describe("Exception name/class"),stack:z20.string().optional().describe("Stack trace string")}),ExceptionpointSnapshotSchema=SnapshotBaseSchema.extend({exception:ExceptionInfoSchema.optional().describe("Exception information"),callStack:z20.array(CallFrameSchema).describe("Call stack at exception"),asyncStackTrace:AsyncStackTraceSchema.optional().describe("Async stack trace"),watchResults:z20.record(z20.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`
317
330
  Retrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.
318
331
  Optional \`types\`: array of \`tracepoint\`, \`logpoint\`, \`exceptionpoint\`. If omitted or empty, returns all.
319
332
  Response fields: \`tracepointSnapshots\`, \`logpointSnapshots\`, \`exceptionpointSnapshots\`.
320
333
  Optional \`probeId\` filters tracepoint or logpoint snapshots; \`fromSequence\` and \`limit\` apply per type.
321
334
  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.
322
- `.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`
335
+ `.trim()}inputSchema(){return{types:z21.array(z21.enum(SNAPSHOT_TYPES)).optional().describe("Return only these types; omit for all."),probeId:z21.string().optional().describe("Filter by this probe ID (tracepoint/logpoint)."),fromSequence:z21.number().int().nonnegative().optional().describe("Snapshots with sequence > this (polling)."),limit:z21.number().int().positive().optional(),maxCallStackDepth:z21.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxCallStackDepth).describe(`Max call stack frames per snapshot. Default ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth}.`),includeScopes:z21.array(z21.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:z21.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxVariablesPerScope).describe(`Max variables per scope. Default ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}.`)}}outputSchema(){return{tracepointSnapshots:z21.array(TracepointSnapshotSchema).describe("Tracepoint snapshots"),logpointSnapshots:z21.array(LogpointSnapshotSchema).describe("Logpoint snapshots"),exceptionpointSnapshots:z21.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 z22}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:z22.array(z22.enum(SNAPSHOT_TYPES2)).optional().describe("Clear only these types; omit for all."),probeId:z22.string().optional().describe("Clear only this probe (tracepoint/logpoint).")}}outputSchema(){return{tracepointCleared:z22.number().describe("Tracepoint snapshots cleared"),logpointCleared:z22.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z22.number().describe("Exceptionpoint snapshots cleared"),message:z22.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 z23}from"zod";var AddWatch=class{name(){return"debug_add-watch"}description(){return`
323
336
  Adds a watch expression to be evaluated at every breakpoint hit.
324
337
  Watch expression results are included in the snapshot's watchResults field.
325
338
 
@@ -330,7 +343,7 @@ Examples:
330
343
  - "JSON.stringify(config)"
331
344
 
332
345
  Watch expressions are evaluated in the context of the paused frame.
333
- `}inputSchema(){return{expression:z18.string().describe("Expression evaluated at every tracepoint hit (e.g. user.name).")}}outputSchema(){return{id:z18.string().describe("Watch expression ID"),expression:z18.string().describe("The watch expression"),message:z18.string().describe("Status message")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let watch=addWatchExpression2(context.browserContext,args.expression);return{id:watch.id,expression:watch.expression,message:`Watch expression added: ${args.expression}`}}};var tools3=[new Status,new ResolveSourceLocation,new PutTracepoint,new RemoveProbe,new ListProbes,new ClearProbes,new PutLogpoint,new PutExceptionpoint,new GetProbeSnapshots,new ClearProbeSnapshots,new AddWatch];import ssim from"ssim.js";var _sharpPromise=null;async function getSharp(){return _sharpPromise||(_sharpPromise=import("sharp").then(m=>m.default??m)),_sharpPromise}var DEFAULT_GRAYSCALE=!1,DEFAULT_BLUR=10;function _clamp01(v){return Number.isFinite(v)?Math.max(0,Math.min(1,v)):0}async function _loadNormalized(input,canvasWidth,canvasHeight,mode){let sharp=await getSharp(),img;if(mode==="semantic"){img=sharp(input).resize(canvasWidth,canvasHeight,{fit:"cover",position:"centre"});let w=Math.max(1,Math.floor(canvasWidth/2)),h=Math.max(1,Math.floor(canvasHeight/2));img=img.resize(w,h,{fit:"cover",position:"centre"}).grayscale(DEFAULT_GRAYSCALE).blur(DEFAULT_BLUR)}else img=sharp(input).resize(canvasWidth,canvasHeight,{fit:"cover",position:"centre"});let out=await img.ensureAlpha().raw().toBuffer({resolveWithObject:!0});return{data:new Uint8ClampedArray(out.data.buffer,out.data.byteOffset,out.data.byteLength),width:out.info.width,height:out.info.height}}function _computeSsim(a,b){let details=ssim({data:a.data,width:a.width,height:a.height},{data:b.data,width:b.width,height:b.height}),rawScore=Number(details.mssim??details.ssim??0);return _clamp01(rawScore)}async function compare(page,figma,options){let mode=options?.mode??"semantic",canvasWidth=options?.canvasWidth,canvasHeight=options?.canvasHeight;if(typeof canvasWidth!="number"||!Number.isFinite(canvasWidth)||canvasWidth<=0||typeof canvasHeight!="number"||!Number.isFinite(canvasHeight)||canvasHeight<=0){let figmaMeta=await(await getSharp())(figma.image).metadata();if(canvasWidth=figmaMeta.width??0,canvasHeight=figmaMeta.height??0,canvasWidth<=0||canvasHeight<=0)throw new Error("Failed to read Figma image dimensions.")}let figmaImg=await _loadNormalized(figma.image,canvasWidth,canvasHeight,mode),pageImg=await _loadNormalized(page.image,canvasWidth,canvasHeight,mode);return{score:_computeSsim(figmaImg,pageImg)}}function dot(a,b){let n=Math.min(a.length,b.length),s=0;for(let i=0;i<n;i++)s+=a[i]*b[i];return s}function norm(a){let s=0;for(let i=0;i<a.length;i++){let x=a[i];s+=x*x}return Math.sqrt(s)}function l2Normalize(v){let n=norm(v);if(n===0)return v.slice();let out=new Array(v.length);for(let i=0;i<v.length;i++)out[i]=v[i]/n;return out}function cosineSimilarity(a,b,normalize){if(normalize){let na=l2Normalize(a),nb=l2Normalize(b);return dot(na,nb)}let denom=norm(a)*norm(b);return denom===0?0:dot(a,b)/denom}import{BedrockRuntimeClient,InvokeModelCommand}from"@aws-sdk/client-bedrock-runtime";import{fromIni}from"@aws-sdk/credential-providers";var _sharpPromise2=null;async function getSharp2(){return _sharpPromise2||(_sharpPromise2=import("sharp").then(m=>m.default??m)),_sharpPromise2}var DEFAULT_MAX_DIMENSION=1024,DEFAULT_JPEG_QUALITY=90,DEFAULT_AMAZON_BEDROCK_TITAN_OUTPUT_EMBEDDING_LENGTH=1024;async function _prepareImage(buf,imageType,opt){let img=(await getSharp2())(buf),maxDim=opt?.maxDim||DEFAULT_MAX_DIMENSION;if(img=img.resize({width:maxDim,height:maxDim,fit:"inside",withoutEnlargement:!0}),imageType==="png")return await img.png().toBuffer();let jpegQuality=opt?.jpegQuality||DEFAULT_JPEG_QUALITY;return await img.jpeg({quality:jpegQuality}).toBuffer()}var SUPPORTED_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_IDS=new Set(["amazon.titan-embed-image-v1"]),DEFAULT_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID="amazon.titan-embed-image-v1",_bedrockClient;function _isAwsBedrockActive(){return AMAZON_BEDROCK_ENABLE&&!!AWS_REGION}function _getOrCreateBedrockClient(){if(_bedrockClient)return _bedrockClient;let region=AWS_REGION;if(!region)return;let profile=AWS_PROFILE;return profile?(_bedrockClient=new BedrockRuntimeClient({region,credentials:fromIni({profile})}),_bedrockClient):(_bedrockClient=new BedrockRuntimeClient({region}),_bedrockClient)}async function _embedImageWithAmazonBedrockTitan(ss,client,opt,modelId){let bodyObj={inputImage:(await _prepareImage(ss.image,ss.type,opt)).toString("base64"),embeddingConfig:{outputEmbeddingLength:DEFAULT_AMAZON_BEDROCK_TITAN_OUTPUT_EMBEDDING_LENGTH}},cmd=new InvokeModelCommand({modelId,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(bodyObj),"utf-8")}),resp=await client.send(cmd),raw=resp?.body instanceof Uint8Array?resp.body:new Uint8Array(resp?.body??[]),text=Buffer.from(raw).toString("utf-8"),parsed;try{parsed=text?JSON.parse(text):{}}catch{throw new Error(`Amazon Bedrock Titan returned non-JSON response for embeddings: ${text.slice(0,300)}`)}let emb=parsed?.embedding??parsed?.embeddings?.[0]??parsed?.outputEmbedding??parsed?.vector;if(!Array.isArray(emb)||emb.length===0||typeof emb[0]!="number")throw new Error(`Unexpected Amazon Bedrock Titan image embedding response format: ${text.slice(0,500)}`);return emb}async function _embedImageWithAmazonBedrock(ss,opt,client){let modelId=opt?.modelId??AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID??DEFAULT_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock image embedding model id: ${modelId}`);return await _embedImageWithAmazonBedrockTitan(ss,client,opt,modelId)}async function _embedImage(ss,opt){if(_isAwsBedrockActive()){let client=_getOrCreateBedrockClient();return client?_embedImageWithAmazonBedrock(ss,opt,client):void 0}}async function compare2(page,figma,options){let normalize=typeof options?.normalize=="boolean"?options.normalize:!0,figmaVec=await _embedImage(figma,options);if(!figmaVec)return;let pageVec=await _embedImage(page,options);return pageVec?{score:cosineSimilarity(figmaVec,pageVec,normalize)}:void 0}import{BedrockRuntimeClient as BedrockRuntimeClient2,InvokeModelCommand as InvokeModelCommand2}from"@aws-sdk/client-bedrock-runtime";import{fromIni as fromIni2}from"@aws-sdk/credential-providers";var _sharpPromise3=null;async function getSharp3(){return _sharpPromise3||(_sharpPromise3=import("sharp").then(m=>m.default??m)),_sharpPromise3}var UI_DESCRIBE_PROMPT=`
346
+ `}inputSchema(){return{expression:z23.string().describe("Expression evaluated at every tracepoint hit (e.g. user.name).")}}outputSchema(){return{id:z23.string().describe("Watch expression ID"),expression:z23.string().describe("The watch expression"),message:z23.string().describe("Status message")}}async handle(context,args){isDebuggingEnabled2(context.browserContext)||await enableDebugging2(context.browserContext,context.page);let watch=addWatchExpression2(context.browserContext,args.expression);return{id:watch.id,expression:watch.expression,message:`Watch expression added: ${args.expression}`}}};var tools3=[new Status,new ResolveSourceLocation,new PutTracepoint,new RemoveProbe,new ListProbes,new ClearProbes,new PutLogpoint,new PutExceptionpoint,new GetProbeSnapshots,new ClearProbeSnapshots,new AddWatch];import ssim from"ssim.js";var _sharpPromise=null;async function getSharp(){return _sharpPromise||(_sharpPromise=import("sharp").then(m=>m.default??m)),_sharpPromise}var DEFAULT_GRAYSCALE=!1,DEFAULT_BLUR=10;function _clamp01(v){return Number.isFinite(v)?Math.max(0,Math.min(1,v)):0}async function _loadNormalized(input,canvasWidth,canvasHeight,mode){let sharp=await getSharp(),img;if(mode==="semantic"){img=sharp(input).resize(canvasWidth,canvasHeight,{fit:"cover",position:"centre"});let w=Math.max(1,Math.floor(canvasWidth/2)),h=Math.max(1,Math.floor(canvasHeight/2));img=img.resize(w,h,{fit:"cover",position:"centre"}).grayscale(DEFAULT_GRAYSCALE).blur(DEFAULT_BLUR)}else img=sharp(input).resize(canvasWidth,canvasHeight,{fit:"cover",position:"centre"});let out=await img.ensureAlpha().raw().toBuffer({resolveWithObject:!0});return{data:new Uint8ClampedArray(out.data.buffer,out.data.byteOffset,out.data.byteLength),width:out.info.width,height:out.info.height}}function _computeSsim(a,b){let details=ssim({data:a.data,width:a.width,height:a.height},{data:b.data,width:b.width,height:b.height}),rawScore=Number(details.mssim??details.ssim??0);return _clamp01(rawScore)}async function compare(page,figma,options){let mode=options?.mode??"semantic",canvasWidth=options?.canvasWidth,canvasHeight=options?.canvasHeight;if(typeof canvasWidth!="number"||!Number.isFinite(canvasWidth)||canvasWidth<=0||typeof canvasHeight!="number"||!Number.isFinite(canvasHeight)||canvasHeight<=0){let figmaMeta=await(await getSharp())(figma.image).metadata();if(canvasWidth=figmaMeta.width??0,canvasHeight=figmaMeta.height??0,canvasWidth<=0||canvasHeight<=0)throw new Error("Failed to read Figma image dimensions.")}let figmaImg=await _loadNormalized(figma.image,canvasWidth,canvasHeight,mode),pageImg=await _loadNormalized(page.image,canvasWidth,canvasHeight,mode);return{score:_computeSsim(figmaImg,pageImg)}}function dot(a,b){let n=Math.min(a.length,b.length),s=0;for(let i=0;i<n;i++)s+=a[i]*b[i];return s}function norm(a){let s=0;for(let i=0;i<a.length;i++){let x=a[i];s+=x*x}return Math.sqrt(s)}function l2Normalize(v){let n=norm(v);if(n===0)return v.slice();let out=new Array(v.length);for(let i=0;i<v.length;i++)out[i]=v[i]/n;return out}function cosineSimilarity(a,b,normalize){if(normalize){let na=l2Normalize(a),nb=l2Normalize(b);return dot(na,nb)}let denom=norm(a)*norm(b);return denom===0?0:dot(a,b)/denom}import{BedrockRuntimeClient,InvokeModelCommand}from"@aws-sdk/client-bedrock-runtime";import{fromIni}from"@aws-sdk/credential-providers";var _sharpPromise2=null;async function getSharp2(){return _sharpPromise2||(_sharpPromise2=import("sharp").then(m=>m.default??m)),_sharpPromise2}var DEFAULT_MAX_DIMENSION=1024,DEFAULT_JPEG_QUALITY=90,DEFAULT_AMAZON_BEDROCK_TITAN_OUTPUT_EMBEDDING_LENGTH=1024;async function _prepareImage(buf,imageType,opt){let img=(await getSharp2())(buf),maxDim=opt?.maxDim||DEFAULT_MAX_DIMENSION;if(img=img.resize({width:maxDim,height:maxDim,fit:"inside",withoutEnlargement:!0}),imageType==="png")return await img.png().toBuffer();let jpegQuality=opt?.jpegQuality||DEFAULT_JPEG_QUALITY;return await img.jpeg({quality:jpegQuality}).toBuffer()}var SUPPORTED_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_IDS=new Set(["amazon.titan-embed-image-v1"]),DEFAULT_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID="amazon.titan-embed-image-v1",_bedrockClient;function _isAwsBedrockActive(){return AMAZON_BEDROCK_ENABLE&&!!AWS_REGION}function _getOrCreateBedrockClient(){if(_bedrockClient)return _bedrockClient;let region=AWS_REGION;if(!region)return;let profile=AWS_PROFILE;return profile?(_bedrockClient=new BedrockRuntimeClient({region,credentials:fromIni({profile})}),_bedrockClient):(_bedrockClient=new BedrockRuntimeClient({region}),_bedrockClient)}async function _embedImageWithAmazonBedrockTitan(ss,client,opt,modelId){let bodyObj={inputImage:(await _prepareImage(ss.image,ss.type,opt)).toString("base64"),embeddingConfig:{outputEmbeddingLength:DEFAULT_AMAZON_BEDROCK_TITAN_OUTPUT_EMBEDDING_LENGTH}},cmd=new InvokeModelCommand({modelId,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(bodyObj),"utf-8")}),resp=await client.send(cmd),raw=resp?.body instanceof Uint8Array?resp.body:new Uint8Array(resp?.body??[]),text=Buffer.from(raw).toString("utf-8"),parsed;try{parsed=text?JSON.parse(text):{}}catch{throw new Error(`Amazon Bedrock Titan returned non-JSON response for embeddings: ${text.slice(0,300)}`)}let emb=parsed?.embedding??parsed?.embeddings?.[0]??parsed?.outputEmbedding??parsed?.vector;if(!Array.isArray(emb)||emb.length===0||typeof emb[0]!="number")throw new Error(`Unexpected Amazon Bedrock Titan image embedding response format: ${text.slice(0,500)}`);return emb}async function _embedImageWithAmazonBedrock(ss,opt,client){let modelId=opt?.modelId??AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID??DEFAULT_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_IMAGE_EMBED_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock image embedding model id: ${modelId}`);return await _embedImageWithAmazonBedrockTitan(ss,client,opt,modelId)}async function _embedImage(ss,opt){if(_isAwsBedrockActive()){let client=_getOrCreateBedrockClient();return client?_embedImageWithAmazonBedrock(ss,opt,client):void 0}}async function compare2(page,figma,options){let normalize=typeof options?.normalize=="boolean"?options.normalize:!0,figmaVec=await _embedImage(figma,options);if(!figmaVec)return;let pageVec=await _embedImage(page,options);return pageVec?{score:cosineSimilarity(figmaVec,pageVec,normalize)}:void 0}import{BedrockRuntimeClient as BedrockRuntimeClient2,InvokeModelCommand as InvokeModelCommand2}from"@aws-sdk/client-bedrock-runtime";import{fromIni as fromIni2}from"@aws-sdk/credential-providers";var _sharpPromise3=null;async function getSharp3(){return _sharpPromise3||(_sharpPromise3=import("sharp").then(m=>m.default??m)),_sharpPromise3}var UI_DESCRIBE_PROMPT=`
334
347
  You are analyzing a UI screenshot to compare it against another UI.
335
348
 
336
349
  Your goal is to produce a STRUCTURAL LAYOUT FINGERPRINT that remains stable
@@ -392,7 +405,7 @@ FORMAT
392
405
  - Keep the output under ~30 lines.
393
406
 
394
407
  Return plain text only. No markdown.
395
- `;function _resolvePrompt(opt){return opt?.prompt??UI_DESCRIBE_PROMPT.trim()}function _resolveMaxDim(opt){return typeof opt?.maxDim=="number"&&opt.maxDim>0?Math.floor(opt.maxDim):1024}function _resolveImageFormat(opt){return opt?.imageFormat==="jpeg"?"jpeg":"png"}function _resolveJpegQuality(opt){let q=opt?.jpegQuality;return typeof q=="number"&&q>=50&&q<=100?Math.floor(q):90}async function _preprocessImage(buf,opt){let maxDim=_resolveMaxDim(opt),format=_resolveImageFormat(opt),jpegQuality=_resolveJpegQuality(opt),img=(await getSharp3())(buf).resize({width:maxDim,height:maxDim,fit:"inside",withoutEnlargement:!0}),out,mimeType;return format==="png"?(out=await img.png().toBuffer(),mimeType="image/png"):(out=await img.jpeg({quality:jpegQuality}).toBuffer(),mimeType="image/jpeg"),{bytes:out,mimeType}}var SUPPORTED_AMAZON_BEDROCK_TEXT_EMBED_MODEL_IDS=new Set(["amazon.titan-embed-text-v2:0"]),DEFAULT_AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID="amazon.titan-embed-text-v2:0",SUPPORTED_AMAZON_BEDROCK_VISION_MODEL_IDS=new Set(["anthropic.claude-3-haiku-20240307-v1","anthropic.claude-3-sonnet-20240229-v1:0","anthropic.claude-3-5-sonnet-20241022-v2:0","anthropic.claude-3-7-sonnet-20250219-v1:0","anthropic.claude-3-opus-20240229-v1:0","anthropic.claude-haiku-4-5-20251001-v1:0","anthropic.claude-opus-4-1-20250805-v1:0","anthropic.claude-opus-4-5-20251101-v1:0"]),DEFAULT_AMAZON_BEDROCK_VISION_MODEL_ID="anthropic.claude-3-sonnet-20240229-v1:0",_bedrockClient2;function _isAwsBedrockActive2(){return AMAZON_BEDROCK_ENABLE&&!!AWS_REGION}function _getOrCreateBedrockClient2(){if(_bedrockClient2)return _bedrockClient2;let region=AWS_REGION;if(!region)return;let profile=AWS_PROFILE;return profile?(_bedrockClient2=new BedrockRuntimeClient2({region,credentials:fromIni2({profile})}),_bedrockClient2):(_bedrockClient2=new BedrockRuntimeClient2({region}),_bedrockClient2)}async function _invokeBedrock(client,modelId,payload){let cmd=new InvokeModelCommand2({modelId,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(payload))}),resp=await client.send(cmd),raw=Buffer.from(resp.body).toString("utf-8");return JSON.parse(raw)}async function _describeUIWithAmazonBedrockClaude(ss,opt,client,modelId){let{bytes,mimeType}=await _preprocessImage(ss.image,opt),payload={anthropic_version:"bedrock-2023-05-31",max_tokens:1e4,temperature:0,messages:[{role:"user",content:[{type:"text",text:_resolvePrompt(opt)},{type:"image",source:{type:"base64",media_type:mimeType,data:bytes.toString("base64")}}]}]},parsed=await _invokeBedrock(client,modelId,payload),text=parsed?.content?.[0]?.text??parsed?.output_text??parsed?.completion;if(!text||!text.trim())throw new Error("Amazon Bedrock Claude returned empty description.");return text.trim()}async function _embedTextWithAmazonBedrockTitan(text,client,modelId){let emb=(await _invokeBedrock(client,modelId,{inputText:text}))?.embedding;if(!Array.isArray(emb)||typeof emb[0]!="number")throw new Error("Unexpected embedding response for Amazon Bedrock Titan text embedding.");return emb}async function _describeUIWithAmazonBedrock(ss,opt,client){let modelId=opt?.visionModelId??AMAZON_BEDROCK_VISION_MODEL_ID??DEFAULT_AMAZON_BEDROCK_VISION_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_VISION_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock vision model id: ${modelId}`);return await _describeUIWithAmazonBedrockClaude(ss,opt,client,modelId)}async function _embedTextWithAmazonBedrock(text,opt,client){let modelId=opt?.textEmbedModelId??AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID??DEFAULT_AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_TEXT_EMBED_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock text embedding model id: ${modelId}`);return await _embedTextWithAmazonBedrockTitan(text,client,modelId)}async function _describeUI(ss,opt){if(_isAwsBedrockActive2()){let client=_getOrCreateBedrockClient2();return client?_describeUIWithAmazonBedrock(ss,opt,client):void 0}}async function _embedTextVector(text,opt){if(_isAwsBedrockActive2()){let client=_getOrCreateBedrockClient2();return client?_embedTextWithAmazonBedrock(text,opt,client):void 0}}async function compare3(page,figma,options){let normalize=typeof options?.normalize=="boolean"?options.normalize:!0,figmaDesc=await _describeUI(figma,options);if(!figmaDesc)return;let pageDesc=await _describeUI(page,options);if(!pageDesc)return;let figmaVec=await _embedTextVector(figmaDesc,options);if(!figmaVec)return;let pageVec=await _embedTextVector(pageDesc,options);return pageVec?{score:cosineSimilarity(figmaVec,pageVec,normalize)}:void 0}var DEFAULT_MSSIM_WEIGHT=.25,DEFAULT_VECTOR_EMBEDDING_WEIGHT=.5,DEFAULT_TEXT_EMBEDDING_WEIGHT=.25;function _clamp012(v){return Number.isFinite(v)?Math.max(0,Math.min(1,v)):0}function _weightOrDefault(v,def){return typeof v=="number"&&Number.isFinite(v)&&v>0?v:def}async function compareWithNotes(page,figma,options){let notes=[],wMssim=_weightOrDefault(options?.weights?.mssim,DEFAULT_MSSIM_WEIGHT),wVector=_weightOrDefault(options?.weights?.vectorEmbedding,DEFAULT_VECTOR_EMBEDDING_WEIGHT),wText=_weightOrDefault(options?.weights?.textEmbedding,DEFAULT_TEXT_EMBEDDING_WEIGHT),mssimRes=await compare(page,figma,options?.mssim),mssimScore=_clamp012(mssimRes.score);notes.push(`mssim=${mssimScore.toFixed(5)}`);let imageScore;try{let res=await compare2(page,figma,options?.imageEmbedding);res&&typeof res.score=="number"?(imageScore=_clamp012(res.score),notes.push(`image-embedding=${imageScore.toFixed(5)}`)):notes.push("image-embedding=skipped (inactive)")}catch(err){notes.push(`image-embedding=skipped (${err instanceof Error?err.message:String(err)})`)}let textScore;try{let res=await compare3(page,figma,options?.textEmbedding);res&&typeof res.score=="number"?(textScore=_clamp012(res.score),notes.push(`text-embedding=${textScore.toFixed(5)}`)):notes.push("text-embedding=skipped (inactive)")}catch(err){notes.push(`text-embedding=skipped (${err instanceof Error?err.message:String(err)})`)}let parts=[{name:"mssim",score:mssimScore,weight:wMssim}];typeof imageScore=="number"&&parts.push({name:"image-embedding",score:imageScore,weight:wVector}),typeof textScore=="number"&&parts.push({name:"text-embedding",score:textScore,weight:wText});let totalWeight=parts.reduce((s,p)=>s+p.weight,0),combined=totalWeight>0?parts.reduce((s,p)=>s+p.score*(p.weight/totalWeight),0):0,score=_clamp012(combined);return notes.push(`combined=${score.toFixed(5)} (signals=${parts.map(p=>p.name).join(", ")})`),{score,notes}}import crypto2 from"node:crypto";function _requireFigmaToken(){let token=FIGMA_ACCESS_TOKEN;if(!token)throw new Error("No Figma access token configured");return token}function _mimeTypeFor(format){return format==="jpg"?{mimeType:"image/jpeg",type:"jpeg"}:{mimeType:"image/png",type:"png"}}function _buildCacheKey(req){let h=crypto2.createHash("sha256");return h.update(req.fileKey),h.update("|"),h.update(req.nodeId),h.update("|"),h.update(req.format??"png"),h.update("|"),h.update(String(req.scale??2)),h.digest("hex").slice(0,24)}async function _fetchJson(url,token){let res=await fetch(url,{method:"GET",headers:{"X-Figma-Token":token,Accept:"application/json"}}),text=await res.text(),json;try{json=text?JSON.parse(text):{}}catch{throw new Error(`Figma API returned non-JSON response (status=${res.status}). Body: ${text.slice(0,500)}`)}if(!res.ok){let msg=typeof json?.err=="string"?json.err:`Figma API error (status=${res.status})`;throw new Error(msg)}return json}async function _fetchBinary(url){let res=await fetch(url,{method:"GET"});if(!res.ok){let t=await res.text().catch(()=>"");throw new Error(`Failed to download Figma rendered image (status=${res.status}): ${t.slice(0,300)}`)}let ab=await res.arrayBuffer();return Buffer.from(ab)}async function getFigmaDesignScreenshot(req){let token=_requireFigmaToken(),format=req.format??"png",scale=typeof req.scale=="number"&&req.scale>0?req.scale:2,{mimeType,type}=_mimeTypeFor(format),base=FIGMA_API_BASE_URL,fileKey=req.fileKey,nodeId=req.nodeId,url=`${base}/images/${encodeURIComponent(fileKey)}?ids=${encodeURIComponent(nodeId)}&format=${encodeURIComponent(format)}&scale=${encodeURIComponent(String(scale))}`,imgResp=await _fetchJson(url,token),imageUrl=imgResp.images?.[nodeId];if(!imageUrl){let err=typeof imgResp.err=="string"&&imgResp.err.trim()?imgResp.err:"Figma did not return an image URL for the given nodeId.";throw new Error(err)}let image=await _fetchBinary(imageUrl),cacheKey=_buildCacheKey(req),out={image,mimeType,type,cacheKey};return req.includeId===!0&&(out.nodeId=nodeId,out.fileKey=fileKey),out}import{z as z19}from"zod";var DEFAULT_SCREENSHOT_TYPE2="png",DEFAULT_FULL_PAGE=!0,DEFAULT_MSSIM_MODE="semantic",ComparePageWithDesign=class{name(){return"figma_compare-page-with-design"}description(){return`
408
+ `;function _resolvePrompt(opt){return opt?.prompt??UI_DESCRIBE_PROMPT.trim()}function _resolveMaxDim(opt){return typeof opt?.maxDim=="number"&&opt.maxDim>0?Math.floor(opt.maxDim):1024}function _resolveImageFormat(opt){return opt?.imageFormat==="jpeg"?"jpeg":"png"}function _resolveJpegQuality(opt){let q=opt?.jpegQuality;return typeof q=="number"&&q>=50&&q<=100?Math.floor(q):90}async function _preprocessImage(buf,opt){let maxDim=_resolveMaxDim(opt),format=_resolveImageFormat(opt),jpegQuality=_resolveJpegQuality(opt),img=(await getSharp3())(buf).resize({width:maxDim,height:maxDim,fit:"inside",withoutEnlargement:!0}),out,mimeType;return format==="png"?(out=await img.png().toBuffer(),mimeType="image/png"):(out=await img.jpeg({quality:jpegQuality}).toBuffer(),mimeType="image/jpeg"),{bytes:out,mimeType}}var SUPPORTED_AMAZON_BEDROCK_TEXT_EMBED_MODEL_IDS=new Set(["amazon.titan-embed-text-v2:0"]),DEFAULT_AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID="amazon.titan-embed-text-v2:0",SUPPORTED_AMAZON_BEDROCK_VISION_MODEL_IDS=new Set(["anthropic.claude-3-haiku-20240307-v1","anthropic.claude-3-sonnet-20240229-v1:0","anthropic.claude-3-5-sonnet-20241022-v2:0","anthropic.claude-3-7-sonnet-20250219-v1:0","anthropic.claude-3-opus-20240229-v1:0","anthropic.claude-haiku-4-5-20251001-v1:0","anthropic.claude-opus-4-1-20250805-v1:0","anthropic.claude-opus-4-5-20251101-v1:0"]),DEFAULT_AMAZON_BEDROCK_VISION_MODEL_ID="anthropic.claude-3-sonnet-20240229-v1:0",_bedrockClient2;function _isAwsBedrockActive2(){return AMAZON_BEDROCK_ENABLE&&!!AWS_REGION}function _getOrCreateBedrockClient2(){if(_bedrockClient2)return _bedrockClient2;let region=AWS_REGION;if(!region)return;let profile=AWS_PROFILE;return profile?(_bedrockClient2=new BedrockRuntimeClient2({region,credentials:fromIni2({profile})}),_bedrockClient2):(_bedrockClient2=new BedrockRuntimeClient2({region}),_bedrockClient2)}async function _invokeBedrock(client,modelId,payload){let cmd=new InvokeModelCommand2({modelId,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(payload))}),resp=await client.send(cmd),raw=Buffer.from(resp.body).toString("utf-8");return JSON.parse(raw)}async function _describeUIWithAmazonBedrockClaude(ss,opt,client,modelId){let{bytes,mimeType}=await _preprocessImage(ss.image,opt),payload={anthropic_version:"bedrock-2023-05-31",max_tokens:1e4,temperature:0,messages:[{role:"user",content:[{type:"text",text:_resolvePrompt(opt)},{type:"image",source:{type:"base64",media_type:mimeType,data:bytes.toString("base64")}}]}]},parsed=await _invokeBedrock(client,modelId,payload),text=parsed?.content?.[0]?.text??parsed?.output_text??parsed?.completion;if(!text||!text.trim())throw new Error("Amazon Bedrock Claude returned empty description.");return text.trim()}async function _embedTextWithAmazonBedrockTitan(text,client,modelId){let emb=(await _invokeBedrock(client,modelId,{inputText:text}))?.embedding;if(!Array.isArray(emb)||typeof emb[0]!="number")throw new Error("Unexpected embedding response for Amazon Bedrock Titan text embedding.");return emb}async function _describeUIWithAmazonBedrock(ss,opt,client){let modelId=opt?.visionModelId??AMAZON_BEDROCK_VISION_MODEL_ID??DEFAULT_AMAZON_BEDROCK_VISION_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_VISION_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock vision model id: ${modelId}`);return await _describeUIWithAmazonBedrockClaude(ss,opt,client,modelId)}async function _embedTextWithAmazonBedrock(text,opt,client){let modelId=opt?.textEmbedModelId??AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID??DEFAULT_AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID;if(!SUPPORTED_AMAZON_BEDROCK_TEXT_EMBED_MODEL_IDS.has(modelId))throw new Error(`Unsupported Amazon Bedrock text embedding model id: ${modelId}`);return await _embedTextWithAmazonBedrockTitan(text,client,modelId)}async function _describeUI(ss,opt){if(_isAwsBedrockActive2()){let client=_getOrCreateBedrockClient2();return client?_describeUIWithAmazonBedrock(ss,opt,client):void 0}}async function _embedTextVector(text,opt){if(_isAwsBedrockActive2()){let client=_getOrCreateBedrockClient2();return client?_embedTextWithAmazonBedrock(text,opt,client):void 0}}async function compare3(page,figma,options){let normalize=typeof options?.normalize=="boolean"?options.normalize:!0,figmaDesc=await _describeUI(figma,options);if(!figmaDesc)return;let pageDesc=await _describeUI(page,options);if(!pageDesc)return;let figmaVec=await _embedTextVector(figmaDesc,options);if(!figmaVec)return;let pageVec=await _embedTextVector(pageDesc,options);return pageVec?{score:cosineSimilarity(figmaVec,pageVec,normalize)}:void 0}var DEFAULT_MSSIM_WEIGHT=.25,DEFAULT_VECTOR_EMBEDDING_WEIGHT=.5,DEFAULT_TEXT_EMBEDDING_WEIGHT=.25;function _clamp012(v){return Number.isFinite(v)?Math.max(0,Math.min(1,v)):0}function _weightOrDefault(v,def){return typeof v=="number"&&Number.isFinite(v)&&v>0?v:def}async function compareWithNotes(page,figma,options){let notes=[],wMssim=_weightOrDefault(options?.weights?.mssim,DEFAULT_MSSIM_WEIGHT),wVector=_weightOrDefault(options?.weights?.vectorEmbedding,DEFAULT_VECTOR_EMBEDDING_WEIGHT),wText=_weightOrDefault(options?.weights?.textEmbedding,DEFAULT_TEXT_EMBEDDING_WEIGHT),mssimRes=await compare(page,figma,options?.mssim),mssimScore=_clamp012(mssimRes.score);notes.push(`mssim=${mssimScore.toFixed(5)}`);let imageScore;try{let res=await compare2(page,figma,options?.imageEmbedding);res&&typeof res.score=="number"?(imageScore=_clamp012(res.score),notes.push(`image-embedding=${imageScore.toFixed(5)}`)):notes.push("image-embedding=skipped (inactive)")}catch(err){notes.push(`image-embedding=skipped (${err instanceof Error?err.message:String(err)})`)}let textScore;try{let res=await compare3(page,figma,options?.textEmbedding);res&&typeof res.score=="number"?(textScore=_clamp012(res.score),notes.push(`text-embedding=${textScore.toFixed(5)}`)):notes.push("text-embedding=skipped (inactive)")}catch(err){notes.push(`text-embedding=skipped (${err instanceof Error?err.message:String(err)})`)}let parts=[{name:"mssim",score:mssimScore,weight:wMssim}];typeof imageScore=="number"&&parts.push({name:"image-embedding",score:imageScore,weight:wVector}),typeof textScore=="number"&&parts.push({name:"text-embedding",score:textScore,weight:wText});let totalWeight=parts.reduce((s,p)=>s+p.weight,0),combined=totalWeight>0?parts.reduce((s,p)=>s+p.score*(p.weight/totalWeight),0):0,score=_clamp012(combined);return notes.push(`combined=${score.toFixed(5)} (signals=${parts.map(p=>p.name).join(", ")})`),{score,notes}}import crypto2 from"node:crypto";function _requireFigmaToken(){let token=FIGMA_ACCESS_TOKEN;if(!token)throw new Error("No Figma access token configured");return token}function _mimeTypeFor(format){return format==="jpg"?{mimeType:"image/jpeg",type:"jpeg"}:{mimeType:"image/png",type:"png"}}function _buildCacheKey(req){let h=crypto2.createHash("sha256");return h.update(req.fileKey),h.update("|"),h.update(req.nodeId),h.update("|"),h.update(req.format??"png"),h.update("|"),h.update(String(req.scale??2)),h.digest("hex").slice(0,24)}async function _fetchJson(url,token){let res=await fetch(url,{method:"GET",headers:{"X-Figma-Token":token,Accept:"application/json"}}),text=await res.text(),json;try{json=text?JSON.parse(text):{}}catch{throw new Error(`Figma API returned non-JSON response (status=${res.status}). Body: ${text.slice(0,500)}`)}if(!res.ok){let msg=typeof json?.err=="string"?json.err:`Figma API error (status=${res.status})`;throw new Error(msg)}return json}async function _fetchBinary(url){let res=await fetch(url,{method:"GET"});if(!res.ok){let t=await res.text().catch(()=>"");throw new Error(`Failed to download Figma rendered image (status=${res.status}): ${t.slice(0,300)}`)}let ab=await res.arrayBuffer();return Buffer.from(ab)}async function getFigmaDesignScreenshot(req){let token=_requireFigmaToken(),format=req.format??"png",scale=typeof req.scale=="number"&&req.scale>0?req.scale:2,{mimeType,type}=_mimeTypeFor(format),base=FIGMA_API_BASE_URL,fileKey=req.fileKey,nodeId=req.nodeId,url=`${base}/images/${encodeURIComponent(fileKey)}?ids=${encodeURIComponent(nodeId)}&format=${encodeURIComponent(format)}&scale=${encodeURIComponent(String(scale))}`,imgResp=await _fetchJson(url,token),imageUrl=imgResp.images?.[nodeId];if(!imageUrl){let err=typeof imgResp.err=="string"&&imgResp.err.trim()?imgResp.err:"Figma did not return an image URL for the given nodeId.";throw new Error(err)}let image=await _fetchBinary(imageUrl),cacheKey=_buildCacheKey(req),out={image,mimeType,type,cacheKey};return req.includeId===!0&&(out.nodeId=nodeId,out.fileKey=fileKey),out}import{z as z24}from"zod";var DEFAULT_SCREENSHOT_TYPE2="png",DEFAULT_FULL_PAGE=!0,DEFAULT_MSSIM_MODE="semantic",ComparePageWithDesign=class{name(){return"figma_compare-page-with-design"}description(){return`
396
409
  Compares the CURRENT PAGE UI against a Figma design snapshot and returns a combined similarity score.
397
410
 
398
411
  What this tool does:
@@ -410,7 +423,7 @@ How to use it effectively:
410
423
  - Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration (e.g. AWS_REGION, inference profile, etc).
411
424
 
412
425
  This tool is designed for UI regression checks, design parity checks, and "does this page still match the intended layout?" validation.
413
- `.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)break;let latestNetworkActivity=context.lastNetworkActivityTimestamp();if(nowMs-latestNetworkActivity>5e3||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`
426
+ `.trim()}isEnabled(){return!!FIGMA_ACCESS_TOKEN?.trim()}inputSchema(){return{figmaFileKey:z24.string().min(1).describe("Figma file key (from URL: part after /file/)."),figmaNodeId:z24.string().min(1).describe("Figma node id (frame/component, e.g. 12:34)."),selector:z24.string().optional().describe("Compare only this region; omit for full page."),fullPage:z24.boolean().optional().default(DEFAULT_FULL_PAGE).describe("Full scrollable page; ignored when selector set."),figmaScale:z24.number().int().positive().optional().describe("Scale for Figma raster (e.g. 1, 2)."),figmaFormat:z24.enum(["png","jpg"]).optional(),weights:z24.object({mssim:z24.number().positive().optional(),imageEmbedding:z24.number().positive().optional(),textEmbedding:z24.number().positive().optional()}).optional().describe("Weights for combining signals."),mssimMode:z24.enum(["raw","semantic"]).optional().default(DEFAULT_MSSIM_MODE).describe("semantic = more robust for real data vs design."),maxDim:z24.number().int().positive().optional().describe("Max dimension for comparison."),jpegQuality:z24.number().int().min(50).max(100).optional().describe("JPEG quality 50\u2013100.")}}outputSchema(){return{score:z24.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:z24.array(z24.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:z24.object({pageUrl:z24.string().describe("URL of the page that was compared."),pageTitle:z24.string().describe("Title of the page that was compared."),figmaFileKey:z24.string().describe("Figma file key used for the design snapshot."),figmaNodeId:z24.string().describe("Figma node id used for the design snapshot."),selector:z24.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:z24.boolean().describe("Whether the page screenshot was full-page."),pageImageType:z24.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:z24.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)break;let latestNetworkActivity=context.lastNetworkActivityTimestamp();if(nowMs-latestNetworkActivity>5e3||nowMs>=deadlineMs)break;await new Promise(resolve=>{setTimeout(resolve,50)})}}import{z as z25}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:z25.string().describe("CSS selector or ref from a11y snapshot (e.g. e1, @e1)."),timeoutMs:z25.number().int().positive().optional().describe("Wait for element, ms. Default 10000."),waitForNavigation:z25.boolean().optional().default(!1).describe("Wait for navigation triggered by click (parallel with click). Use when click opens a new page."),waitForTimeoutMs:z25.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 z26}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:z26.string().describe("CSS selector or ref for the element to drag."),targetSelector:z26.string().describe("CSS selector or ref for the drop target."),timeoutMs:z26.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 z27}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:z27.string().describe("CSS selector or ref from a11y snapshot for the input."),value:z27.string(),timeoutMs:z27.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 z28}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:z28.string().describe("CSS selector or ref from a11y snapshot."),timeoutMs:z28.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 z29}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`
414
427
  Presses a keyboard key with optional "hold" and auto-repeat behavior.
415
428
 
416
429
  Key facts:
@@ -424,7 +437,7 @@ Execution logic:
424
437
  \u2192 a single keyboard.press() is executed.
425
438
  - If holdMs is provided AND repeat=true:
426
439
  \u2192 keyboard.press() is called repeatedly until holdMs elapses.
427
- `.trim()}inputSchema(){return{key:z24.string().describe("Key name (e.g. Enter, ArrowDown, Space)."),selector:z24.string().optional().describe("Focus this element first; omit for page focus."),holdMs:z24.number().int().min(0).optional().describe("Ms between keydown and keyup."),repeat:z24.boolean().optional().default(!1).describe("Repeat key while holdMs (e.g. for scroll)."),repeatIntervalMs:z24.number().int().min(MIN_REPEAT_INTERVAL_MS).optional().default(DEFAULT_REPEAT_INTERVAL_MS),timeoutMs:z24.number().int().positive().optional()}}outputSchema(){return{}}async handle(context,args){if(args.selector){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS5,locator=resolveSelectorOrRef(context,args.selector);await locator.waitFor({state:"visible",timeout}),await locator.focus()}let holdMs=args.holdMs??0,repeat=args.repeat===!0;if(holdMs<=0||repeat===!1)return await context.page.keyboard.press(args.key,holdMs>0?{delay:holdMs}:void 0),{};let repeatIntervalMs=typeof args.repeatIntervalMs=="number"&&Number.isFinite(args.repeatIntervalMs)&&args.repeatIntervalMs>=MIN_REPEAT_INTERVAL_MS?Math.floor(args.repeatIntervalMs):DEFAULT_REPEAT_INTERVAL_MS,startMs=Date.now();for(;Date.now()-startMs<holdMs;)await context.page.keyboard.press(args.key),await context.page.waitForTimeout(repeatIntervalMs);return{}}};import{z as z25}from"zod";var MIN_VIEWPORT_WIDTH=200,MIN_VIEWPORT_HEIGHT=200,ResizeViewport=class{name(){return"interaction_resize-viewport"}description(){return`
440
+ `.trim()}inputSchema(){return{key:z29.string().describe("Key name (e.g. Enter, ArrowDown, Space)."),selector:z29.string().optional().describe("Focus this element first; omit for page focus."),holdMs:z29.number().int().min(0).optional().describe("Ms between keydown and keyup."),repeat:z29.boolean().optional().default(!1).describe("Repeat key while holdMs (e.g. for scroll)."),repeatIntervalMs:z29.number().int().min(MIN_REPEAT_INTERVAL_MS).optional().default(DEFAULT_REPEAT_INTERVAL_MS),timeoutMs:z29.number().int().positive().optional()}}outputSchema(){return{}}async handle(context,args){if(args.selector){let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS5,locator=resolveSelectorOrRef(context,args.selector);await locator.waitFor({state:"visible",timeout}),await locator.focus()}let holdMs=args.holdMs??0,repeat=args.repeat===!0;if(holdMs<=0||repeat===!1)return await context.page.keyboard.press(args.key,holdMs>0?{delay:holdMs}:void 0),{};let repeatIntervalMs=typeof args.repeatIntervalMs=="number"&&Number.isFinite(args.repeatIntervalMs)&&args.repeatIntervalMs>=MIN_REPEAT_INTERVAL_MS?Math.floor(args.repeatIntervalMs):DEFAULT_REPEAT_INTERVAL_MS,startMs=Date.now();for(;Date.now()-startMs<holdMs;)await context.page.keyboard.press(args.key),await context.page.waitForTimeout(repeatIntervalMs);return{}}};import{z as z30}from"zod";var MIN_VIEWPORT_WIDTH=200,MIN_VIEWPORT_HEIGHT=200,ResizeViewport=class{name(){return"interaction_resize-viewport"}description(){return`
428
441
  Resizes the PAGE VIEWPORT using Playwright viewport emulation (page.setViewportSize).
429
442
 
430
443
  This affects:
@@ -437,7 +450,7 @@ Notes:
437
450
  - Runtime switching to viewport=null (binding to real window size) is not supported by Playwright.
438
451
  If you need real window-driven responsive behavior, start the BrowserContext with viewport: null
439
452
  and use the window resize tool instead.
440
- `.trim()}inputSchema(){return{width:z25.number().int().min(MIN_VIEWPORT_WIDTH),height:z25.number().int().min(MIN_VIEWPORT_HEIGHT)}}outputSchema(){return{requested:z25.object({width:z25.number().int().describe("Requested viewport width (CSS pixels)."),height:z25.number().int().describe("Requested viewport height (CSS pixels).")}).describe("Requested viewport configuration."),viewport:z25.object({innerWidth:z25.number().int().describe("window.innerWidth after resize (CSS pixels)."),innerHeight:z25.number().int().describe("window.innerHeight after resize (CSS pixels)."),outerWidth:z25.number().int().describe("window.outerWidth after resize (CSS pixels)."),outerHeight:z25.number().int().describe("window.outerHeight after resize (CSS pixels)."),devicePixelRatio:z25.number().describe("window.devicePixelRatio after resize.")}).describe("Viewport metrics observed inside the page after resizing.")}}async handle(context,args){await context.page.setViewportSize({width:args.width,height:args.height});let metrics=await context.page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio}));return{requested:{width:args.width,height:args.height},viewport:{innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)}}}};import{z as z26}from"zod";var MIN_WINDOW_WIDTH=200,MIN_WINDOW_HEIGHT=200,ResizeWindow=class{name(){return"interaction_resize-window"}description(){return`
453
+ `.trim()}inputSchema(){return{width:z30.number().int().min(MIN_VIEWPORT_WIDTH),height:z30.number().int().min(MIN_VIEWPORT_HEIGHT)}}outputSchema(){return{requested:z30.object({width:z30.number().int().describe("Requested viewport width (CSS pixels)."),height:z30.number().int().describe("Requested viewport height (CSS pixels).")}).describe("Requested viewport configuration."),viewport:z30.object({innerWidth:z30.number().int().describe("window.innerWidth after resize (CSS pixels)."),innerHeight:z30.number().int().describe("window.innerHeight after resize (CSS pixels)."),outerWidth:z30.number().int().describe("window.outerWidth after resize (CSS pixels)."),outerHeight:z30.number().int().describe("window.outerHeight after resize (CSS pixels)."),devicePixelRatio:z30.number().describe("window.devicePixelRatio after resize.")}).describe("Viewport metrics observed inside the page after resizing.")}}async handle(context,args){await context.page.setViewportSize({width:args.width,height:args.height});let metrics=await context.page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio}));return{requested:{width:args.width,height:args.height},viewport:{innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)}}}};import{z as z31}from"zod";var MIN_WINDOW_WIDTH=200,MIN_WINDOW_HEIGHT=200,ResizeWindow=class{name(){return"interaction_resize-window"}description(){return`
441
454
  Resizes the REAL BROWSER WINDOW (OS-level window) for the current page using Chrome DevTools Protocol (CDP).
442
455
 
443
456
  This tool works best on Chromium-based browsers (Chromium/Chrome/Edge).
@@ -447,7 +460,7 @@ so the page layout follows the OS window size.
447
460
  Important:
448
461
  - If Playwright viewport emulation is enabled (viewport is NOT null), resizing the OS window may not change page layout.
449
462
  - On non-Chromium browsers (Firefox/WebKit), CDP is not available and this tool will fail.
450
- `.trim()}inputSchema(){return{width:z26.number().int().min(MIN_WINDOW_WIDTH).optional().describe("Required when state=normal."),height:z26.number().int().min(MIN_WINDOW_HEIGHT).optional().describe("Required when state=normal."),state:z26.enum(["normal","maximized","minimized","fullscreen"]).optional().default("normal").describe("When not normal, width/height may be ignored.")}}outputSchema(){return{requested:z26.object({width:z26.number().int().nullable().describe("Requested window width (pixels). Null if not provided."),height:z26.number().int().nullable().describe("Requested window height (pixels). Null if not provided."),state:z26.enum(["normal","maximized","minimized","fullscreen"]).describe("Requested window state.")}).describe("Requested window change parameters."),before:z26.object({windowId:z26.number().int().describe("CDP window id for the current target."),state:z26.string().nullable().describe("Window state before resizing."),left:z26.number().int().nullable().describe("Window left position before resizing."),top:z26.number().int().nullable().describe("Window top position before resizing."),width:z26.number().int().nullable().describe("Window width before resizing."),height:z26.number().int().nullable().describe("Window height before resizing.")}).describe("Window bounds before resizing."),after:z26.object({windowId:z26.number().int().describe("CDP window id for the current target."),state:z26.string().nullable().describe("Window state after resizing."),left:z26.number().int().nullable().describe("Window left position after resizing."),top:z26.number().int().nullable().describe("Window top position after resizing."),width:z26.number().int().nullable().describe("Window width after resizing."),height:z26.number().int().nullable().describe("Window height after resizing.")}).describe("Window bounds after resizing."),viewport:z26.object({innerWidth:z26.number().int().describe("window.innerWidth after resizing (CSS pixels)."),innerHeight:z26.number().int().describe("window.innerHeight after resizing (CSS pixels)."),outerWidth:z26.number().int().describe("window.outerWidth after resizing (CSS pixels)."),outerHeight:z26.number().int().describe("window.outerHeight after resizing (CSS pixels)."),devicePixelRatio:z26.number().describe("window.devicePixelRatio after resizing.")}).describe("Page viewport metrics after resizing (helps verify responsive behavior).")}}async handle(context,args){let state=args.state??"normal",width=args.width,height=args.height;if(state==="normal"&&(typeof width!="number"||typeof height!="number"))throw new Error('state="normal" requires both width and height.');let page=context.page,cdp=await page.context().newCDPSession(page);try{let info=await cdp.send("Browser.getWindowForTarget",{}),windowId=Number(info.windowId),beforeBounds=info.bounds??{},before={windowId,state:typeof beforeBounds.windowState=="string"?beforeBounds.windowState:null,left:typeof beforeBounds.left=="number"?beforeBounds.left:null,top:typeof beforeBounds.top=="number"?beforeBounds.top:null,width:typeof beforeBounds.width=="number"?beforeBounds.width:null,height:typeof beforeBounds.height=="number"?beforeBounds.height:null},boundsToSet={};state!=="normal"?boundsToSet.windowState=state:(boundsToSet.windowState="normal",boundsToSet.width=width,boundsToSet.height=height),await cdp.send("Browser.setWindowBounds",{windowId,bounds:boundsToSet});let afterBounds=(await cdp.send("Browser.getWindowForTarget",{})).bounds??{},after={windowId,state:typeof afterBounds.windowState=="string"?afterBounds.windowState:null,left:typeof afterBounds.left=="number"?afterBounds.left:null,top:typeof afterBounds.top=="number"?afterBounds.top:null,width:typeof afterBounds.width=="number"?afterBounds.width:null,height:typeof afterBounds.height=="number"?afterBounds.height:null},metrics=await page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio})),viewport={innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)};return{requested:{width:typeof width=="number"?width:null,height:typeof height=="number"?height:null,state},before,after,viewport}}catch(e){let msg=String(e?.message??e);throw new Error(`Failed to resize real browser window via CDP. This tool works best on Chromium-based browsers. Original error: ${msg}`)}finally{await cdp.detach().catch(()=>{})}}};import{z as z27}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS6=1e4,SELECTABLE_ROLES=new Set(["listbox","combobox"]),Select=class{name(){return"interaction_select"}description(){return"Select an option in a dropdown. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z27.string().describe("CSS selector or ref from a11y snapshot for the dropdown."),value:z27.string(),timeoutMs:z27.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,SELECTABLE_ROLES,"select","Use a ref for a listbox or combobox (e.g. from the latest <a11y_take-aria-snapshot>). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS6;return await resolveSelectorOrRef(context,args.selector).selectOption(args.value,{timeout}),{}}};import{z as z28}from"zod";var DEFAULT_BEHAVIOR="auto",DEFAULT_MODE="by",Scroll=class{name(){return"interaction_scroll"}description(){return`
463
+ `.trim()}inputSchema(){return{width:z31.number().int().min(MIN_WINDOW_WIDTH).optional().describe("Required when state=normal."),height:z31.number().int().min(MIN_WINDOW_HEIGHT).optional().describe("Required when state=normal."),state:z31.enum(["normal","maximized","minimized","fullscreen"]).optional().default("normal").describe("When not normal, width/height may be ignored.")}}outputSchema(){return{requested:z31.object({width:z31.number().int().nullable().describe("Requested window width (pixels). Null if not provided."),height:z31.number().int().nullable().describe("Requested window height (pixels). Null if not provided."),state:z31.enum(["normal","maximized","minimized","fullscreen"]).describe("Requested window state.")}).describe("Requested window change parameters."),before:z31.object({windowId:z31.number().int().describe("CDP window id for the current target."),state:z31.string().nullable().describe("Window state before resizing."),left:z31.number().int().nullable().describe("Window left position before resizing."),top:z31.number().int().nullable().describe("Window top position before resizing."),width:z31.number().int().nullable().describe("Window width before resizing."),height:z31.number().int().nullable().describe("Window height before resizing.")}).describe("Window bounds before resizing."),after:z31.object({windowId:z31.number().int().describe("CDP window id for the current target."),state:z31.string().nullable().describe("Window state after resizing."),left:z31.number().int().nullable().describe("Window left position after resizing."),top:z31.number().int().nullable().describe("Window top position after resizing."),width:z31.number().int().nullable().describe("Window width after resizing."),height:z31.number().int().nullable().describe("Window height after resizing.")}).describe("Window bounds after resizing."),viewport:z31.object({innerWidth:z31.number().int().describe("window.innerWidth after resizing (CSS pixels)."),innerHeight:z31.number().int().describe("window.innerHeight after resizing (CSS pixels)."),outerWidth:z31.number().int().describe("window.outerWidth after resizing (CSS pixels)."),outerHeight:z31.number().int().describe("window.outerHeight after resizing (CSS pixels)."),devicePixelRatio:z31.number().describe("window.devicePixelRatio after resizing.")}).describe("Page viewport metrics after resizing (helps verify responsive behavior).")}}async handle(context,args){let state=args.state??"normal",width=args.width,height=args.height;if(state==="normal"&&(typeof width!="number"||typeof height!="number"))throw new Error('state="normal" requires both width and height.');let page=context.page,cdp=await page.context().newCDPSession(page);try{let info=await cdp.send("Browser.getWindowForTarget",{}),windowId=Number(info.windowId),beforeBounds=info.bounds??{},before={windowId,state:typeof beforeBounds.windowState=="string"?beforeBounds.windowState:null,left:typeof beforeBounds.left=="number"?beforeBounds.left:null,top:typeof beforeBounds.top=="number"?beforeBounds.top:null,width:typeof beforeBounds.width=="number"?beforeBounds.width:null,height:typeof beforeBounds.height=="number"?beforeBounds.height:null},boundsToSet={};state!=="normal"?boundsToSet.windowState=state:(boundsToSet.windowState="normal",boundsToSet.width=width,boundsToSet.height=height),await cdp.send("Browser.setWindowBounds",{windowId,bounds:boundsToSet});let afterBounds=(await cdp.send("Browser.getWindowForTarget",{})).bounds??{},after={windowId,state:typeof afterBounds.windowState=="string"?afterBounds.windowState:null,left:typeof afterBounds.left=="number"?afterBounds.left:null,top:typeof afterBounds.top=="number"?afterBounds.top:null,width:typeof afterBounds.width=="number"?afterBounds.width:null,height:typeof afterBounds.height=="number"?afterBounds.height:null},metrics=await page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio})),viewport={innerWidth:Number(metrics.innerWidth),innerHeight:Number(metrics.innerHeight),outerWidth:Number(metrics.outerWidth),outerHeight:Number(metrics.outerHeight),devicePixelRatio:Number(metrics.devicePixelRatio)};return{requested:{width:typeof width=="number"?width:null,height:typeof height=="number"?height:null,state},before,after,viewport}}catch(e){let msg=String(e?.message??e);throw new Error(`Failed to resize real browser window via CDP. This tool works best on Chromium-based browsers. Original error: ${msg}`)}finally{await cdp.detach().catch(()=>{})}}};import{z as z32}from"zod";var DEFAULT_SELECTOR_TIMEOUT_MS6=1e4,SELECTABLE_ROLES=new Set(["listbox","combobox"]),Select=class{name(){return"interaction_select"}description(){return"Select an option in a dropdown. Accepts a CSS selector or a ref from the last ARIA snapshot (e.g. e1, @e1)."}inputSchema(){return{selector:z32.string().describe("CSS selector or ref from a11y snapshot for the dropdown."),value:z32.string(),timeoutMs:z32.number().int().positive().optional().describe("Wait for element, ms. Default 10000.")}}outputSchema(){return{}}async handle(context,args){assertRefRole(context,args.selector,SELECTABLE_ROLES,"select","Use a ref for a listbox or combobox (e.g. from the latest <a11y_take-aria-snapshot>). Refs are page-specific; re-snapshot after navigation.");let timeout=args.timeoutMs??DEFAULT_SELECTOR_TIMEOUT_MS6;return await resolveSelectorOrRef(context,args.selector).selectOption(args.value,{timeout}),{}}};import{z as z33}from"zod";var DEFAULT_BEHAVIOR="auto",DEFAULT_MODE="by",Scroll=class{name(){return"interaction_scroll"}description(){return`
451
464
  Scrolls the page viewport or a specific scrollable element.
452
465
 
453
466
  Modes:
@@ -463,42 +476,55 @@ Use this tool to:
463
476
  - Jump to the top/bottom without knowing exact positions
464
477
  - Bring elements into view before clicking
465
478
  - Inspect lazy-loaded content that appears on scroll
466
- `.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("Scrollable container: ref (e.g. from latest <a11y_take-aria-snapshot>), getBy\u2026 expression, or CSS. 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_MS2=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_MS2).describe("Max wait ms. 0=no timeout."),waitUntil:z29.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after history navigation."),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. Same semantics as <content_take-screenshot> (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_MS3=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_MS3).describe("Max wait ms. 0=no timeout."),waitUntil:z30.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL2).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after navigation."),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. Same semantics as <content_take-screenshot> (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_MS4=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" (same as <content_take-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_MS4).describe("Max wait ms."),waitUntil:z31.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL3).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after reload."),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. Same semantics as <content_take-screenshot> (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(`
479
+ `.trim()}inputSchema(){return{mode:z33.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:z33.string().optional().describe("Scrollable container: ref (e.g. from latest <a11y_take-aria-snapshot>), getBy\u2026 expression, or CSS. Omit for viewport."),dx:z33.number().optional(),dy:z33.number().optional(),x:z33.number().optional(),y:z33.number().optional(),behavior:z33.enum(["auto","smooth"]).optional().default(DEFAULT_BEHAVIOR)}}outputSchema(){return{mode:z33.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:z33.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:z33.enum(["auto","smooth"]).describe("The scroll behavior used."),before:z33.object({x:z33.number().describe("ScrollLeft before scrolling."),y:z33.number().describe("ScrollTop before scrolling."),scrollWidth:z33.number().describe("Total scrollable width before scrolling."),scrollHeight:z33.number().describe("Total scrollable height before scrolling."),clientWidth:z33.number().describe("Viewport/container client width before scrolling."),clientHeight:z33.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:z33.object({x:z33.number().describe("ScrollLeft after scrolling."),y:z33.number().describe("ScrollTop after scrolling."),scrollWidth:z33.number().describe("Total scrollable width after scrolling."),scrollHeight:z33.number().describe("Total scrollable height after scrolling."),clientWidth:z33.number().describe("Viewport/container client width after scrolling."),clientHeight:z33.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:z33.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:z33.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:z33.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:z33.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:z33.boolean().describe("Whether the scroll position is at the far left."),isAtRight:z33.boolean().describe("Whether the scroll position is at the far right."),isAtTop:z33.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:z33.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 fs6 from"node:fs/promises";import os5 from"node:os";import path6 from"node:path";import{z as z34}from"zod";var DEFAULT_SCREENSHOT_NAME2="screenshot",DEFAULT_TIMEOUT_MS2=0,DEFAULT_WAIT_FOR_TIMEOUT_MS2=3e4,DEFAULT_WAIT_UNTIL="load",DIRECTION_VALUES=["back","forward"],refMapEntrySchema2=z34.object({role:z34.string(),name:z34.string().optional(),selector:z34.string(),nth:z34.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:z34.enum(DIRECTION_VALUES),timeout:z34.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS2).describe("Max wait ms. 0=no timeout."),waitUntil:z34.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after history navigation."),waitForNavigation:z34.boolean().optional().default(!0).describe("Wait for navigation then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z34.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:z34.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z34.object({interactiveOnly:z34.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z34.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:z34.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:z34.object({outputPath:z34.string().optional().default(os5.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z34.string().optional().default(DEFAULT_SCREENSHOT_NAME2).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z34.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z34.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z34.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z34.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. Same semantics as <content_take-screenshot> (outputPath/name default to tmp and "screenshot").')}}outputSchema(){return{url:z34.string().describe("Contains the URL of the navigated page.").optional(),status:z34.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z34.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z34.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z34.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z34.string().optional(),refs:z34.record(z34.string(),refMapEntrySchema2).optional(),image:z34.object({data:z34.any().describe("Base64-encoded image data."),mimeType:z34.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??os5.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=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,_artifacts:[{type:"IMAGE",filePath:screenshotFilePath,mimeType:screenshotFilePath.endsWith(".jpeg")?"image/jpeg":"image/png"}]},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs7 from"node:fs/promises";import os6 from"node:os";import path7 from"node:path";import{z as z35}from"zod";var DEFAULT_SCREENSHOT_NAME3="screenshot",DEFAULT_TIMEOUT_MS3=0,DEFAULT_WAIT_FOR_TIMEOUT_MS3=3e4,DEFAULT_WAIT_UNTIL2="load",refMapEntrySchema3=z35.object({role:z35.string(),name:z35.string().optional(),selector:z35.string(),nth:z35.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:z35.string(),timeout:z35.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS3).describe("Max wait ms. 0=no timeout."),waitUntil:z35.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL2).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after navigation."),waitForNavigation:z35.boolean().optional().default(!0).describe("Wait for navigation then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z35.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:z35.boolean().optional().default(!0).describe("Return ARIA snapshot with refs after nav."),snapshotOptions:z35.object({interactiveOnly:z35.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z35.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:z35.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:z35.object({outputPath:z35.string().optional().default(os6.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z35.string().optional().default(DEFAULT_SCREENSHOT_NAME3).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z35.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z35.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z35.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z35.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. Same semantics as <content_take-screenshot> (outputPath/name default to tmp and "screenshot").')}}outputSchema(){return{url:z35.string().describe("Contains the URL of the navigated page.").optional(),status:z35.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:z35.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:z35.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z35.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z35.string().optional().describe("When includeSnapshot is true: page URL, title, and ARIA tree with refs."),refs:z35.record(z35.string(),refMapEntrySchema3).optional().describe("When includeSnapshot is true: map of ref id (e1, e2, ...) to role/name/selector for use in interaction tools."),image:z35.object({data:z35.any().describe("Base64-encoded image data."),mimeType:z35.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??os6.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=path7.resolve(outputPath,filename);await fs7.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,_artifacts:[{type:"IMAGE",filePath:screenshotFilePath,mimeType:screenshotFilePath.endsWith(".jpeg")?"image/jpeg":"image/png"}]},...output.output!==void 0&&{output:output.output},...output.refs!==void 0&&{refs:output.refs},...image!==void 0&&{image}}}};import fs8 from"node:fs/promises";import os7 from"node:os";import path8 from"node:path";import{z as z36}from"zod";var DEFAULT_SCREENSHOT_NAME4="screenshot",DEFAULT_TIMEOUT_MS4=0,DEFAULT_WAIT_FOR_TIMEOUT_MS4=3e4,DEFAULT_WAIT_UNTIL3="load",refMapEntrySchema4=z36.object({role:z36.string(),name:z36.string().optional(),selector:z36.string(),nth:z36.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" (same as <content_take-screenshot>). Use `screenshotOptions.includeBase64: true` only when the file cannot be read from the path.\n '}inputSchema(){return{timeout:z36.number().int().nonnegative().optional().default(DEFAULT_TIMEOUT_MS4).describe("Max wait ms."),waitUntil:z36.enum(["load","domcontentloaded","commit"]).optional().default(DEFAULT_WAIT_UNTIL3).describe("Playwright navigation lifecycle: when the main frame reaches this state. Does not use Playwright networkidle; use waitForNavigation for session network-idle after reload."),waitForNavigation:z36.boolean().optional().default(!0).describe("Wait for reload then for network idle before snapshot/screenshot. Default true."),waitForTimeoutMs:z36.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:z36.boolean().optional().default(!0).describe("Return ARIA snapshot with refs."),snapshotOptions:z36.object({interactiveOnly:z36.boolean().optional().default(!1).describe("Only interactive elements get refs. Default false (content roles like headings also included)."),cursorInteractive:z36.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:z36.boolean().optional().default(!1).describe("Take a screenshot after reload; saved to disk (default: OS temp dir)."),screenshotOptions:z36.object({outputPath:z36.string().optional().default(os7.tmpdir()).describe("Directory to save the screenshot. Default: OS temp dir."),name:z36.string().optional().default(DEFAULT_SCREENSHOT_NAME4).describe('Base name for the screenshot file. Default: "screenshot".'),fullPage:z36.boolean().optional().default(!0).describe("Capture full scrollable page. Default: true."),type:z36.enum(["png","jpeg"]).optional().default("png").describe("Image format. Default: png."),annotate:z36.boolean().optional().default(!0).describe("Overlay ARIA ref labels on the screenshot. Default: true."),includeBase64:z36.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. Same semantics as <content_take-screenshot> (outputPath/name default to tmp and "screenshot").')}}outputSchema(){return{url:z36.string().describe("Contains the URL of the reloaded page.").optional(),status:z36.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:z36.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:z36.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional(),screenshotFilePath:z36.string().optional().describe("When includeScreenshot is true: full path of the saved screenshot file."),output:z36.string().optional(),refs:z36.record(z36.string(),refMapEntrySchema4).optional(),image:z36.object({data:z36.any().describe("Base64-encoded image data."),mimeType:z36.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??os7.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=path8.resolve(outputPath,filename);await fs8.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,_artifacts:[{type:"IMAGE",filePath:screenshotFilePath,mimeType:screenshotFilePath.endsWith(".jpeg")?"image/jpeg":"image/png"}]},...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 z37}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:z37.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z37.string().optional().describe("Filter by message text."),timestamp:z37.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z37.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z37.object({count:z37.number().int().nonnegative().default(100),from:z37.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional()}}outputSchema(){return{messages:z37.array(z37.object({type:z37.string().describe("Type of the console message."),text:z37.string().describe("Text of the console message."),location:z37.object({url:z37.string().describe("URL of the resource."),lineNumber:z37.number().nonnegative().describe("0-based line number in the resource."),columnNumber:z37.number().nonnegative().describe("0-based column number in the resource.")}).describe("Location of the console message in the resource.").optional(),timestamp:z37.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the console message."),sequenceNumber:z37.number().int().nonnegative().describe(`
467
480
  A monotonically increasing sequence number assigned to each console message.
468
481
  It reflects the order in which messages were captured and can be used by clients
469
482
  to retrieve messages incrementally by requesting only those with a higher sequence
470
- 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(`
483
+ 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 z38}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:z38.enum(getEnumKeyTuples(HttpResourceType)).transform(createEnumTransformer(HttpResourceType)).optional().describe("Type filter."),status:z38.object({min:z38.number().int().positive().optional(),max:z38.number().int().positive().optional()}).optional().describe("HTTP status range."),ok:z38.boolean().optional().describe("2xx only."),timestamp:z38.number().int().nonnegative().optional().describe("Only requests at or after this Unix ms."),sequenceNumber:z38.number().int().nonnegative().optional().describe("Incremental: only requests with sequence > this."),limit:z38.object({count:z38.number().int().nonnegative().default(100),from:z38.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).default({count:100,from:"end"}).optional(),includeRequestHeaders:z38.boolean().default(!1).optional().describe("Include request headers in each item. Default false."),includeResponseHeaders:z38.boolean().default(!1).optional().describe("Include response headers in each item. Default false."),includeResponseBody:z38.boolean().default(!1).optional().describe("Include response body in each item. Default false.")}}outputSchema(){return{requests:z38.array(z38.object({url:z38.string().describe("HTTP request url."),method:z38.enum(getEnumKeyTuples(HttpMethod)).describe(`HTTP request method. Valid values are: ${getEnumKeyTuples(HttpMethod)}`),headers:z38.record(z38.string(),z38.string()).describe("HTTP request headers as key-value pairs. Present when includeRequestHeaders was true.").optional(),body:z38.string().describe("HTTP request body if available.").optional(),resourceType:z38.enum(getEnumKeyTuples(HttpResourceType)).describe(`
471
484
  HTTP request resource type as it was perceived by the rendering engine.
472
- 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(`
485
+ Valid values are: ${getEnumKeyTuples(HttpResourceType)}`),failure:z38.string().describe("Error message of the HTTP request if failed.").optional(),duration:z38.number().describe('HTTP request duration in milliseconds. "-1" if not available (no response).').optional(),response:z38.object({status:z38.number().int().positive().describe("HTTP response status code."),statusText:z38.string().describe("HTTP response status text."),headers:z38.record(z38.string(),z38.string()).describe("HTTP response headers. Present when includeResponseHeaders was true.").optional(),body:z38.string().describe("HTTP response body if available. Present when includeResponseBody was true.").optional()}).describe("HTTP response.").optional(),ok:z38.boolean().describe(`
473
486
  Flag to represent whether the HTTP request successful or failed.
474
487
  An HTTP request is considered successful only if its status code is 2XX.
475
- 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(`
488
+ Otherwise (non-2XX status code or no response at all because of timeout, network failure, etc ...) it is considered as failed.`).optional(),timestamp:z38.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the HTTP request."),sequenceNumber:z38.number().int().nonnegative().describe(`
476
489
  A monotonically increasing sequence number assigned to each HTTP request.
477
490
  It reflects the order in which requests were captured and can be used by clients
478
491
  to retrieve requests incrementally by requesting only those with a higher sequence
479
- 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`
492
+ 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 z39}from"zod";var GetTraceContext=class{name(){return"o11y_get-trace-context"}description(){return"Gets the OpenTelemetry trace context (trace id and tracestate) from the live browser page when OTEL is enabled."}inputSchema(){return{}}outputSchema(){return{traceId:z39.string().describe("The OpenTelemetry compatible trace id of the current session if available.").optional(),traceState:z39.string().describe("The W3C tracestate value of the current session if available.").optional()}}async handle(context,_args){return await context.getTraceContextFromBrowser()}};import{z as z40}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`
480
493
  Collects Web Vitals (LCP, INP, CLS, TTFB, FCP) with Google thresholds and recommendations.
481
494
  Call after navigation or user actions; use waitMs for more stable LCP/CLS/INP.
482
495
  Some metrics may be unavailable depending on browser and interactions.
483
- `.trim()}inputSchema(){return{waitMs:z35.number().int().min(0).max(MAX_WAIT_MS).optional().default(DEFAULT_WAIT_MS).describe("Ms to wait before reading (e.g. let LCP settle)."),includeDebug:z35.boolean().optional().default(!1).describe("Include entry counts and LCP element hint.")}}outputSchema(){let ratingSchema=z35.object({rating:z35.enum(["good","needs_improvement","poor","not_available"]).describe("Rating based on Google thresholds."),value:z35.number().nullable().describe("Metric value (null if unavailable)."),unit:z35.enum(["ms","score"]).describe("Unit of the metric."),thresholds:z35.object({good:z35.number().describe('Upper bound for the "good" rating.'),poor:z35.number().describe('Lower bound for the "poor" rating.')}).describe("Thresholds used for rating.")});return{url:z35.string().describe("Current page URL."),title:z35.string().describe("Current page title."),timestampMs:z35.number().int().describe("Unix epoch timestamp (ms) when the metrics were captured."),metrics:z35.object({lcpMs:z35.number().nullable().describe("Largest Contentful Paint in milliseconds."),inpMs:z35.number().nullable().describe("Interaction to Next Paint in milliseconds (best-effort approximation)."),cls:z35.number().nullable().describe("Cumulative Layout Shift score."),ttfbMs:z35.number().nullable().describe("Time to First Byte in milliseconds."),fcpMs:z35.number().nullable().describe("First Contentful Paint in milliseconds.")}).describe("Raw metric values (null if unavailable)."),ratings:z35.object({lcp:ratingSchema.describe("LCP rating."),inp:ratingSchema.describe("INP rating."),cls:ratingSchema.describe("CLS rating."),ttfb:ratingSchema.describe("TTFB rating."),fcp:ratingSchema.describe("FCP rating.")}).describe("Ratings computed from Google thresholds."),recommendations:z35.object({coreWebVitalsPassed:z35.boolean().describe('True if all Core Web Vitals are rated "good".'),summary:z35.array(z35.string()).describe("High-level summary and prioritization guidance."),lcp:z35.array(z35.string()).describe("Recommendations for improving LCP."),inp:z35.array(z35.string()).describe("Recommendations for improving INP."),cls:z35.array(z35.string()).describe("Recommendations for improving CLS."),ttfb:z35.array(z35.string()).describe("Recommendations for improving TTFB."),fcp:z35.array(z35.string()).describe("Recommendations for improving FCP."),general:z35.array(z35.string()).describe("General measurement and debugging notes.")}).describe("Recommendations based on the measured values and their ratings."),notes:z35.array(z35.string()).describe("Notes about metric availability, browser limitations, and interpretation."),debug:z35.object({waitMs:z35.number().int().describe("Actual wait duration used before reading metrics."),entries:z35.object({navigation:z35.number().int().describe("Count of navigation entries."),paint:z35.number().int().describe("Count of paint entries."),lcp:z35.number().int().describe("Count of largest-contentful-paint entries."),layoutShift:z35.number().int().describe("Count of layout-shift entries."),eventTiming:z35.number().int().describe("Count of event timing entries.")}).describe("Counts of PerformanceEntry types used to compute metrics."),lastLcpSelectorHint:z35.string().nullable().describe("Best-effort selector hint for the last LCP element (if available)."),lastLcpTagName:z35.string().nullable().describe("Tag name of the last LCP element (if available).")}).optional().describe("Optional debug details.")}}async handle(context,args){let waitMs=args.waitMs??DEFAULT_WAIT_MS,includeDebug=args.includeDebug===!0,pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),timestampMs=Date.now(),result=await context.page.evaluate(async params=>{let waitMsEval=params.waitMsEval,includeDebugEval=params.includeDebugEval,notes2=[],sleep=ms=>new Promise(resolve=>{setTimeout(()=>resolve(),ms)});waitMsEval>0&&await sleep(waitMsEval);let ttfbMs2=null,navEntries=performance.getEntriesByType("navigation")??[];if(navEntries.length>0){let nav=navEntries[0];typeof nav.responseStart=="number"&&nav.responseStart>=0&&(ttfbMs2=nav.responseStart)}else notes2.push("TTFB: navigation entries not available (older browser or restricted timing).");let fcpMs2=null,paintEntries=performance.getEntriesByType("paint")??[],fcp=paintEntries.find(e=>e.name==="first-contentful-paint");fcp&&typeof fcp.startTime=="number"?fcpMs2=fcp.startTime:notes2.push("FCP: paint entries not available (browser may not support Paint Timing).");let lcpMs2=null,lastLcpEl=null,lcpEntries=performance.getEntriesByType("largest-contentful-paint")??[];if(lcpEntries.length>0){let last=lcpEntries[lcpEntries.length-1];typeof last.startTime=="number"&&(lcpMs2=last.startTime),last.element&&last.element instanceof Element&&(lastLcpEl=last.element)}else notes2.push("LCP: largest-contentful-paint entries not available (requires LCP support).");let selectorHintFor=el=>{if(!el)return null;let dt=el.getAttribute("data-testid")||el.getAttribute("data-test-id")||el.getAttribute("data-test");if(dt&&dt.trim())return'[data-testid="'+dt.replace(/"/g,'\\"')+'"]';let ds=el.getAttribute("data-selector");if(ds&&ds.trim())return'[data-selector="'+ds.replace(/"/g,'\\"')+'"]';if(el.id)try{return"#"+CSS.escape(el.id)}catch{return"#"+String(el.id)}return el.tagName?el.tagName.toLowerCase():null},cls2=null,layoutShiftEntries=performance.getEntriesByType("layout-shift")??[];if(layoutShiftEntries.length>0){let sum=0;for(let e of layoutShiftEntries)e&&e.hadRecentInput===!0||typeof e.value=="number"&&(sum+=e.value);cls2=sum}else notes2.push("CLS: layout-shift entries not available (requires Layout Instability API support).");let inpMs2=null,eventEntries=performance.getEntriesByType("event")??[];if(eventEntries.length>0){let maxDur=0;for(let e of eventEntries){let interactionId=typeof e.interactionId=="number"?e.interactionId:0,duration=typeof e.duration=="number"?e.duration:0;interactionId>0&&duration>maxDur&&(maxDur=duration)}maxDur>0?inpMs2=maxDur:notes2.push("INP: event timing entries exist but no interactionId-based events were found.")}else notes2.push("INP: event timing entries not available (requires Event Timing API support).");notes2.length===0?notes2.push("All requested metrics were available."):notes2.push("Some metrics may be null due to browser support limitations.");let out={metrics:{ttfbMs:ttfbMs2,fcpMs:fcpMs2,lcpMs:lcpMs2,cls:cls2,inpMs:inpMs2},notes:notes2,lcp:{selectorHint:selectorHintFor(lastLcpEl),tagName:lastLcpEl?String(lastLcpEl.tagName).toLowerCase():null},debug:{waitMs:waitMsEval,entries:{navigation:navEntries.length,paint:paintEntries.length,lcp:lcpEntries.length,layoutShift:layoutShiftEntries.length,eventTiming:eventEntries.length}}};return includeDebugEval?(out.debug.lastLcpSelectorHint=out.lcp.selectorHint,out.debug.lastLcpTagName=out.lcp.tagName):delete out.debug,out},{waitMsEval:waitMs,includeDebugEval:includeDebug}),lcpMs=typeof result?.metrics?.lcpMs=="number"?result.metrics.lcpMs:null,inpMs=typeof result?.metrics?.inpMs=="number"?result.metrics.inpMs:null,cls=typeof result?.metrics?.cls=="number"?result.metrics.cls:null,ttfbMs=typeof result?.metrics?.ttfbMs=="number"?result.metrics.ttfbMs:null,fcpMs=typeof result?.metrics?.fcpMs=="number"?result.metrics.fcpMs:null,ratings={lcp:rateMs(lcpMs,2500,4e3),inp:rateMs(inpMs,200,500),cls:rateScore(cls,.1,.25),ttfb:rateMs(ttfbMs,800,1800),fcp:rateMs(fcpMs,1800,3e3)},lcpHint=typeof result?.lcp?.selectorHint=="string"?result.lcp.selectorHint:null,recommendations=buildRecommendations({ratings,lcpSelectorHint:lcpHint}),notes=Array.isArray(result?.notes)?result.notes:[];notes.push(`Ratings: LCP=${formatRating(ratings.lcp.rating)}, INP=${formatRating(ratings.inp.rating)}, CLS=${formatRating(ratings.cls.rating)}.`);let output={url:pageUrl,title:pageTitle,timestampMs,metrics:{lcpMs,inpMs,cls,ttfbMs,fcpMs},ratings,recommendations,notes};return includeDebug&&result?.debug&&(output.debug=result.debug),output}};import{z as z36}from"zod";var NewTraceId=class{name(){return"o11y_new-trace-id"}description(){return"Generates new OpenTelemetry compatible trace id and sets it to the current session."}inputSchema(){return{}}outputSchema(){return{traceId:z36.string().describe("The generated new OpenTelemetry compatible trace id.")}}async handle(context,args){let traceId=newTraceId();return await context.setTraceId(traceId),{traceId}}};import{z as z37}from"zod";var SetTraceId=class{name(){return"o11y_set-trace-id"}description(){return"Sets the OpenTelemetry compatible trace id of the current session."}inputSchema(){return{traceId:z37.string()}}outputSchema(){return{}}async handle(context,args){return await context.setTraceId(args.traceId),{}}};var tools7=[new GetConsoleMessages,new GetHttpRequests,new GetTraceId,new GetWebVitals,new NewTraceId,new SetTraceId];import{z as z38}from"zod";var DEFAULT_MAX_STACK_DEPTH=30,DEFAULT_MAX_PROPS_PREVIEW_CHARS=2e3,DEFAULT_INCLUDE_PROPS_PREVIEW=!0,DEFAULT_MAX_ELEMENT_PATH_DEPTH=25,DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN=5e4,GetComponentForElement=class{name(){return"react_get-component-for-element"}description(){return`
496
+ `.trim()}inputSchema(){return{waitMs:z40.number().int().min(0).max(MAX_WAIT_MS).optional().default(DEFAULT_WAIT_MS).describe("Ms to wait before reading (e.g. let LCP settle)."),includeDebug:z40.boolean().optional().default(!1).describe("Include entry counts and LCP element hint.")}}outputSchema(){let ratingSchema=z40.object({rating:z40.enum(["good","needs_improvement","poor","not_available"]).describe("Rating based on Google thresholds."),value:z40.number().nullable().describe("Metric value (null if unavailable)."),unit:z40.enum(["ms","score"]).describe("Unit of the metric."),thresholds:z40.object({good:z40.number().describe('Upper bound for the "good" rating.'),poor:z40.number().describe('Lower bound for the "poor" rating.')}).describe("Thresholds used for rating.")});return{url:z40.string().describe("Current page URL."),title:z40.string().describe("Current page title."),timestampMs:z40.number().int().describe("Unix epoch timestamp (ms) when the metrics were captured."),metrics:z40.object({lcpMs:z40.number().nullable().describe("Largest Contentful Paint in milliseconds."),inpMs:z40.number().nullable().describe("Interaction to Next Paint in milliseconds (best-effort approximation)."),cls:z40.number().nullable().describe("Cumulative Layout Shift score."),ttfbMs:z40.number().nullable().describe("Time to First Byte in milliseconds."),fcpMs:z40.number().nullable().describe("First Contentful Paint in milliseconds.")}).describe("Raw metric values (null if unavailable)."),ratings:z40.object({lcp:ratingSchema.describe("LCP rating."),inp:ratingSchema.describe("INP rating."),cls:ratingSchema.describe("CLS rating."),ttfb:ratingSchema.describe("TTFB rating."),fcp:ratingSchema.describe("FCP rating.")}).describe("Ratings computed from Google thresholds."),recommendations:z40.object({coreWebVitalsPassed:z40.boolean().describe('True if all Core Web Vitals are rated "good".'),summary:z40.array(z40.string()).describe("High-level summary and prioritization guidance."),lcp:z40.array(z40.string()).describe("Recommendations for improving LCP."),inp:z40.array(z40.string()).describe("Recommendations for improving INP."),cls:z40.array(z40.string()).describe("Recommendations for improving CLS."),ttfb:z40.array(z40.string()).describe("Recommendations for improving TTFB."),fcp:z40.array(z40.string()).describe("Recommendations for improving FCP."),general:z40.array(z40.string()).describe("General measurement and debugging notes.")}).describe("Recommendations based on the measured values and their ratings."),notes:z40.array(z40.string()).describe("Notes about metric availability, browser limitations, and interpretation."),debug:z40.object({waitMs:z40.number().int().describe("Actual wait duration used before reading metrics."),entries:z40.object({navigation:z40.number().int().describe("Count of navigation entries."),paint:z40.number().int().describe("Count of paint entries."),lcp:z40.number().int().describe("Count of largest-contentful-paint entries."),layoutShift:z40.number().int().describe("Count of layout-shift entries."),eventTiming:z40.number().int().describe("Count of event timing entries.")}).describe("Counts of PerformanceEntry types used to compute metrics."),lastLcpSelectorHint:z40.string().nullable().describe("Best-effort selector hint for the last LCP element (if available)."),lastLcpTagName:z40.string().nullable().describe("Tag name of the last LCP element (if available).")}).optional().describe("Optional debug details.")}}async handle(context,args){let waitMs=args.waitMs??DEFAULT_WAIT_MS,includeDebug=args.includeDebug===!0,pageUrl=String(context.page.url()),pageTitle=String(await context.page.title()),timestampMs=Date.now(),result=await context.page.evaluate(async params=>{let waitMsEval=params.waitMsEval,includeDebugEval=params.includeDebugEval,notes2=[],sleep=ms=>new Promise(resolve=>{setTimeout(()=>resolve(),ms)});waitMsEval>0&&await sleep(waitMsEval);let ttfbMs2=null,navEntries=performance.getEntriesByType("navigation")??[];if(navEntries.length>0){let nav=navEntries[0];typeof nav.responseStart=="number"&&nav.responseStart>=0&&(ttfbMs2=nav.responseStart)}else notes2.push("TTFB: navigation entries not available (older browser or restricted timing).");let fcpMs2=null,paintEntries=performance.getEntriesByType("paint")??[],fcp=paintEntries.find(e=>e.name==="first-contentful-paint");fcp&&typeof fcp.startTime=="number"?fcpMs2=fcp.startTime:notes2.push("FCP: paint entries not available (browser may not support Paint Timing).");let lcpMs2=null,lastLcpEl=null,lcpEntries=performance.getEntriesByType("largest-contentful-paint")??[];if(lcpEntries.length>0){let last=lcpEntries[lcpEntries.length-1];typeof last.startTime=="number"&&(lcpMs2=last.startTime),last.element&&last.element instanceof Element&&(lastLcpEl=last.element)}else notes2.push("LCP: largest-contentful-paint entries not available (requires LCP support).");let selectorHintFor=el=>{if(!el)return null;let dt=el.getAttribute("data-testid")||el.getAttribute("data-test-id")||el.getAttribute("data-test");if(dt&&dt.trim())return'[data-testid="'+dt.replace(/"/g,'\\"')+'"]';let ds=el.getAttribute("data-selector");if(ds&&ds.trim())return'[data-selector="'+ds.replace(/"/g,'\\"')+'"]';if(el.id)try{return"#"+CSS.escape(el.id)}catch{return"#"+String(el.id)}return el.tagName?el.tagName.toLowerCase():null},cls2=null,layoutShiftEntries=performance.getEntriesByType("layout-shift")??[];if(layoutShiftEntries.length>0){let sum=0;for(let e of layoutShiftEntries)e&&e.hadRecentInput===!0||typeof e.value=="number"&&(sum+=e.value);cls2=sum}else notes2.push("CLS: layout-shift entries not available (requires Layout Instability API support).");let inpMs2=null,eventEntries=performance.getEntriesByType("event")??[];if(eventEntries.length>0){let maxDur=0;for(let e of eventEntries){let interactionId=typeof e.interactionId=="number"?e.interactionId:0,duration=typeof e.duration=="number"?e.duration:0;interactionId>0&&duration>maxDur&&(maxDur=duration)}maxDur>0?inpMs2=maxDur:notes2.push("INP: event timing entries exist but no interactionId-based events were found.")}else notes2.push("INP: event timing entries not available (requires Event Timing API support).");notes2.length===0?notes2.push("All requested metrics were available."):notes2.push("Some metrics may be null due to browser support limitations.");let out={metrics:{ttfbMs:ttfbMs2,fcpMs:fcpMs2,lcpMs:lcpMs2,cls:cls2,inpMs:inpMs2},notes:notes2,lcp:{selectorHint:selectorHintFor(lastLcpEl),tagName:lastLcpEl?String(lastLcpEl.tagName).toLowerCase():null},debug:{waitMs:waitMsEval,entries:{navigation:navEntries.length,paint:paintEntries.length,lcp:lcpEntries.length,layoutShift:layoutShiftEntries.length,eventTiming:eventEntries.length}}};return includeDebugEval?(out.debug.lastLcpSelectorHint=out.lcp.selectorHint,out.debug.lastLcpTagName=out.lcp.tagName):delete out.debug,out},{waitMsEval:waitMs,includeDebugEval:includeDebug}),lcpMs=typeof result?.metrics?.lcpMs=="number"?result.metrics.lcpMs:null,inpMs=typeof result?.metrics?.inpMs=="number"?result.metrics.inpMs:null,cls=typeof result?.metrics?.cls=="number"?result.metrics.cls:null,ttfbMs=typeof result?.metrics?.ttfbMs=="number"?result.metrics.ttfbMs:null,fcpMs=typeof result?.metrics?.fcpMs=="number"?result.metrics.fcpMs:null,ratings={lcp:rateMs(lcpMs,2500,4e3),inp:rateMs(inpMs,200,500),cls:rateScore(cls,.1,.25),ttfb:rateMs(ttfbMs,800,1800),fcp:rateMs(fcpMs,1800,3e3)},lcpHint=typeof result?.lcp?.selectorHint=="string"?result.lcp.selectorHint:null,recommendations=buildRecommendations({ratings,lcpSelectorHint:lcpHint}),notes=Array.isArray(result?.notes)?result.notes:[];notes.push(`Ratings: LCP=${formatRating(ratings.lcp.rating)}, INP=${formatRating(ratings.inp.rating)}, CLS=${formatRating(ratings.cls.rating)}.`);let output={url:pageUrl,title:pageTitle,timestampMs,metrics:{lcpMs,inpMs,cls,ttfbMs,fcpMs},ratings,recommendations,notes};return includeDebug&&result?.debug&&(output.debug=result.debug),output}};import{z as z41}from"zod";var NewTraceId=class{name(){return"o11y_new-trace-id"}description(){return"Generates new OpenTelemetry compatible trace id and sets it to the current session."}inputSchema(){return{}}outputSchema(){return{traceId:z41.string().describe("The generated new OpenTelemetry compatible trace id.")}}async handle(context,args){let traceId=newTraceId();return await context.setTraceId(traceId),{traceId}}};import{z as z42}from"zod";var SetTraceContext=class{name(){return"o11y_set-trace-context"}description(){return"Sets or clears the OpenTelemetry trace context. Empty traceId clears the MCP-pinned trace id (new browser traces get random ids). Empty traceState clears tracestate. Non-empty traceState must be valid W3C tracestate (comma-separated key=value list)."}inputSchema(){return{traceId:z42.string().optional(),traceState:z42.string().optional()}}outputSchema(){return{}}async handle(context,args){let hasTraceId=Object.prototype.hasOwnProperty.call(args,"traceId"),hasTraceState=Object.prototype.hasOwnProperty.call(args,"traceState");if(!hasTraceId&&!hasTraceState)throw new Error("At least one of traceId or traceState must be provided (use empty string to clear / reset).");if(hasTraceId){let tid=args.traceId;if(tid===void 0)throw new Error("traceId was provided but is undefined.");tid.trim()===""?await context.clearTraceId():await context.setTraceId(tid)}if(hasTraceState){let ts=args.traceState;if(ts===void 0)throw new Error("traceState was provided but is undefined.");ts.trim()===""?await context.clearTraceState():await context.setTraceState(ts)}return{}}};var tools7=[new GetConsoleMessages,new GetHttpRequests,new GetTraceContext,new GetWebVitals,new NewTraceId,new SetTraceContext];import{z as z43}from"zod";var DEFAULT_MAX_STACK_DEPTH=30,DEFAULT_MAX_PROPS_PREVIEW_CHARS=2e3,DEFAULT_INCLUDE_PROPS_PREVIEW=!0,DEFAULT_MAX_ELEMENT_PATH_DEPTH=25,DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN=5e4,GetComponentForElement=class{name(){return"react_get-component-for-element"}description(){return`
484
497
  Finds React component(s) for a DOM element via React Fiber (best-effort). Give selector or (x,y); we resolve the element,
485
498
  find __reactFiber$ on it or ancestors, then build the component stack from the host fiber that owns that node.
486
499
  Fiber is not a public API\u2014results vary by dev/prod build; names can be displayName, wrappers, or minified.
487
500
  wrappersDetected/wrapperFrames help with memo/forwardRef/context. If hostMapping.strategy is ancestor-fallback,
488
501
  use a more specific selector or deeper node for better accuracy.
489
- `.trim()}inputSchema(){return{selector:z38.string().optional().describe("Target element; takes precedence over x/y."),x:z38.number().int().optional().describe("Viewport X when selector omitted."),y:z38.number().int().optional().describe("Viewport Y when selector omitted."),maxStackDepth:z38.number().int().positive().optional().default(DEFAULT_MAX_STACK_DEPTH),includePropsPreview:z38.boolean().optional().default(DEFAULT_INCLUDE_PROPS_PREVIEW).describe("If true, includes a best-effort, truncated props preview for the nearest component."),maxPropsPreviewChars:z38.number().int().positive().optional().default(DEFAULT_MAX_PROPS_PREVIEW_CHARS).describe("Maximum characters for props preview (after safe stringification).")}}outputSchema(){return{target:z38.object({selector:z38.string().nullable(),point:z38.object({x:z38.number().int().nullable(),y:z38.number().int().nullable()}),found:z38.boolean(),domHint:z38.string().nullable(),elementPath:z38.string().nullable()}),react:z38.object({detected:z38.boolean(),detectionReason:z38.string(),fiberKey:z38.string().nullable(),hostMapping:z38.object({fiberOnTargetElement:z38.boolean().describe("Whether the initial fiber pointer was found directly on the target element."),anchorDomHint:z38.string().nullable().describe("DOM hint of the element where fiber pointer was found (target or ancestor)."),targetDomHint:z38.string().nullable().describe("DOM hint of the actual target element."),hostFiberMatchedTarget:z38.boolean().describe("Whether we found the exact host fiber for target element (fiber.stateNode === targetEl) via subtree scan."),subtreeScanNodesScanned:z38.number().int().describe("Number of fiber nodes scanned during subtree search."),subtreeScanMaxNodes:z38.number().int().describe("Maximum fiber nodes allowed to scan (safety cap)."),strategy:z38.enum(["direct-on-target","ancestor-subtree-scan","ancestor-fallback"]).describe("Mapping strategy used to produce the final stack.")}),nearestComponent:z38.object({name:z38.string().nullable(),displayName:z38.string().nullable(),kind:z38.enum(["function","class","unknown"]),debugSource:z38.object({fileName:z38.string().optional(),lineNumber:z38.number().int().optional(),columnNumber:z38.number().int().optional()}).optional(),propsPreview:z38.string().optional()}).nullable(),componentStack:z38.array(z38.object({name:z38.string().nullable(),displayName:z38.string().nullable(),kind:z38.enum(["function","class","unknown"]),debugSource:z38.object({fileName:z38.string().optional(),lineNumber:z38.number().int().optional(),columnNumber:z38.number().int().optional()}).optional()})),componentStackText:z38.string(),wrappersDetected:z38.array(z38.string()),wrapperFrames:z38.array(z38.object({wrapper:z38.string(),frameIndex:z38.number().int(),frameLabel:z38.string()})),notes:z38.array(z38.string())})}}async handle(context,args){let selector=typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():void 0,x=typeof args.x=="number"&&Number.isFinite(args.x)?Math.floor(args.x):void 0,y=typeof args.y=="number"&&Number.isFinite(args.y)?Math.floor(args.y):void 0;if(!selector&&(typeof x!="number"||typeof y!="number"))throw new Error("Provide either selector, or both x and y for elementFromPoint.");let maxStackDepth=typeof args.maxStackDepth=="number"&&args.maxStackDepth>0?Math.floor(args.maxStackDepth):DEFAULT_MAX_STACK_DEPTH,includePropsPreview=args.includePropsPreview===void 0?DEFAULT_INCLUDE_PROPS_PREVIEW:args.includePropsPreview===!0,maxPropsPreviewChars=typeof args.maxPropsPreviewChars=="number"&&Number.isFinite(args.maxPropsPreviewChars)&&args.maxPropsPreviewChars>0?Math.floor(args.maxPropsPreviewChars):DEFAULT_MAX_PROPS_PREVIEW_CHARS;return await context.page.evaluate(params=>{let selectorEval=params.selectorEval,xEval=params.xEval,yEval=params.yEval,maxStackDepthEval=params.maxStackDepthEval,includePropsPreviewEval=params.includePropsPreviewEval,maxPropsPreviewCharsEval=params.maxPropsPreviewCharsEval,maxElementPathDepthEval=params.maxElementPathDepthEval,maxFiberNodesToScanEval=params.maxFiberNodesToScanEval,notes=[];function domHint(el){if(!el)return null;let tag=el.tagName.toLowerCase(),idVal=el.id&&el.id.trim()?"#"+el.id.trim():"",clsRaw=typeof el.className=="string"?el.className:"",cls=clsRaw?"."+clsRaw.trim().split(/\s+/).slice(0,4).join("."):"";return tag+idVal+cls}function buildElementPath(el,maxDepth){if(!el)return null;let parts=[],cur2=el,depth=0;for(;cur2&&depth<maxDepth;){let tag=cur2.tagName?cur2.tagName.toLowerCase():"unknown",idVal=cur2.id&&cur2.id.trim()?"#"+cur2.id.trim():"",clsRaw=typeof cur2.className=="string"?cur2.className:"",clsList=clsRaw?clsRaw.trim().split(/\s+/).filter(Boolean).slice(0,3):[],cls=clsList.length>0?"."+clsList.join("."):"",nth="";try{let parent=cur2.parentElement;if(parent){let siblings=Array.from(parent.children).filter(c=>c.tagName===cur2.tagName);if(siblings.length>1){let idx=siblings.indexOf(cur2)+1;idx>0&&(nth=`:nth-of-type(${idx})`)}}}catch{}parts.push(`${tag}${idVal}${cls}${nth}`),cur2=cur2.parentElement,depth++}return parts.reverse(),parts.join(" > ")}function findFiberKeyOn(el){if(!el)return null;let keys=Object.getOwnPropertyNames(el);for(let k of keys)if(k.startsWith("__reactFiber$")||k.startsWith("__reactInternalInstance$"))return k;return null}function findFiberForElement(start){if(!start)return null;let directKey=findFiberKeyOn(start);if(directKey){let fiber=start[directKey];if(fiber)return{fiber,fiberKey:directKey,onTarget:!0,anchorEl:start}}let cur2=start;for(;cur2;){let k=findFiberKeyOn(cur2);if(k){let fiber=cur2[k];if(fiber)return{fiber,fiberKey:k,onTarget:!1,anchorEl:cur2}}cur2=cur2.parentElement}return null}function findHostFiberForDomElement(subtreeRootFiber,targetEl2,maxNodesToScan){if(!subtreeRootFiber)return{hostFiber:null,scanned:0,found:!1};if(subtreeRootFiber.stateNode===targetEl2)return{hostFiber:subtreeRootFiber,scanned:1,found:!0};let queue=[],visited=new Set;queue.push(subtreeRootFiber),visited.add(subtreeRootFiber);let scanned=0;for(;queue.length>0;){let f=queue.shift();if(scanned++,f&&f.stateNode===targetEl2)return{hostFiber:f,scanned,found:!0};if(scanned>=maxNodesToScan)return{hostFiber:null,scanned,found:!1};let child=f?f.child:null;child&&!visited.has(child)&&(visited.add(child),queue.push(child));let sibling=f?f.sibling:null;sibling&&!visited.has(sibling)&&(visited.add(sibling),queue.push(sibling))}return{hostFiber:null,scanned,found:!1}}function isClassComponentType(t){if(!t)return!1;let proto=t.prototype;return!!(proto&&proto.isReactComponent)}function typeDisplayName(t){if(!t)return null;if(typeof t=="function"){let dn=t.displayName;return typeof dn=="string"&&dn.trim()?dn.trim():typeof t.name=="string"&&t.name.trim()?t.name.trim():"Anonymous"}if(typeof t=="object"){let dn=t.displayName;if(typeof dn=="string"&&dn.trim())return dn.trim();let render=t.render;if(typeof render=="function"){let rdn=render.displayName;return typeof rdn=="string"&&rdn.trim()?rdn.trim():typeof render.name=="string"&&render.name.trim()?render.name.trim():"Anonymous"}}return null}function typeName(t){if(!t)return null;if(typeof t=="function")return typeof t.name=="string"&&t.name.trim()?t.name.trim():null;if(typeof t=="object"){let render=t.render;return typeof render=="function"&&typeof render.name=="string"&&render.name.trim()?render.name.trim():null}return null}function unwrapType(t){if(!t)return t;if(typeof t=="object"){let render=t.render;if(typeof render=="function")return render}return t}function isMeaningfulComponentFiber(f){return f?typeof unwrapType(f.type??f.elementType)=="function":!1}function inferKind(f){if(!f)return"unknown";let t=unwrapType(f.type??f.elementType);return typeof t=="function"?isClassComponentType(t)?"class":"function":"unknown"}function getDebugSource(f){let ds=f?f._debugSource:void 0;if(!ds||typeof ds!="object")return;let out={};return typeof ds.fileName=="string"&&(out.fileName=ds.fileName),typeof ds.lineNumber=="number"&&(out.lineNumber=ds.lineNumber),typeof ds.columnNumber=="number"&&(out.columnNumber=ds.columnNumber),Object.keys(out).length>0?out:void 0}function safeStringify(v,maxChars){let seen=new WeakSet;function helper(x2,depth){if(x2===null)return null;let t=typeof x2;if(t==="string")return x2.length>500?x2.slice(0,500)+"\u2026":x2;if(t==="number"||t==="boolean")return x2;if(t==="bigint")return String(x2);if(t!=="undefined"){if(t==="function")return"[function]";if(t==="symbol")return String(x2);if(t==="object"){if(x2 instanceof Element)return"[Element "+(x2.tagName?x2.tagName.toLowerCase():"")+"]";if(x2 instanceof Window)return"[Window]";if(x2 instanceof Document)return"[Document]";if(x2 instanceof Date)return x2.toISOString();if(seen.has(x2))return"[circular]";if(seen.add(x2),Array.isArray(x2))return depth<=0?"[array len="+x2.length+"]":x2.slice(0,20).map(it=>helper(it,depth-1));if(depth<=0)return"[object]";let out={},keys=Object.keys(x2).slice(0,40);for(let k of keys)try{out[k]=helper(x2[k],depth-1)}catch{out[k]="[unreadable]"}return out}return String(x2)}}let s="";try{s=JSON.stringify(helper(v,2))}catch{try{s=String(v)}catch{s="[unserializable]"}}return s.length>maxChars?s.slice(0,maxChars)+"\u2026(truncated)":s}function stackTextFromFrames(frames){return frames.map(f=>{let dn=f?.displayName,nm=f?.name;return typeof dn=="string"&&dn.trim()?dn.trim():typeof nm=="string"&&nm.trim()?nm.trim():"Anonymous"}).filter(x2=>!!x2).reverse().join(" > ")}function frameFromFiber(f){let t=unwrapType(f.type??f.elementType),name=typeName(t),displayName=typeDisplayName(t),kind=inferKind(f),debugSource=getDebugSource(f),frame={name,displayName,kind};return debugSource&&(frame.debugSource=debugSource),frame}function makeFrameKey(frame){let dn=frame.displayName?frame.displayName:"",nm=frame.name?frame.name:"",src=frame.debugSource,srcKey=src&&typeof src=="object"?`${String(src.fileName??"")}:${String(src.lineNumber??"")}:${String(src.columnNumber??"")}`:"";return`${dn}|${nm}|${frame.kind}|${srcKey}`}function collapseConsecutiveDuplicates(frames){let out=[],lastKey=null;for(let f of frames){let k=makeFrameKey(f);lastKey&&k===lastKey||(out.push(f),lastKey=k)}return out}function detectWrappers(frames){let found=new Set;function canonicalAdd(key){let v=key.trim().toLowerCase();v&&found.add(v)}for(let f of frames){let label=typeof f.displayName=="string"&&f.displayName.trim()?f.displayName.trim():typeof f.name=="string"&&f.name.trim()?f.name.trim():"";if(!label)continue;let l=label.toLowerCase();/^memo(\(|$)/i.test(label)&&canonicalAdd("memo"),l.includes("forwardref")&&canonicalAdd("forwardref"),l.includes(".provider")&&canonicalAdd("context-provider"),l.includes(".consumer")&&canonicalAdd("context-consumer"),l.includes("suspense")&&canonicalAdd("suspense"),l.includes("fragment")&&canonicalAdd("fragment"),l.includes("strictmode")&&canonicalAdd("strictmode"),l.includes("profiler")&&canonicalAdd("profiler")}let out=Array.from(found);return out.sort((a,b)=>a<b?-1:a>b?1:0),out}function findWrapperFrames(frames,wrappersDetected2){let out=[],wrappers=new Set(wrappersDetected2.map(w=>w.toLowerCase()));for(let i=0;i<frames.length;i++){let f=frames[i],label=f.displayName&&f.displayName.trim()?f.displayName.trim():f.name&&f.name.trim()?f.name.trim():"Anonymous",l=label.toLowerCase();for(let w of wrappers){let hit=!1;w==="memo"?hit=/^memo(\(|$)/i.test(label):w==="forwardref"?hit=l.includes("forwardref"):w==="context-provider"?hit=l.includes(".provider"):w==="context-consumer"?hit=l.includes(".consumer"):w==="suspense"?hit=l.includes("suspense"):w==="fragment"?hit=l.includes("fragment"):w==="strictmode"?hit=l.includes("strictmode"):w==="profiler"&&(hit=l.includes("profiler")),hit&&out.push({wrapper:w,frameIndex:i,frameLabel:label})}}return out}let targetEl=null;if(selectorEval&&selectorEval.trim())try{targetEl=document.querySelector(selectorEval)}catch{targetEl=null}else typeof xEval=="number"&&typeof yEval=="number"&&(targetEl=document.elementFromPoint(xEval,yEval));let foundEl=!!targetEl,targetHint=domHint(targetEl),elementPath=buildElementPath(targetEl,maxElementPathDepthEval);if(!foundEl)return{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!1,domHint:null,elementPath:null},react:{detected:!1,detectionReason:"Target element not found.",fiberKey:null,hostMapping:{fiberOnTargetElement:!1,anchorDomHint:null,targetDomHint:null,hostFiberMatchedTarget:!1,subtreeScanNodesScanned:0,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes:["No DOM element could be resolved; cannot inspect React."]}};let fiberHit=findFiberForElement(targetEl);if(!fiberHit)return notes.push("No React fiber pointer found on the element or its ancestors."),notes.push("This can happen if the page is not React, uses a different renderer, or runs with hardened/minified internals."),{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!0,domHint:targetHint,elementPath},react:{detected:!1,detectionReason:"React fiber not found on element/ancestors.",fiberKey:null,hostMapping:{fiberOnTargetElement:!1,anchorDomHint:null,targetDomHint:targetHint,hostFiberMatchedTarget:!1,subtreeScanNodesScanned:0,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes}};let hostFiber=fiberHit.fiber,hostMatch=fiberHit.onTarget,subtreeScanned=0,anchorHint=domHint(fiberHit.anchorEl),strategy="direct-on-target";if(!fiberHit.onTarget){strategy="ancestor-fallback";let scanRes=findHostFiberForDomElement(fiberHit.fiber,targetEl,maxFiberNodesToScanEval);subtreeScanned=scanRes.scanned,scanRes.found&&scanRes.hostFiber?(hostFiber=scanRes.hostFiber,hostMatch=!0,strategy="ancestor-subtree-scan",notes.push(`Mapped target DOM element to host fiber by scanning fiber subtree (scanned=${subtreeScanned}).`)):(hostMatch=!1,notes.push(`Could not find exact host fiber for the target element in the ancestor fiber subtree (scanned=${subtreeScanned}). Falling back to ancestor fiber stack; some frames may be unrelated.`))}let hostMapping={fiberOnTargetElement:fiberHit.onTarget,anchorDomHint:anchorHint,targetDomHint:targetHint,hostFiberMatchedTarget:hostMatch,subtreeScanNodesScanned:subtreeScanned,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy},nearest=null,cur=hostFiber;for(;cur;){if(isMeaningfulComponentFiber(cur)){nearest=cur;break}cur=cur.return??null}nearest||notes.push("Fiber was found, but no meaningful function/class component was detected in the return chain.");let stack=[],seenFibers=new Set,stackCur=nearest;for(;stackCur&&stack.length<maxStackDepthEval;){if(seenFibers.has(stackCur)){notes.push("Detected a cycle in fiber.return chain; stopping stack traversal.");break}seenFibers.add(stackCur),isMeaningfulComponentFiber(stackCur)&&stack.push(frameFromFiber(stackCur)),stackCur=stackCur.return??null}let collapsedStack=stack.length>0?collapseConsecutiveDuplicates(stack):[],componentStackText=stackTextFromFrames(collapsedStack),wrappersDetected=detectWrappers(collapsedStack),wrapperFrames=findWrapperFrames(collapsedStack,wrappersDetected);wrappersDetected.length>=3&&notes.push(`Wrapper-heavy stack detected (${wrappersDetected.join(", ")}). Interpreting nearestComponent may require skipping wrappers.`);let nearestOut=null;if(nearest&&(nearestOut=frameFromFiber(nearest),includePropsPreviewEval))try{let props=nearest.memoizedProps;props!==void 0?nearestOut.propsPreview=safeStringify(props,maxPropsPreviewCharsEval):notes.push("memoizedProps not available on nearest component fiber.")}catch{notes.push("Failed to read memoizedProps for nearest component (best-effort).")}return notes.push("React Fiber inspection uses non-public internals; fields are best-effort."),notes.push("Component names may come from displayName, wrappers, third-party libraries, or minified production builds."),strategy==="ancestor-fallback"&&notes.push("Host mapping fallback was used; consider selecting a deeper/more specific DOM element for a more accurate component stack."),{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!0,domHint:targetHint,elementPath},react:{detected:!0,detectionReason:strategy==="direct-on-target"?"React fiber found on the target element.":strategy==="ancestor-subtree-scan"?"React fiber found on an ancestor; exact host fiber located via subtree scan.":"React fiber found on an ancestor; exact host fiber not found, stack may include unrelated frames.",fiberKey:fiberHit.fiberKey,hostMapping,nearestComponent:nearestOut,componentStack:collapsedStack,componentStackText,wrappersDetected,wrapperFrames,notes}}},{selectorEval:selector??null,xEval:typeof x=="number"?x:null,yEval:typeof y=="number"?y:null,maxStackDepthEval:maxStackDepth,includePropsPreviewEval:includePropsPreview,maxPropsPreviewCharsEval:maxPropsPreviewChars,maxElementPathDepthEval:DEFAULT_MAX_ELEMENT_PATH_DEPTH,maxFiberNodesToScanEval:DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN})}};import{z as z39}from"zod";var DEFAULT_MATCH_STRATEGY="contains",DEFAULT_MAX_ELEMENTS=200,DEFAULT_ONLY_VISIBLE2=!0,DEFAULT_ONLY_IN_VIEWPORT2=!0,DEFAULT_TEXT_PREVIEW_MAX_LENGTH2=80,DEFAULT_MAX_MATCHES=5,DEFAULT_STACK_LIMIT=50,INTERNAL_MAX_ROOTS_SCAN=20,INTERNAL_MAX_FIBERS_VISITED=25e4,GetElementForComponent=class{name(){return"react_get-element-for-component"}description(){return`
502
+ `.trim()}inputSchema(){return{selector:z43.string().optional().describe("Target element; takes precedence over x/y."),x:z43.number().int().optional().describe("Viewport X when selector omitted."),y:z43.number().int().optional().describe("Viewport Y when selector omitted."),maxStackDepth:z43.number().int().positive().optional().default(DEFAULT_MAX_STACK_DEPTH),includePropsPreview:z43.boolean().optional().default(DEFAULT_INCLUDE_PROPS_PREVIEW).describe("If true, includes a best-effort, truncated props preview for the nearest component."),maxPropsPreviewChars:z43.number().int().positive().optional().default(DEFAULT_MAX_PROPS_PREVIEW_CHARS).describe("Maximum characters for props preview (after safe stringification).")}}outputSchema(){return{target:z43.object({selector:z43.string().nullable(),point:z43.object({x:z43.number().int().nullable(),y:z43.number().int().nullable()}),found:z43.boolean(),domHint:z43.string().nullable(),elementPath:z43.string().nullable()}),react:z43.object({detected:z43.boolean(),detectionReason:z43.string(),fiberKey:z43.string().nullable(),hostMapping:z43.object({fiberOnTargetElement:z43.boolean().describe("Whether the initial fiber pointer was found directly on the target element."),anchorDomHint:z43.string().nullable().describe("DOM hint of the element where fiber pointer was found (target or ancestor)."),targetDomHint:z43.string().nullable().describe("DOM hint of the actual target element."),hostFiberMatchedTarget:z43.boolean().describe("Whether we found the exact host fiber for target element (fiber.stateNode === targetEl) via subtree scan."),subtreeScanNodesScanned:z43.number().int().describe("Number of fiber nodes scanned during subtree search."),subtreeScanMaxNodes:z43.number().int().describe("Maximum fiber nodes allowed to scan (safety cap)."),strategy:z43.enum(["direct-on-target","ancestor-subtree-scan","ancestor-fallback"]).describe("Mapping strategy used to produce the final stack.")}),nearestComponent:z43.object({name:z43.string().nullable(),displayName:z43.string().nullable(),kind:z43.enum(["function","class","unknown"]),debugSource:z43.object({fileName:z43.string().optional(),lineNumber:z43.number().int().optional(),columnNumber:z43.number().int().optional()}).optional(),propsPreview:z43.string().optional()}).nullable(),componentStack:z43.array(z43.object({name:z43.string().nullable(),displayName:z43.string().nullable(),kind:z43.enum(["function","class","unknown"]),debugSource:z43.object({fileName:z43.string().optional(),lineNumber:z43.number().int().optional(),columnNumber:z43.number().int().optional()}).optional()})),componentStackText:z43.string(),wrappersDetected:z43.array(z43.string()),wrapperFrames:z43.array(z43.object({wrapper:z43.string(),frameIndex:z43.number().int(),frameLabel:z43.string()})),notes:z43.array(z43.string())})}}async handle(context,args){let selector=typeof args.selector=="string"&&args.selector.trim()?args.selector.trim():void 0,x=typeof args.x=="number"&&Number.isFinite(args.x)?Math.floor(args.x):void 0,y=typeof args.y=="number"&&Number.isFinite(args.y)?Math.floor(args.y):void 0;if(!selector&&(typeof x!="number"||typeof y!="number"))throw new Error("Provide either selector, or both x and y for elementFromPoint.");let maxStackDepth=typeof args.maxStackDepth=="number"&&args.maxStackDepth>0?Math.floor(args.maxStackDepth):DEFAULT_MAX_STACK_DEPTH,includePropsPreview=args.includePropsPreview===void 0?DEFAULT_INCLUDE_PROPS_PREVIEW:args.includePropsPreview===!0,maxPropsPreviewChars=typeof args.maxPropsPreviewChars=="number"&&Number.isFinite(args.maxPropsPreviewChars)&&args.maxPropsPreviewChars>0?Math.floor(args.maxPropsPreviewChars):DEFAULT_MAX_PROPS_PREVIEW_CHARS;return await context.page.evaluate(params=>{let selectorEval=params.selectorEval,xEval=params.xEval,yEval=params.yEval,maxStackDepthEval=params.maxStackDepthEval,includePropsPreviewEval=params.includePropsPreviewEval,maxPropsPreviewCharsEval=params.maxPropsPreviewCharsEval,maxElementPathDepthEval=params.maxElementPathDepthEval,maxFiberNodesToScanEval=params.maxFiberNodesToScanEval,notes=[];function domHint(el){if(!el)return null;let tag=el.tagName.toLowerCase(),idVal=el.id&&el.id.trim()?"#"+el.id.trim():"",clsRaw=typeof el.className=="string"?el.className:"",cls=clsRaw?"."+clsRaw.trim().split(/\s+/).slice(0,4).join("."):"";return tag+idVal+cls}function buildElementPath(el,maxDepth){if(!el)return null;let parts=[],cur2=el,depth=0;for(;cur2&&depth<maxDepth;){let tag=cur2.tagName?cur2.tagName.toLowerCase():"unknown",idVal=cur2.id&&cur2.id.trim()?"#"+cur2.id.trim():"",clsRaw=typeof cur2.className=="string"?cur2.className:"",clsList=clsRaw?clsRaw.trim().split(/\s+/).filter(Boolean).slice(0,3):[],cls=clsList.length>0?"."+clsList.join("."):"",nth="";try{let parent=cur2.parentElement;if(parent){let siblings=Array.from(parent.children).filter(c=>c.tagName===cur2.tagName);if(siblings.length>1){let idx=siblings.indexOf(cur2)+1;idx>0&&(nth=`:nth-of-type(${idx})`)}}}catch{}parts.push(`${tag}${idVal}${cls}${nth}`),cur2=cur2.parentElement,depth++}return parts.reverse(),parts.join(" > ")}function findFiberKeyOn(el){if(!el)return null;let keys=Object.getOwnPropertyNames(el);for(let k of keys)if(k.startsWith("__reactFiber$")||k.startsWith("__reactInternalInstance$"))return k;return null}function findFiberForElement(start){if(!start)return null;let directKey=findFiberKeyOn(start);if(directKey){let fiber=start[directKey];if(fiber)return{fiber,fiberKey:directKey,onTarget:!0,anchorEl:start}}let cur2=start;for(;cur2;){let k=findFiberKeyOn(cur2);if(k){let fiber=cur2[k];if(fiber)return{fiber,fiberKey:k,onTarget:!1,anchorEl:cur2}}cur2=cur2.parentElement}return null}function findHostFiberForDomElement(subtreeRootFiber,targetEl2,maxNodesToScan){if(!subtreeRootFiber)return{hostFiber:null,scanned:0,found:!1};if(subtreeRootFiber.stateNode===targetEl2)return{hostFiber:subtreeRootFiber,scanned:1,found:!0};let queue=[],visited=new Set;queue.push(subtreeRootFiber),visited.add(subtreeRootFiber);let scanned=0;for(;queue.length>0;){let f=queue.shift();if(scanned++,f&&f.stateNode===targetEl2)return{hostFiber:f,scanned,found:!0};if(scanned>=maxNodesToScan)return{hostFiber:null,scanned,found:!1};let child=f?f.child:null;child&&!visited.has(child)&&(visited.add(child),queue.push(child));let sibling=f?f.sibling:null;sibling&&!visited.has(sibling)&&(visited.add(sibling),queue.push(sibling))}return{hostFiber:null,scanned,found:!1}}function isClassComponentType(t){if(!t)return!1;let proto=t.prototype;return!!(proto&&proto.isReactComponent)}function typeDisplayName(t){if(!t)return null;if(typeof t=="function"){let dn=t.displayName;return typeof dn=="string"&&dn.trim()?dn.trim():typeof t.name=="string"&&t.name.trim()?t.name.trim():"Anonymous"}if(typeof t=="object"){let dn=t.displayName;if(typeof dn=="string"&&dn.trim())return dn.trim();let render=t.render;if(typeof render=="function"){let rdn=render.displayName;return typeof rdn=="string"&&rdn.trim()?rdn.trim():typeof render.name=="string"&&render.name.trim()?render.name.trim():"Anonymous"}}return null}function typeName(t){if(!t)return null;if(typeof t=="function")return typeof t.name=="string"&&t.name.trim()?t.name.trim():null;if(typeof t=="object"){let render=t.render;return typeof render=="function"&&typeof render.name=="string"&&render.name.trim()?render.name.trim():null}return null}function unwrapType(t){if(!t)return t;if(typeof t=="object"){let render=t.render;if(typeof render=="function")return render}return t}function isMeaningfulComponentFiber(f){return f?typeof unwrapType(f.type??f.elementType)=="function":!1}function inferKind(f){if(!f)return"unknown";let t=unwrapType(f.type??f.elementType);return typeof t=="function"?isClassComponentType(t)?"class":"function":"unknown"}function getDebugSource(f){let ds=f?f._debugSource:void 0;if(!ds||typeof ds!="object")return;let out={};return typeof ds.fileName=="string"&&(out.fileName=ds.fileName),typeof ds.lineNumber=="number"&&(out.lineNumber=ds.lineNumber),typeof ds.columnNumber=="number"&&(out.columnNumber=ds.columnNumber),Object.keys(out).length>0?out:void 0}function safeStringify(v,maxChars){let seen=new WeakSet;function helper(x2,depth){if(x2===null)return null;let t=typeof x2;if(t==="string")return x2.length>500?x2.slice(0,500)+"\u2026":x2;if(t==="number"||t==="boolean")return x2;if(t==="bigint")return String(x2);if(t!=="undefined"){if(t==="function")return"[function]";if(t==="symbol")return String(x2);if(t==="object"){if(x2 instanceof Element)return"[Element "+(x2.tagName?x2.tagName.toLowerCase():"")+"]";if(x2 instanceof Window)return"[Window]";if(x2 instanceof Document)return"[Document]";if(x2 instanceof Date)return x2.toISOString();if(seen.has(x2))return"[circular]";if(seen.add(x2),Array.isArray(x2))return depth<=0?"[array len="+x2.length+"]":x2.slice(0,20).map(it=>helper(it,depth-1));if(depth<=0)return"[object]";let out={},keys=Object.keys(x2).slice(0,40);for(let k of keys)try{out[k]=helper(x2[k],depth-1)}catch{out[k]="[unreadable]"}return out}return String(x2)}}let s="";try{s=JSON.stringify(helper(v,2))}catch{try{s=String(v)}catch{s="[unserializable]"}}return s.length>maxChars?s.slice(0,maxChars)+"\u2026(truncated)":s}function stackTextFromFrames(frames){return frames.map(f=>{let dn=f?.displayName,nm=f?.name;return typeof dn=="string"&&dn.trim()?dn.trim():typeof nm=="string"&&nm.trim()?nm.trim():"Anonymous"}).filter(x2=>!!x2).reverse().join(" > ")}function frameFromFiber(f){let t=unwrapType(f.type??f.elementType),name=typeName(t),displayName=typeDisplayName(t),kind=inferKind(f),debugSource=getDebugSource(f),frame={name,displayName,kind};return debugSource&&(frame.debugSource=debugSource),frame}function makeFrameKey(frame){let dn=frame.displayName?frame.displayName:"",nm=frame.name?frame.name:"",src=frame.debugSource,srcKey=src&&typeof src=="object"?`${String(src.fileName??"")}:${String(src.lineNumber??"")}:${String(src.columnNumber??"")}`:"";return`${dn}|${nm}|${frame.kind}|${srcKey}`}function collapseConsecutiveDuplicates(frames){let out=[],lastKey=null;for(let f of frames){let k=makeFrameKey(f);lastKey&&k===lastKey||(out.push(f),lastKey=k)}return out}function detectWrappers(frames){let found=new Set;function canonicalAdd(key){let v=key.trim().toLowerCase();v&&found.add(v)}for(let f of frames){let label=typeof f.displayName=="string"&&f.displayName.trim()?f.displayName.trim():typeof f.name=="string"&&f.name.trim()?f.name.trim():"";if(!label)continue;let l=label.toLowerCase();/^memo(\(|$)/i.test(label)&&canonicalAdd("memo"),l.includes("forwardref")&&canonicalAdd("forwardref"),l.includes(".provider")&&canonicalAdd("context-provider"),l.includes(".consumer")&&canonicalAdd("context-consumer"),l.includes("suspense")&&canonicalAdd("suspense"),l.includes("fragment")&&canonicalAdd("fragment"),l.includes("strictmode")&&canonicalAdd("strictmode"),l.includes("profiler")&&canonicalAdd("profiler")}let out=Array.from(found);return out.sort((a,b)=>a<b?-1:a>b?1:0),out}function findWrapperFrames(frames,wrappersDetected2){let out=[],wrappers=new Set(wrappersDetected2.map(w=>w.toLowerCase()));for(let i=0;i<frames.length;i++){let f=frames[i],label=f.displayName&&f.displayName.trim()?f.displayName.trim():f.name&&f.name.trim()?f.name.trim():"Anonymous",l=label.toLowerCase();for(let w of wrappers){let hit=!1;w==="memo"?hit=/^memo(\(|$)/i.test(label):w==="forwardref"?hit=l.includes("forwardref"):w==="context-provider"?hit=l.includes(".provider"):w==="context-consumer"?hit=l.includes(".consumer"):w==="suspense"?hit=l.includes("suspense"):w==="fragment"?hit=l.includes("fragment"):w==="strictmode"?hit=l.includes("strictmode"):w==="profiler"&&(hit=l.includes("profiler")),hit&&out.push({wrapper:w,frameIndex:i,frameLabel:label})}}return out}let targetEl=null;if(selectorEval&&selectorEval.trim())try{targetEl=document.querySelector(selectorEval)}catch{targetEl=null}else typeof xEval=="number"&&typeof yEval=="number"&&(targetEl=document.elementFromPoint(xEval,yEval));let foundEl=!!targetEl,targetHint=domHint(targetEl),elementPath=buildElementPath(targetEl,maxElementPathDepthEval);if(!foundEl)return{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!1,domHint:null,elementPath:null},react:{detected:!1,detectionReason:"Target element not found.",fiberKey:null,hostMapping:{fiberOnTargetElement:!1,anchorDomHint:null,targetDomHint:null,hostFiberMatchedTarget:!1,subtreeScanNodesScanned:0,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes:["No DOM element could be resolved; cannot inspect React."]}};let fiberHit=findFiberForElement(targetEl);if(!fiberHit)return notes.push("No React fiber pointer found on the element or its ancestors."),notes.push("This can happen if the page is not React, uses a different renderer, or runs with hardened/minified internals."),{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!0,domHint:targetHint,elementPath},react:{detected:!1,detectionReason:"React fiber not found on element/ancestors.",fiberKey:null,hostMapping:{fiberOnTargetElement:!1,anchorDomHint:null,targetDomHint:targetHint,hostFiberMatchedTarget:!1,subtreeScanNodesScanned:0,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes}};let hostFiber=fiberHit.fiber,hostMatch=fiberHit.onTarget,subtreeScanned=0,anchorHint=domHint(fiberHit.anchorEl),strategy="direct-on-target";if(!fiberHit.onTarget){strategy="ancestor-fallback";let scanRes=findHostFiberForDomElement(fiberHit.fiber,targetEl,maxFiberNodesToScanEval);subtreeScanned=scanRes.scanned,scanRes.found&&scanRes.hostFiber?(hostFiber=scanRes.hostFiber,hostMatch=!0,strategy="ancestor-subtree-scan",notes.push(`Mapped target DOM element to host fiber by scanning fiber subtree (scanned=${subtreeScanned}).`)):(hostMatch=!1,notes.push(`Could not find exact host fiber for the target element in the ancestor fiber subtree (scanned=${subtreeScanned}). Falling back to ancestor fiber stack; some frames may be unrelated.`))}let hostMapping={fiberOnTargetElement:fiberHit.onTarget,anchorDomHint:anchorHint,targetDomHint:targetHint,hostFiberMatchedTarget:hostMatch,subtreeScanNodesScanned:subtreeScanned,subtreeScanMaxNodes:maxFiberNodesToScanEval,strategy},nearest=null,cur=hostFiber;for(;cur;){if(isMeaningfulComponentFiber(cur)){nearest=cur;break}cur=cur.return??null}nearest||notes.push("Fiber was found, but no meaningful function/class component was detected in the return chain.");let stack=[],seenFibers=new Set,stackCur=nearest;for(;stackCur&&stack.length<maxStackDepthEval;){if(seenFibers.has(stackCur)){notes.push("Detected a cycle in fiber.return chain; stopping stack traversal.");break}seenFibers.add(stackCur),isMeaningfulComponentFiber(stackCur)&&stack.push(frameFromFiber(stackCur)),stackCur=stackCur.return??null}let collapsedStack=stack.length>0?collapseConsecutiveDuplicates(stack):[],componentStackText=stackTextFromFrames(collapsedStack),wrappersDetected=detectWrappers(collapsedStack),wrapperFrames=findWrapperFrames(collapsedStack,wrappersDetected);wrappersDetected.length>=3&&notes.push(`Wrapper-heavy stack detected (${wrappersDetected.join(", ")}). Interpreting nearestComponent may require skipping wrappers.`);let nearestOut=null;if(nearest&&(nearestOut=frameFromFiber(nearest),includePropsPreviewEval))try{let props=nearest.memoizedProps;props!==void 0?nearestOut.propsPreview=safeStringify(props,maxPropsPreviewCharsEval):notes.push("memoizedProps not available on nearest component fiber.")}catch{notes.push("Failed to read memoizedProps for nearest component (best-effort).")}return notes.push("React Fiber inspection uses non-public internals; fields are best-effort."),notes.push("Component names may come from displayName, wrappers, third-party libraries, or minified production builds."),strategy==="ancestor-fallback"&&notes.push("Host mapping fallback was used; consider selecting a deeper/more specific DOM element for a more accurate component stack."),{target:{selector:selectorEval??null,point:{x:typeof xEval=="number"?xEval:null,y:typeof yEval=="number"?yEval:null},found:!0,domHint:targetHint,elementPath},react:{detected:!0,detectionReason:strategy==="direct-on-target"?"React fiber found on the target element.":strategy==="ancestor-subtree-scan"?"React fiber found on an ancestor; exact host fiber located via subtree scan.":"React fiber found on an ancestor; exact host fiber not found, stack may include unrelated frames.",fiberKey:fiberHit.fiberKey,hostMapping,nearestComponent:nearestOut,componentStack:collapsedStack,componentStackText,wrappersDetected,wrapperFrames,notes}}},{selectorEval:selector??null,xEval:typeof x=="number"?x:null,yEval:typeof y=="number"?y:null,maxStackDepthEval:maxStackDepth,includePropsPreviewEval:includePropsPreview,maxPropsPreviewCharsEval:maxPropsPreviewChars,maxElementPathDepthEval:DEFAULT_MAX_ELEMENT_PATH_DEPTH,maxFiberNodesToScanEval:DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN})}};import{z as z44}from"zod";var DEFAULT_MATCH_STRATEGY="contains",DEFAULT_MAX_ELEMENTS=200,DEFAULT_ONLY_VISIBLE2=!0,DEFAULT_ONLY_IN_VIEWPORT2=!0,DEFAULT_TEXT_PREVIEW_MAX_LENGTH2=80,DEFAULT_MAX_MATCHES=5,DEFAULT_STACK_LIMIT=50,INTERNAL_MAX_ROOTS_SCAN=20,INTERNAL_MAX_FIBERS_VISITED=25e4,GetElementForComponent=class{name(){return"react_get-element-for-component"}description(){return`
490
503
  Maps a React component instance to the DOM elements it renders (DOM footprint) by traversing the Fiber graph.
491
504
  Prefer an anchor (anchorSelector or anchorX/anchorY) to target the instance; optionally add a query
492
505
  (componentName, fileNameHint, lineNumber) to search Fiber. With both, we rank candidates and pick the best match near the anchor.
493
506
  React DevTools hook gives reliable root discovery (getFiberRoots); without it we fall back to DOM scan for __reactFiber$ (best-effort).
494
507
  For more reliable roots in a persistent browser, install the React Developer Tools Chrome extension.
495
508
  Debug source is best-effort and may be missing in some builds.
496
- `.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`
509
+ `.trim()}inputSchema(){return{anchorSelector:z44.string().optional().describe("Anchor selector/ref."),anchorX:z44.number().int().nonnegative().optional().describe("Viewport X when anchorSelector omitted."),anchorY:z44.number().int().nonnegative().optional().describe("Viewport Y when anchorSelector omitted."),componentName:z44.string().optional(),matchStrategy:z44.enum(["exact","contains"]).optional().default(DEFAULT_MATCH_STRATEGY),fileNameHint:z44.string().optional().describe("File hint."),lineNumber:z44.number().int().positive().optional(),maxElements:z44.number().int().positive().optional().default(DEFAULT_MAX_ELEMENTS),onlyVisible:z44.boolean().optional().default(DEFAULT_ONLY_VISIBLE2),onlyInViewport:z44.boolean().optional().default(DEFAULT_ONLY_IN_VIEWPORT2),textPreviewMaxLength:z44.number().int().positive().optional().default(DEFAULT_TEXT_PREVIEW_MAX_LENGTH2),maxMatches:z44.number().int().positive().optional().default(DEFAULT_MAX_MATCHES)}}outputSchema(){return{reactDetected:z44.boolean().describe("True if __REACT_DEVTOOLS_GLOBAL_HOOK__ looks available."),fiberDetected:z44.boolean().describe("True if DOM appears to contain React Fiber pointers (__reactFiber$...)."),rootDiscovery:z44.enum(["devtools-hook","dom-fiber-scan","none"]).describe("How roots were discovered."),component:z44.object({name:z44.string().nullable(),displayName:z44.string().nullable(),debugSource:z44.object({fileName:z44.string().nullable(),lineNumber:z44.number().int().nullable(),columnNumber:z44.number().int().nullable()}).nullable(),componentStack:z44.array(z44.string()),selection:z44.enum(["anchor","query","query+anchor","unknown"]),scoring:z44.object({score:z44.number(),nameMatched:z44.boolean(),debugSourceMatched:z44.boolean(),anchorRelated:z44.boolean(),proximityPx:z44.number().nullable().optional()}).optional()}).nullable(),candidates:z44.array(z44.object({name:z44.string().nullable(),displayName:z44.string().nullable(),debugSource:z44.object({fileName:z44.string().nullable(),lineNumber:z44.number().int().nullable(),columnNumber:z44.number().int().nullable()}).nullable(),componentStack:z44.array(z44.string()),selection:z44.enum(["anchor","query","query+anchor","unknown"]),scoring:z44.object({score:z44.number(),nameMatched:z44.boolean(),debugSourceMatched:z44.boolean(),anchorRelated:z44.boolean(),proximityPx:z44.number().nullable().optional()}).optional(),domFootprintCount:z44.number().int()})).describe("Ranked candidate matches (best-first)."),elements:z44.array(z44.object({tagName:z44.string(),id:z44.string().nullable(),className:z44.string().nullable(),selectorHint:z44.string().nullable(),textPreview:z44.string().nullable(),boundingBox:z44.object({x:z44.number(),y:z44.number(),width:z44.number(),height:z44.number()}).nullable(),isVisible:z44.boolean(),isInViewport:z44.boolean()})),notes:z44.array(z44.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 z45}from"zod";var ScenarioAdd=class{name(){return"scenario-add"}description(){return`
510
+ Adds a new scenario. A scenario is a reusable JS script (like <execute>) that can call tools via callTool().
511
+ Scenarios are stored in .browser-devtools-mcp/scenarios.json (project-level by default, or global with scope="global").
512
+ `.trim()}inputSchema(){return{name:z45.string().describe("Unique scenario name (used as key)."),description:z45.string().describe("Description of what this scenario does."),script:z45.string().describe("JavaScript code to execute. Same sandbox as <execute>: use await callTool(name, input, returnOutput?) to invoke tools."),scope:z45.enum(["project","global"]).optional().default("project").describe("Storage scope (default: project).")}}outputSchema(){return{scenario:z45.object({name:z45.string(),description:z45.string(),script:z45.string()})}}async handle(_context,args){let scope=args.scope==="global"?"global":"project",store=new ScenarioStore(scope),scenario={name:args.name,description:args.description,script:args.script};return store.add(scenario),{scenario}}};import{z as z46}from"zod";var ScenarioDelete=class{name(){return"scenario-delete"}description(){return`
513
+ Deletes a scenario by name.
514
+ `.trim()}inputSchema(){return{name:z46.string().describe("Name of the scenario to delete."),scope:z46.enum(["project","global"]).optional().default("project").describe("Storage scope (default: project).")}}outputSchema(){return{deleted:z46.boolean().describe("Whether the scenario was found and deleted.")}}async handle(_context,args){let scope=args.scope==="global"?"global":"project";return{deleted:new ScenarioStore(scope).delete(args.name)}}};import{z as z47}from"zod";var ScenarioList=class{name(){return"scenario-list"}description(){return`
515
+ Lists all available scenarios. When scope is omitted, returns scenarios from both project and global scopes (project overrides global for same name).
516
+ `.trim()}inputSchema(){return{scope:z47.enum(["project","global"]).optional().describe("Filter by scope. Omit to list from both (project overrides global).")}}outputSchema(){return{scenarios:z47.array(z47.object({name:z47.string(),description:z47.string(),script:z47.string()}))}}async handle(_context,args){if(args.scope){let scope=args.scope==="global"?"global":"project";return{scenarios:new ScenarioStore(scope).list()}}return{scenarios:listAllScenarios()}}};init_logger();var SearchStrategy=(SearchStrategy3=>(SearchStrategy3.SIMPLE="SIMPLE",SearchStrategy3.FTS5="FTS5",SearchStrategy3))(SearchStrategy||{});function createSearchEngine(strategy){switch(strategy){case"FTS5":try{let{Fts5Strategy:Fts5Strategy2}=(init_fts5_strategy(),__toCommonJS(fts5_strategy_exports));return new Fts5Strategy2}catch(err){return error(`search: FTS5 strategy failed to initialize, falling back to SIMPLE: ${err}`),_createSimpleEngine()}case"SIMPLE":return _createSimpleEngine();default:return error(`search: unknown strategy "${strategy}", falling back to SIMPLE`),_createSimpleEngine()}}function _createSimpleEngine(){let{MiniSearchStrategy:MiniSearchStrategy2}=(init_minisearch_strategy(),__toCommonJS(minisearch_strategy_exports));return new MiniSearchStrategy2}function resolveSearchStrategy(value){let upper=value.toUpperCase();return upper in SearchStrategy?SearchStrategy[upper]:(error(`search: unknown strategy "${value}", falling back to SIMPLE`),"SIMPLE")}import{z as z48}from"zod";var DEFAULT_LIMIT=10,ScenarioSearch=class{name(){return"scenario-search"}description(){return`
517
+ Searches scenarios by query across both project and global scopes.
518
+ Uses configurable search strategy (SEARCH_STRATEGY or SCENARIO_SEARCH_STRATEGY env var).
519
+ Returns matching scenarios ranked by relevance.
520
+ `.trim()}inputSchema(){return{query:z48.string().describe("Search query."),limit:z48.number().int().optional().default(DEFAULT_LIMIT).describe(`Maximum number of results (default: ${DEFAULT_LIMIT}).`)}}outputSchema(){return{scenarios:z48.array(z48.object({name:z48.string(),description:z48.string(),script:z48.string(),score:z48.number()}))}}async handle(_context,args){let strategy=resolveSearchStrategy(SCENARIO_SEARCH_STRATEGY??SEARCH_STRATEGY),engine=createSearchEngine(strategy),allScenarios=listAllScenarios(),documents=allScenarios.map(s=>({id:s.name,fields:{name:s.name,description:s.description}}));engine.index(documents);let limit=args.limit??DEFAULT_LIMIT,results=engine.search(args.query,limit);engine.close();let scenarioMap=new Map(allScenarios.map(s=>[s.name,s])),matched=[];for(let result of results){let scenario=scenarioMap.get(result.id);scenario&&matched.push({...scenario,score:result.score})}return{scenarios:matched}}};import{z as z49}from"zod";var ScenarioUpdate=class{name(){return"scenario-update"}description(){return`
521
+ Updates an existing scenario's description and/or script.
522
+ `.trim()}inputSchema(){return{name:z49.string().describe("Name of the scenario to update."),description:z49.string().optional().describe("New description (omit to keep current)."),script:z49.string().optional().describe("New script (omit to keep current)."),scope:z49.enum(["project","global"]).optional().default("project").describe("Storage scope (default: project).")}}outputSchema(){return{scenario:z49.object({name:z49.string(),description:z49.string(),script:z49.string()})}}async handle(_context,args){let scope=args.scope==="global"?"global":"project",store=new ScenarioStore(scope),updates={};if(args.description!==void 0&&(updates.description=args.description),args.script!==void 0&&(updates.script=args.script),Object.keys(updates).length===0)throw new Error("At least one of description or script must be provided for update");return{scenario:store.update(args.name,updates)}}};var tools9=[new ScenarioAdd,new ScenarioUpdate,new ScenarioDelete,new ScenarioList,new ScenarioSearch];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 z50}from"zod";var Clear=class{name(){return"stub_clear"}description(){return`
497
523
  Clears stubs installed.
498
524
 
499
525
  - If stubId is provided, clears only that stub.
500
526
  - If stubId is omitted, clears all stubs for the current session/context.
501
- `.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`
527
+ `.trim()}inputSchema(){return{stubId:z50.string().optional().describe("Remove this stub; omit to clear all.")}}outputSchema(){return{clearedCount:z50.number().int().nonnegative().describe("Number of stubs removed.")}}async handle(context,args){return{clearedCount:clearStub(context.browserContext,args.stubId)}}};import{z as z51}from"zod";var InterceptHttpRequest=class{name(){return"stub_intercept-http-request"}description(){return`
502
528
  Installs a request interceptor stub that can modify outgoing requests before they are sent.
503
529
 
504
530
  Use cases:
@@ -511,10 +537,10 @@ Notes:
511
537
  - pattern is a glob matched against the full request URL (picomatch).
512
538
  - This modifies requests; it does not change responses.
513
539
  - times limits how many times the interceptor applies (-1 means infinite).
514
- `.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`
540
+ `.trim()}inputSchema(){return{pattern:z51.string().describe("URL glob (picomatch)."),modifications:z51.object({headers:z51.record(z51.string(),z51.string()).optional(),body:z51.union([z51.string(),z51.record(z51.string(),z51.any()),z51.array(z51.any())]).optional(),method:z51.string().optional().describe("Override method (e.g. GET, POST).")}).optional().describe("Changes to apply to the request."),delayMs:z51.number().int().nonnegative().optional(),times:z51.number().int().optional().describe("Max applications; -1 = infinite.")}}outputSchema(){return{stubId:z51.string().describe("Unique id of the installed stub."),kind:z51.literal("intercept_http_request").describe("Stub kind."),pattern:z51.string().describe("Glob pattern."),enabled:z51.boolean().describe("Whether the stub is enabled."),delayMs:z51.number().int().describe("Applied artificial delay in milliseconds."),times:z51.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 z52}from"zod";var List=class{name(){return"stub_list"}description(){return`
515
541
  Lists currently installed stubs for the active browser context/session.
516
542
  Useful to debug why certain calls are being mocked/intercepted.
517
- `.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`
543
+ `.trim()}inputSchema(){return{}}outputSchema(){return{stubs:z52.array(z52.object({id:z52.string().describe("Stub id."),kind:z52.string().describe("Stub kind."),enabled:z52.boolean().describe("Whether stub is enabled."),pattern:z52.string().describe("Glob pattern (picomatch)."),delayMs:z52.number().int().describe("Artificial delay in ms."),times:z52.number().int().describe("Max applications (-1 means infinite)."),usedCount:z52.number().int().describe("How many times it has been applied."),action:z52.string().optional().describe("For mock_response: fulfill/abort."),status:z52.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 z53}from"zod";var MockHttpResponse=class{name(){return"stub_mock-http-response"}description(){return`
518
544
  Installs a response stub for matching requests using glob patterns (picomatch).
519
545
 
520
546
  Use cases:
@@ -528,10 +554,10 @@ Notes:
528
554
  - pattern is a glob matched against the full request URL.
529
555
  - stubs are evaluated in insertion order; first match wins.
530
556
  - times limits how many times the stub applies (-1 means infinite).
531
- `.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_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`
557
+ `.trim()}inputSchema(){return{pattern:z53.string().describe("URL glob."),response:z53.object({action:z53.enum(["fulfill","abort"]).optional().default("fulfill"),status:z53.number().int().min(100).max(599).optional(),headers:z53.record(z53.string(),z53.string()).optional(),body:z53.union([z53.string(),z53.record(z53.string(),z53.any()),z53.array(z53.any())]).optional(),abortErrorCode:z53.string().optional().describe("Error code when action=abort.")}).describe("Response."),delayMs:z53.number().int().nonnegative().optional(),times:z53.number().int().optional().describe("Max times to apply; -1=infinite."),chance:z53.number().min(0).max(1).optional().describe("Probability 0\u20131; omit=always.")}}outputSchema(){return{stubId:z53.string().describe("Unique id of the installed stub (use it to clear later)."),kind:z53.literal("mock_http_response").describe("Stub kind."),pattern:z53.string().describe("Glob pattern."),enabled:z53.boolean().describe("Whether the stub is enabled."),delayMs:z53.number().int().describe("Applied artificial delay in milliseconds."),times:z53.number().int().describe("Max applications (-1 means infinite)."),chance:z53.number().optional().describe("Apply probability (omit means always)."),action:z53.enum(["fulfill","abort"]).describe("Applied action."),status:z53.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 z54}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`
532
558
  Waits until the page is network-idle: in-flight requests <= maxConnections for at least idleTimeMs (server-side tracking, no page globals).
533
559
  Use before SPAs, screenshots, or AX snapshots for stable results. With long-polling, increase maxConnections or use shorter idleTimeMs.
534
- `.trim()}inputSchema(){return{timeoutMs:z44.number().int().min(0).optional().default(DEFAULT_TIMEOUT_MS5).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_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 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,dontCloseBrowserContext:browserContextInfo.dontCloseBrowserContext??!1});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 run navigation content interaction a11y accessibility o11y react stub sync figma debug";zshCompletionOptions=` '--headless[Run in headless mode]' \\
560
+ `.trim()}inputSchema(){return{timeoutMs:z54.number().int().min(0).optional().default(DEFAULT_TIMEOUT_MS5).describe("Max wait ms before failing."),idleTimeMs:z54.number().int().min(0).optional().default(DEFAULT_IDLE_TIME_MS).describe("Network must stay idle for this many ms to resolve."),maxConnections:z54.number().int().min(0).optional().default(DEFAULT_MAX_CONNECTIONS).describe("Idle when in-flight requests <= this."),pollIntervalMs:z54.number().int().min(10).optional().default(DEFAULT_POLL_INTERVAL_MS).describe("Polling interval ms.")}}outputSchema(){return{waitedMs:z54.number().int().nonnegative().describe("Total time waited until the network became idle or the tool timed out (milliseconds)."),idleTimeMs:z54.number().int().nonnegative().describe("Idle duration required for success (milliseconds)."),timeoutMs:z54.number().int().nonnegative().describe("Maximum allowed wait time (milliseconds)."),maxConnections:z54.number().int().nonnegative().describe("Idle threshold used: in-flight requests must be <= this value."),pollIntervalMs:z54.number().int().nonnegative().describe("Polling interval used to sample the in-flight request count (milliseconds)."),finalInFlightRequests:z54.number().int().nonnegative().describe("The last observed number of in-flight requests at the moment the tool returned."),observedIdleMs:z54.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,dontCloseBrowserContext:browserContextInfo.dontCloseBrowserContext??!1});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 run navigation content interaction a11y accessibility o11y react stub sync figma debug";zshCompletionOptions=` '--headless[Run in headless mode]' \\
535
561
  '--no-headless[Run in headful mode]' \\
536
562
  '--persistent[Use persistent context]' \\
537
563
  '--no-persistent[Use ephemeral context]' \\
@@ -748,30 +774,30 @@ When asked to check for UI problems, layout issues, or visual bugs, ALWAYS follo
748
774
  // Or with page.evaluate: await page.evaluate(() => document.querySelector('button').click());
749
775
  await callTool('<a11y_take-aria-snapshot>', {}, true);
750
776
  await callTool('<content_take-screenshot>', {}, true);
751
- `.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 as execSync2}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=execSync2(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync2('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
777
+ `.trim(),platformInfo={serverInfo:{instructions:BROWSER_SERVER_INSTRUCTIONS_ENABLE?SERVER_INSTRUCTIONS:void 0,policies:BROWSER_POLICY_UI_DEBUGGING_ENABLE?[UI_DEBUGGING_POLICY]:[]},toolsInfo:{tools:tools12,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}init_logger();import{execSync as execSync2}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 path9=new URL(targetWebSocketDebuggerUrl).pathname;return`ws://${host}:${port}${path9}`}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=execSync2(`wmic process where "name like '%node%'" get ProcessId,CommandLine /format:csv`,{encoding:"utf-8",timeout:5e3}):output=execSync2('ps aux | grep -E "[n]ode|[t]s-node|[n]px"',{encoding:"utf-8",timeout:5e3});let processes=[];for(let line of output.split(`
752
778
  `)){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=execSync2(`docker ps -q -f name=${containerName}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
753
779
  `).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=execSync2(`docker top ${containerId}`,{encoding:"utf-8",timeout:5e3}).trim().split(`
754
780
  `);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{execSync2(`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(`
755
781
  `);throw new Error(`Multiple Node.js processes match "${options.processName}":
756
782
  ${procList}
757
- 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`
783
+ 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.")}init_logger();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)}};init_logger();init_logger();var NodeToolExecutor=class{async executeTool(context,tool,args){debug(`Executing Node.js tool ${tool.name()} with input: ${toJson(args)}`);let startTime=Date.now();try{let result=await tool.handle(context,args);return debug(`Executed Node.js tool ${tool.name()} successfully`),sendToolCallToCollector(args._metadata,tool.name(),args,{duration:Date.now()-startTime,tool_response:result}),result}catch(err){throw debug(`Error executing Node.js tool ${tool.name()}: ${err}`),sendToolCallToCollector(args._metadata,tool.name(),args,{duration:Date.now()-startTime,error:err instanceof Error?err.message:String(err)}),err}}};import{z as z55}from"zod";var Connect=class{name(){return"debug_connect"}description(){return`
758
784
  Connects to a Node.js process for V8 debugging. Supports PID, process name, Docker (containerId/containerName, port 9229),
759
785
  inspector port, or WebSocket URL. Activates inspector via SIGUSR1 if needed; for Docker, uses docker top + exec.
760
786
  Default host 127.0.0.1 (NODE_INSPECTOR_HOST), port 9229. Container-to-container: use host.docker.internal, map port, mount docker.sock.
761
787
  After connect, use tracepoints, logpoints, exceptionpoints, watch expressions.
762
- `.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`
788
+ `.trim()}inputSchema(){return{pid:z55.number().int().positive().optional(),processName:z55.string().optional().describe("Process name pattern to match."),containerId:z55.string().optional().describe("Docker container ID."),containerName:z55.string().optional().describe("Container name pattern."),host:z55.string().optional(),inspectorPort:z55.number().int().positive().optional().describe("Inspector port (default: 9229)."),wsUrl:z55.string().optional().describe("WebSocket URL when debugger already listening.")}}outputSchema(){return{connected:z55.boolean().describe("Whether connection was successful"),pid:z55.number().describe("Process ID"),command:z55.string().optional().describe("Process command line"),containerId:z55.string().optional().describe("Docker container ID when connected via container"),inspectorHost:z55.string().optional().describe("Inspector host"),inspectorPort:z55.number().optional().describe("Inspector port"),webSocketUrl:z55.string().optional().describe("WebSocket URL"),title:z55.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 z56}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:z56.boolean().describe("Whether disconnection was successful")}}async handle(context,_args){return await context.disconnect(),{disconnected:!0}}};import{z as z57}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`
763
789
  Retrieves snapshots captured by tracepoints, logpoints, and/or exceptionpoints.
764
790
  Optional \`types\`: array of \`tracepoint\`, \`logpoint\`, \`exceptionpoint\`. If omitted or empty, returns all.
765
791
  Response: tracepointSnapshots, logpointSnapshots, exceptionpointSnapshots. Optional probeId, fromSequence, limit apply per type.
766
792
  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.
767
- `.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-X7CLDI7H.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`
793
+ `.trim()}inputSchema(){return{types:z57.array(z57.enum(GET_SNAPSHOT_TYPES)).optional().describe("Which snapshot types to return. If omitted or empty, all are returned."),probeId:z57.string().optional().describe("Filter tracepoint or logpoint snapshots by this probe ID"),fromSequence:z57.number().int().nonnegative().optional().describe("Return snapshots with sequence number > fromSequence (per type)."),limit:z57.number().int().positive().optional().describe("Maximum number of snapshots per type."),maxCallStackDepth:z57.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxCallStackDepth).describe(`Max call stack frames per snapshot. Default ${DEFAULT_TRIM_OPTIONS.maxCallStackDepth}.`),includeScopes:z57.array(z57.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:z57.number().int().positive().optional().default(DEFAULT_TRIM_OPTIONS.maxVariablesPerScope).describe(`Max variables per scope. Default ${DEFAULT_TRIM_OPTIONS.maxVariablesPerScope}.`)}}outputSchema(){return{tracepointSnapshots:z57.array(z57.any()).describe("Tracepoint snapshots"),logpointSnapshots:z57.array(z57.any()).describe("Logpoint snapshots"),exceptionpointSnapshots:z57.array(z57.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:z57.array(z57.enum(SNAPSHOT_TYPES3)).optional().describe("Which snapshot types to clear. If omitted or empty, all are cleared."),probeId:z57.string().optional().describe("Clear only snapshots for this probe ID (tracepoint or logpoint). Ignored for exceptionpoint.")}}outputSchema(){return{tracepointCleared:z57.number().describe("Tracepoint snapshots cleared"),logpointCleared:z57.number().describe("Logpoint snapshots cleared"),exceptionpointCleared:z57.number().describe("Exceptionpoint snapshots cleared"),message:z57.string().describe("Status message")}}async handle(context,args){let{clearSnapshotsByProbe:clearSnapshotsByProbe2,listProbes:listProbes3}=await import("./core-42ZXN723.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 z58}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:z58.enum(getEnumKeyTuples(ConsoleMessageLevelName)).transform(createEnumTransformer(ConsoleMessageLevelName)).optional().describe("Filter by level (this level or higher)."),search:z58.string().optional().describe("Filter by message text."),timestamp:z58.number().int().nonnegative().optional().describe("Only messages at or after this Unix ms."),sequenceNumber:z58.number().int().nonnegative().optional().describe("Incremental: only messages with sequence > this."),limit:z58.object({count:z58.number().int().nonnegative().default(0).describe("0 = no limit."),from:z58.enum(["start","end"]).default("end").describe("Keep from start or end when truncating.")}).optional()}}outputSchema(){return{messages:z58.array(z58.object({type:z58.string().describe("Type of the console message."),text:z58.string().describe("Text of the console message."),location:z58.object({url:z58.string().describe("URL of the resource."),lineNumber:z58.number().nonnegative().describe("0-based line number."),columnNumber:z58.number().nonnegative().describe("0-based column number.")}).describe("Location of the console message.").optional(),timestamp:z58.number().int().nonnegative().describe("Unix epoch timestamp (ms)."),sequenceNumber:z58.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 z59}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:z59.enum(REMOVE_TYPES2).describe("Kind of probe to remove: tracepoint, logpoint, or watch"),id:z59.string().describe("Probe or watch expression ID to remove")}}outputSchema(){return{removed:z59.boolean().describe("Whether the probe or watch was removed"),message:z59.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:z59.array(z59.enum(LIST_TYPES2)).optional().describe("Which probe types to list. If omitted or empty, all are returned.")}}outputSchema(){return{tracepoints:z59.array(z59.any()).describe("Tracepoints"),logpoints:z59.array(z59.any()).describe("Logpoints"),watches:z59.array(z59.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:z59.array(z59.enum(PROBE_TYPES2)).optional().describe("Which probe types to clear. If omitted or empty, all are cleared.")}}outputSchema(){return{tracepointsCleared:z59.number().describe("Number of tracepoints cleared"),logpointsCleared:z59.number().describe("Number of logpoints cleared"),watchesCleared:z59.number().describe("Number of watch expressions cleared"),message:z59.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 z60}from"zod";var PutExceptionpoint2=class{name(){return"debug_put-exceptionpoint"}description(){return`
768
794
  Sets the exception tracepoint state for a Node.js process:
769
795
  - "none": Don't capture on exceptions
770
796
  - "uncaught": Capture only on uncaught exceptions
771
797
  - "all": Capture on all exceptions (caught and uncaught)
772
798
 
773
799
  When an exception occurs, a snapshot is captured with exception details.
774
- `}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`
800
+ `}inputSchema(){return{state:z60.enum(["none","uncaught","all"]).describe("Exception tracepoint state")}}outputSchema(){return{state:z60.string().describe("New exception tracepoint state"),previousState:z60.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 z61}from"zod";var PutLogpoint2=class{name(){return"debug_put-logpoint"}description(){return`
775
801
  Puts a logpoint on a Node.js backend process.
776
802
  When the logpoint is hit, the logExpression is evaluated and the result
777
803
  is captured in the snapshot's logResult field.
@@ -785,7 +811,7 @@ logExpression examples:
785
811
  - Simple value: "user.name"
786
812
  - Template: "\`User: \${user.name}, Age: \${user.age}\`"
787
813
  - Object: "{ user, timestamp: Date.now() }"
788
- `}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`
814
+ `}inputSchema(){return{urlPattern:z61.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z61.number().int().positive().describe("Line number (1-based)"),columnNumber:z61.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),logExpression:z61.string().describe("Expression to evaluate and log when hit"),condition:z61.string().describe("Conditional expression - logpoint only hits if this evaluates to true").optional(),hitCondition:z61.string().describe("Hit count condition").optional()}}outputSchema(){return{id:z61.string().describe("Logpoint ID"),urlPattern:z61.string().describe("URL pattern"),lineNumber:z61.number().describe("Line number"),logExpression:z61.string().describe("Log expression"),resolvedLocations:z61.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 z62}from"zod";var PutTracepoint2=class{name(){return"debug_put-tracepoint"}description(){return`
789
815
  Puts a non-blocking tracepoint on a Node.js backend process.
790
816
  When hit, a snapshot of the call stack and local variables is captured
791
817
  automatically without pausing execution.
@@ -799,26 +825,26 @@ DO NOT escape characters yourself.
799
825
 
800
826
  Returns resolvedLocations: number of scripts where the tracepoint was set.
801
827
  If 0, the pattern didn't match any loaded scripts.
802
- `}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`
828
+ `}inputSchema(){return{urlPattern:z62.string().describe('Script file path pattern (e.g., "server.js"). Auto-escaped.'),lineNumber:z62.number().int().positive().describe("Line number (1-based)"),columnNumber:z62.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),condition:z62.string().describe("Conditional expression - only triggers if this evaluates to true").optional(),hitCondition:z62.string().describe('Hit count condition, e.g., "== 5", ">= 10", "% 10 == 0"').optional()}}outputSchema(){return{id:z62.string().describe("Tracepoint ID"),urlPattern:z62.string().describe("URL pattern"),lineNumber:z62.number().describe("Line number"),columnNumber:z62.number().optional().describe("Column number"),condition:z62.string().optional().describe("Condition expression"),hitCondition:z62.string().optional().describe("Hit count condition"),resolvedLocations:z62.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 z63}from"zod";var ResolveSourceLocation2=class{name(){return"debug_resolve-source-location"}description(){return`
803
829
  Resolves a generated/bundled code location to its original source via source maps.
804
830
  Useful for translating minified stack traces or bundle line numbers to original TypeScript/JavaScript source.
805
831
 
806
832
  Requires an active Node.js debug connection (call <debug_connect> first).
807
833
  Input: generated script URL (e.g. file path), line, column (1-based).
808
834
  Output: original source path, line, column when a source map is available.
809
- `}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`
835
+ `}inputSchema(){return{url:z63.string().describe("Generated script URL (e.g. file:///path/to/dist/app.js)"),line:z63.number().int().positive().describe("Line number in generated code (1-based)"),column:z63.number().int().nonnegative().describe("Column number in generated code (1-based, default 1)").optional()}}outputSchema(){return{resolved:z63.boolean().describe("Whether the location was resolved to original source"),source:z63.string().optional().describe("Original source file path"),line:z63.number().optional().describe("Line number in original source (1-based)"),column:z63.number().optional().describe("Column number in original source (1-based)"),name:z63.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 z64}from"zod";var Status2=class{name(){return"debug_status"}description(){return`
810
836
  Returns the current Node.js debugging status including:
811
837
  - Whether connected and debugging is enabled
812
838
  - Source map status
813
839
  - Exceptionpoint state
814
840
  - Count of tracepoints, logpoints, watches
815
841
  - Snapshot statistics
816
- `}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`
842
+ `}inputSchema(){return{}}outputSchema(){return{connected:z64.boolean().describe("Whether connected to a Node.js process"),enabled:z64.boolean().describe("Whether debugging is enabled"),pid:z64.number().optional().describe("Connected process PID"),hasSourceMaps:z64.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:z64.string().describe("Exceptionpoint state"),tracepointCount:z64.number().describe("Number of tracepoints"),logpointCount:z64.number().describe("Number of logpoints"),watchExpressionCount:z64.number().describe("Number of watch expressions"),snapshotStats:z64.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 z65}from"zod";var AddWatch2=class{name(){return"debug_add-watch"}description(){return`
817
843
  Adds a watch expression to be evaluated at every tracepoint hit on the Node.js process.
818
844
  Watch expression results are included in the snapshot's watchResults field.
819
845
 
820
846
  Examples: "user.name", "this.state", "items.length", "JSON.stringify(config)"
821
- `}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 run 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:"run",description:"Script execution commands"},{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){let env={...process.env};return env.PLATFORM="node",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(`
847
+ `}inputSchema(){return{expression:z65.string().describe("JavaScript expression to watch")}}outputSchema(){return{id:z65.string().describe("Watch expression ID"),expression:z65.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];var tools14=[...tools13];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=tools14;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 run 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:"run",description:"Script execution commands"},{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){let env={...process.env};return env.PLATFORM="node",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(`
822
848
  `)}formatConfigForRepl(_opts){return[` console-messages-buffer = ${NODE_CONSOLE_MESSAGES_BUFFER_SIZE}`,` node-inspector-host = ${NODE_INSPECTOR_HOST??"127.0.0.1"}`].join(`
823
849
  `)}},cliProvider2=new NodeCliProvider;var SERVER_INSTRUCTIONS2=`
824
850
  Node.js Backend Debugging Platform
@@ -859,4 +885,4 @@ Node.js Debugging Best Practices:
859
885
  - Use source maps when debugging TypeScript or compiled code
860
886
  - Disconnect cleanly when done to avoid leaving inspector sessions open
861
887
  - Use exception breakpoints to catch unhandled errors
862
- `;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}from"zod";function augmentToolInputSchema(shape){if(!TOOL_INPUT_METADATA_ENABLE)return shape;let extra={};return Object.prototype.hasOwnProperty.call(shape,"_metadata")||(extra._metadata=z56.object({sessionId:z56.string().optional(),traceId:z56.string().optional()}).catchall(z56.unknown()).optional()),Object.keys(extra).length===0?shape:{...shape,...extra}}import{z as z57,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:z57.object(augmentToolInputSchema(tool.inputSchema())).strict(),outputSchema:z57.object(tool.outputSchema()).strict()})}async runTool(context,toolName,toolArgs){let canonicalKey=denormalizeToolName(toolName),toolDef=this.tools.get(canonicalKey);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,augmentToolInputSchema,ToolRegistry,isToolEnabled};
888
+ `;var nodePlatformInfo={serverInfo:{instructions:NODE_SERVER_INSTRUCTIONS_ENABLE?SERVER_INSTRUCTIONS2:void 0,policies:NODE_POLICY_DEBUGGING_ENABLE?[NODE_DEBUGGING_POLICY]:[]},toolsInfo:{tools:tools14,createToolSessionContext:createToolSessionContext2,createToolExecutor:createToolExecutor2},cliInfo:{cliProvider:cliProvider2}};function _resolvePlatformInfo(){return PLATFORM==="node"?nodePlatformInfo:platformInfo}var platformInfo2=_resolvePlatformInfo();export{augmentToolInputSchema,ToolRegistry,isToolEnabled,ScenarioRun,platformInfo2 as platformInfo};