browser-devtools-mcp 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -25
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/runner.js +158 -0
- package/dist/core-B3VLZZCP.js +1 -0
- package/dist/core-IV5QBQ2N.js +13 -0
- package/dist/core-NLBNZBEB.js +1124 -0
- package/dist/daemon-server.js +1 -1
- package/dist/index.js +2 -209
- package/dist/platform/browser/cli/runner.js +2 -0
- package/dist/platform/browser/index.d.ts +2 -0
- package/dist/platform/browser/tools/a11y/index.d.ts +2 -0
- package/dist/platform/browser/tools/content/index.d.ts +2 -0
- package/dist/platform/browser/tools/debug/index.d.ts +2 -0
- package/dist/platform/browser/tools/figma/index.d.ts +2 -0
- package/dist/platform/browser/tools/index.d.ts +5 -0
- package/dist/platform/browser/tools/interaction/index.d.ts +2 -0
- package/dist/platform/browser/tools/navigation/index.d.ts +2 -0
- package/dist/platform/browser/tools/o11y/index.d.ts +2 -0
- package/dist/platform/browser/tools/react/index.d.ts +2 -0
- package/dist/platform/browser/tools/run/index.d.ts +2 -0
- package/dist/platform/browser/tools/stub/index.d.ts +2 -0
- package/dist/platform/browser/tools/sync/index.d.ts +2 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/node/cli/runner.js +2 -0
- package/dist/platform/node/entry.js +2 -0
- package/dist/platform/node/index.d.ts +11 -0
- package/dist/platform/node/tools/debug/index.d.ts +6 -0
- package/dist/platform/node/tools/index.d.ts +10 -0
- package/dist/platform/node/tools/run/index.d.ts +2 -0
- package/dist/platform/types.d.ts +15 -0
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/types.d.ts +8 -3
- package/package.json +6 -2
- package/dist/cli.js +0 -179
- package/dist/core.js +0 -764
- package/dist/tools/a11y/index.d.ts +0 -2
- package/dist/tools/content/index.d.ts +0 -2
- package/dist/tools/debug/index.d.ts +0 -2
- package/dist/tools/figma/index.d.ts +0 -2
- package/dist/tools/interaction/index.d.ts +0 -2
- package/dist/tools/navigation/index.d.ts +0 -2
- package/dist/tools/o11y/index.d.ts +0 -2
- package/dist/tools/react/index.d.ts +0 -2
- package/dist/tools/run/index.d.ts +0 -2
- package/dist/tools/stub/index.d.ts +0 -2
- package/dist/tools/sync/index.d.ts +0 -2
- /package/dist/{otel → platform/browser/otel}/otel-initializer.bundle.js +0 -0
- /package/dist/{tools → platform/browser/tools}/figma/compare/index.d.ts +0 -0
- /package/dist/{tools → platform/browser/tools}/figma/compare/types.d.ts +0 -0
- /package/dist/{types.d.ts → platform/browser/types.d.ts} +0 -0
package/dist/core.js
DELETED
|
@@ -1,764 +0,0 @@
|
|
|
1
|
-
var na=Object.defineProperty;var i=(o,e)=>na(o,"name",{value:e,configurable:!0});import oa from"path";function Se(o){let e=process.env[o];if(!e)return;let t=e.trim();return t||void 0}i(Se,"_envStr");function pt(o){let e=Se(o);if(!e)return;let t=Number(e);if(Number.isFinite(t))return Math.floor(t)}i(pt,"_envInt");function kt(o){let e=Se(o);if(e)return e==="true"}i(kt,"_envBool");function ra(o){let e={};if(!o)return e;let t=o.split(",");for(let n of t){let r=n.trim();if(!r)continue;let s=r.indexOf("=");if(s===-1)continue;let a=r.slice(0,s).trim(),u=r.slice(s+1).trim();!a||!u||(e[a]=u)}return e}i(ra,"_parseKeyValueFromEnv");var kl=pt("PORT")??3e3,Dl=pt("SESSION_IDLE_SECONDS")??300,Al=pt("SESSION_IDLE_CHECK_SECONDS")??30,Ll=kt("SESSION_CLOSE_ON_SOCKET_CLOSE")??!1,Gt=pt("CONSOLE_MESSAGES_BUFFER_SIZE")??1e3,jt=pt("HTTP_REQUESTS_BUFFER_SIZE")??1e3,kr=kt("BROWSER_HEADLESS_ENABLE")??!0,Dr=kt("BROWSER_PERSISTENT_ENABLE")??!1,Ar=Se("BROWSER_PERSISTENT_USER_DATA_DIR")??oa.join(process.cwd(),"browser-devtools-mcp"),Lr=kt("BROWSER_USE_INSTALLED_ON_SYSTEM")??!1,Br=Se("BROWSER_EXECUTABLE_PATH"),Fr=Se("BROWSER_LOCALE"),Hr=kt("OTEL_ENABLE")??!1,Wr=Se("OTEL_SERVICE_NAME")??"frontend",Ur=Se("OTEL_SERVICE_VERSION"),or=Se("OTEL_ASSETS_DIR"),qr=Se("OTEL_INSTRUMENTATION_USER_INTERACTION_EVENTS")?.split(",")??["click"],Kt=Se("OTEL_EXPORTER_TYPE")??"none",rn=Se("OTEL_EXPORTER_HTTP_URL"),Vr=ra(Se("OTEL_EXPORTER_HTTP_HEADERS")),Dt=Se("AWS_REGION"),sn=Se("AWS_PROFILE"),an=kt("AMAZON_BEDROCK_ENABLE")??!1,zr=Se("AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID"),$r=Se("AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID"),Gr=Se("AMAZON_BEDROCK_VISION_MODEL_ID"),jr=Se("FIGMA_ACCESS_TOKEN")??"",Kr=Se("FIGMA_API_BASE_URL")??"https://api.figma.com/v1",Bl=pt("DAEMON_PORT")??2020,Fl=pt("DAEMON_SESSION_IDLE_SECONDS")??300,Hl=pt("DAEMON_SESSION_IDLE_CHECK_SECONDS")??30;var un="[BROWSER-DEVTOOLS]",At=!0,ia=process.env.DEBUG_ENABLE==="true";function ln(){return`${new Date().toLocaleTimeString(void 0,{hour:"numeric",minute:"numeric",second:"numeric",hour12:!1,timeZoneName:"short"})}`}i(ln,"_timeAsString");function cn(...o){return Xr()?o:(o||[]).map(e=>e?e instanceof Error||e.name&&e.message&&e.stack?`${e.name}: ${e.message}`:e:"")}i(cn,"_normalizeArgs");function ql(){At=!0}i(ql,"enable");function Vl(){At=!1}i(Vl,"disable");function Xr(){return ia}i(Xr,"isDebugEnabled");function be(...o){At&&Xr()&&console.debug(un,ln(),"|","DEBUG","-",...cn(...o))}i(be,"debug");function zl(...o){At&&console.info(un,ln(),"|","INFO ","-",...cn(...o))}i(zl,"info");function rr(...o){At&&console.warn(un,ln(),"|","WARN ","-",...cn(...o))}i(rr,"warn");function $l(...o){At&&console.error(un,ln(),"|","ERROR","-",...cn(...o))}i($l,"error");function sa(){let o=new WeakSet;return(e,t)=>{if(typeof t=="object"&&t!==null){if(o.has(t))return;o.add(t)}return t}}i(sa,"_getCircularReplacer");function ir(o){return JSON.stringify(o,sa())}i(ir,"toJson");import aa from"fs";import{chromium as Jr,firefox as Qr,webkit as Zr}from"playwright";var ua="chromium",Yr=new Map,mn=new Map;function la(o){return JSON.stringify(o)}i(la,"_browserKey");function ca(o){let e={headless:o.headless,executablePath:o.executablePath,handleSIGINT:!1,handleSIGTERM:!1};if(o.useInstalledOnSystem)if(o.browserType==="chromium")e.channel="chrome",e.args=["--disable-blink-features=AutomationControlled"],e.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${o.browserType} is not supported to be used from the one installed on the system`);return e}i(ca,"_browserLaunchOptions");async function ma(o){let e;switch(o.browserType){case"firefox":e=Qr;break;case"webkit":e=Zr;break;default:e=Jr;break}return e.launch(ca(o))}i(ma,"_createBrowser");async function pa(o){let e=la(o),t=Yr.get(e);if(t&&!t.isConnected()){try{await t.close().catch(()=>{})}catch{}t=void 0}return t||(t=await ma(o),Yr.set(e,t)),t}i(pa,"_getBrowser");function da(o){return o.persistent.userDataDir}i(da,"_persistentBrowserContextKey");function ba(o){let e=o.browserOptions,t={headless:e.headless,executablePath:e.executablePath,bypassCSP:!0,viewport:e.headless?void 0:null,locale:o.locale};if(e.useInstalledOnSystem)if(e.browserType==="chromium")t.channel="chrome",t.args=["--disable-blink-features=AutomationControlled"],t.ignoreDefaultArgs=["--disable-extensions"];else throw new Error(`Browser type ${e.browserType} is not supported to be used from the one installed on the system`);return t}i(ba,"_persistentBrowserContextLaunchOptions");async function fa(o){let e;switch(o.browserOptions.browserType){case"firefox":e=Qr;break;case"webkit":e=Zr;break;default:e=Jr;break}let t=o.persistent.userDataDir;aa.mkdirSync(t,{recursive:!0});let n=await e.launchPersistentContext(t,ba(o));for(let r of n.pages())try{await r.close()}catch{}return n}i(fa,"_createPersistentBrowserContext");async function ga(o){let e=da(o),t=mn.get(e);if(t&&!t.browser()?.isConnected()){try{await t.close().catch(()=>{})}catch{}t=void 0}if(!t)t=await fa(o),mn.set(e,t);else throw new Error(`There is already active persistent browser context in the user data directory: ${o.persistent?.userDataDir}`);return t}i(ga,"_getPersistentBrowserContext");async function ei(o={browserOptions:{browserType:ua,headless:kr,executablePath:Br,useInstalledOnSystem:Lr},persistent:Dr?{userDataDir:Ar}:void 0,locale:Fr}){return o.persistent?{browserContext:await ga(o)}:{browserContext:await(await pa(o.browserOptions)).newContext({viewport:o.browserOptions.headless?void 0:null,bypassCSP:!0,locale:o.locale})}}i(ei,"newBrowserContext");async function ti(o,e={}){return await o.newPage()}i(ti,"newPage");async function ni(o){await o.close();let e=!1;for(let[t,n]of mn.entries())o===n&&(mn.delete(t),e=!0);return e}i(ni,"closeBrowserContext");import oi from"fs";import Yt from"path";import{fileURLToPath as Ta}from"url";function ha(o){let e=o.trim();return e.startsWith("/")||(e="/"+e),e.endsWith("/")||(e=e+"/"),e}i(ha,"_normalizeBasePath");function ya(o){let e=o.trim();return e&&(e.endsWith("/")?e.slice(0,-1):e)}i(ya,"_normalizeUpstreamBaseUrl");function Sa(o,e){try{let n=new URL(o).pathname;if(!n.startsWith(e))return null;let r=n.slice(e.length);return r?r.startsWith("/")?r:"/"+r:null}catch{return null}}i(Sa,"_computeSuffixPath");function wa(o,e,t){try{let r=new URL(t).search??"";return o+e+r}catch{return o+e}}i(wa,"_appendSuffixToUpstream");var pn=class{static{i(this,"OTELProxy")}config;queue;workers;isRunning;isInstalled;metrics;constructor(e){let t=e.maxQueueSize??200,n=e.concurrency??2,r=e.respondNoContent??!0,s=ha(e.localPath),a=ya(e.upstreamUrl);this.config={...e,localPath:s,upstreamUrl:a,maxQueueSize:t,concurrency:n,respondNoContent:r},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(e){if(this.isInstalled)return;let t=this.config.localPath;if(!t.startsWith("/"))throw new Error('localPath must start with "/" (e.g. "/__mcp_otel/").');let n=`**${t}**`;await e.route(n,async r=>{await this._handleRoute(r)}),this.isInstalled=!0,this.isRunning||await this.start(),be(`[otel-proxy] installed route pattern: ${n} (basePath=${t}, upstreamBase=${this.config.upstreamUrl})`)}async uninstall(e){if(!this.isInstalled)return;let t=`**${this.config.localPath}**`;try{await e.unroute(t)}catch{}this.isInstalled=!1,await this.stop()}async start(){if(this.isRunning)return;this.isRunning=!0;let e=Math.max(1,this.config.concurrency);for(let t=0;t<e;t++){let n=this._workerLoop(t);this.workers.push(n)}be(`[otel-proxy] started with concurrency=${e}, 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}be("[otel-proxy] stopped")}}async _handleRoute(e){let t=e.request();if(this.metrics.routedRequests++,t.method().toUpperCase()==="OPTIONS"){await this._fulfillFast(e);return}if(this.config.shouldForward&&!this.config.shouldForward(t)){await this._fulfillFast(e);return}let n=t.url(),r=this.config.localPath,s=Sa(n,r);if(!s){await e.fallback();return}let a=wa(this.config.upstreamUrl,s,n),l=await t.postDataBuffer()??Buffer.alloc(0),c=t.headers()["content-type"]??"application/x-protobuf",p=t.method(),b={"content-type":c};if(this.config.upstreamHeaders)for(let[C,M]of Object.entries(this.config.upstreamHeaders))b[C]=M;if(this.queue.length>=this.config.maxQueueSize){this.metrics.droppedBatches++,await this._fulfillFast(e),rr(`[otel-proxy] dropped batch (queue full: ${this.queue.length}/${this.config.maxQueueSize}) suffix=${s}`);return}let h={body:l,contentType:c,createdAtMs:Date.now(),upstreamUrl:a,method:p,headers:b};this.queue.push(h),this.metrics.acceptedBatches++,await this._fulfillFast(e)}async _fulfillFast(e){let t=this.config.respondNoContent?204:200;if(t===204){await e.fulfill({status:t});return}await e.fulfill({status:t,headers:{"content-type":"text/plain; charset=utf-8"},body:""})}async _workerLoop(e){for(;this.isRunning;){let t=this.queue.shift();if(!t){await this._sleep(25);continue}this.metrics.inFlight++;try{await this._forwardUpstream(t),this.metrics.forwardedBatches++}catch(n){this.metrics.failedBatches++;let r=n instanceof Error?n.message:String(n);this.metrics.lastError=r,rr(`[otel-proxy] worker=${e} forward failed: ${r}`)}finally{this.metrics.inFlight--}}}async _forwardUpstream(e){let t=await fetch(e.upstreamUrl,{method:e.method,headers:e.headers,body:new Uint8Array(e.body)});if(t.status<200||t.status>=300){let n=await this._safeReadText(t);throw new Error(`upstream returned ${t.status} for ${e.upstreamUrl}: ${n}`)}}async _safeReadText(e){try{return(await e.text()).slice(0,500)}catch{return""}}async _sleep(e){await new Promise(t=>{setTimeout(()=>t(),e)})}};var xa=Ta(import.meta.url),Ia=Yt.dirname(xa),dn="/__mcp_otel/",Ca="otel-initializer.bundle.js";function va(){return or?or:Yt.join(Ia,"otel")}i(va,"_getOtelAssetsDir");function Ea(){if(Kt==="otlp/http"||rn){if(!rn)throw new Error('OTEL exporter HTTP url must be set when OTEL exporter type is "otlp/http"');return{type:"otlp/http",url:dn,upstreamURL:rn,headers:Vr}}else{if(Kt==="console")return{type:"console"};if(Kt==="none")return{type:"none"};throw new Error(`Invalid OTEL exporter type ${Kt}`)}}i(Ea,"_getOTELExporterConfig");function Oa(){return{userInteractionEvents:qr}}i(Oa,"_getOTELInstrumentationConfig");function Pa(){return{serviceName:Wr,serviceVersion:Ur,exporter:Ea(),instrumentation:Oa(),debug:!1}}i(Pa,"_getOTELConfig");function Ra(o,e){let t=Yt.isAbsolute(o)?o:Yt.join(process.cwd(),o),n=Yt.join(t,e);if(!oi.existsSync(n))throw new Error(`OTEL bundle not found at: ${n}`);return oi.readFileSync(n,"utf-8")}i(Ra,"_readBundleContent");async function Na(o,e){await o.evaluate(t=>{let n=globalThis;n.__MCP_DEVTOOLS__||(n.__MCP_DEVTOOLS__={}),n.__MCP_TRACE_ID__=t.traceId,n.__mcpOtel&&typeof n.__mcpOtel.init=="function"?n.__mcpOtel.init(t):(n.__MCP_DEVTOOLS__.otelInitialized=!1,n.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available while applying config")},e).catch(t=>{let n=t instanceof Error?t.message:String(t);be(`[otel-controller] applyConfigToPage failed (ignored): ${n}`)})}i(Na,"_applyConfigToPage");function _a(o,e){let t=new WeakMap,n=i(a=>{if(t.has(a))return;let u=i(async l=>{l===a.mainFrame()&&await Na(a,e())},"onFrameNavigated");t.set(a,u),a.on("framenavigated",u)},"attachToPage");for(let a of o.pages())n(a);let r=i(a=>{n(a)},"onNewPage");o.on("page",r);let s=i(()=>{try{o.off("page",r)}catch{}for(let a of o.pages()){let u=t.get(a);if(u)try{a.off("framenavigated",u)}catch{}}},"detach");return be("[otel-controller] auto-sync installed (page+framenavigated)"),{detach:s}}i(_a,"_installAutoSync");var bn=class{static{i(this,"OTELController")}browserContext;config;proxy;initialized=!1;autoSyncDetach;constructor(e){this.browserContext=e,this.config=Pa()}async init(e){if(this.initialized){be("[otel-controller] init skipped: BrowserContext already initialized");return}if(!e.traceId||!e.traceId.trim())throw new Error("[otel-controller] init requires a non-empty traceId");this.config.traceId=e.traceId;let t=va();this.config.exporter.type==="otlp/http"&&(this.proxy=new pn({localPath:dn,upstreamUrl:this.config.exporter.upstreamURL,upstreamHeaders:{...this.config.exporter.headers??{}}}),await this.proxy.install(this.browserContext)),be(`[otel-controller] exporter=${this.config.exporter.type} localBase=${dn}`+(this.config.exporter.type==="otlp/http"?` upstreamBase=${this.config.exporter.upstreamURL}`:""));let n=Ra(t,Ca),r=_a(this.browserContext,()=>this.config);this.autoSyncDetach=r.detach,await this.browserContext.addInitScript({content:n}),await this.browserContext.addInitScript(s=>{let a=globalThis;a.__MCP_DEVTOOLS__||(a.__MCP_DEVTOOLS__={}),a.__MCP_TRACE_ID__=s.traceId,a.__mcpOtel&&typeof a.__mcpOtel.init=="function"?a.__mcpOtel.init(s):(a.__MCP_DEVTOOLS__.otelInitialized=!1,a.__MCP_DEVTOOLS__.otelInitError="__mcpOtel.init is not available (initializer bundle did not install)")},this.config),this.initialized=!0,be("[otel-controller] init installed: bundle + config init scripts + auto-sync")}isOTELRequest(e){return new URL(e.url()).pathname.startsWith(dn)}async isInitialized(e){return await e.evaluate(()=>globalThis.__MCP_DEVTOOLS__?.otelInitialized===!0)}async getInitError(e){return await e.evaluate(()=>{let n=globalThis.__MCP_DEVTOOLS__?.otelInitError;if(typeof n=="string"&&n.trim())return n})}async getTraceId(e){return await e.evaluate(()=>{let t=globalThis;if(t.__mcpOtel&&typeof t.__mcpOtel.getTraceId=="function"){let r=t.__mcpOtel.getTraceId();if(typeof r=="string"&&r.trim())return r}let n=t.__MCP_TRACE_ID__;if(typeof n=="string"&&n.trim())return n})}async setTraceId(e,t){this.config.traceId=t,await e.evaluate(n=>{let r=globalThis;r.__mcpOtel&&typeof r.__mcpOtel.setTraceId=="function"?r.__mcpOtel.setTraceId(n):r.__MCP_TRACE_ID__=n},t)}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 Lt=(s=>(s.ALL="all",s.DEBUG="debug",s.INFO="info",s.WARNING="warning",s.ERROR="error",s))(Lt||{});var fn={all:{code:-1},debug:{code:0},info:{code:1},warning:{code:2},error:{code:3}},gn=(u=>(u.GET="GET",u.POST="POST",u.PUT="PUT",u.PATCH="PATCH",u.DELETE="DELETE",u.HEAD="HEAD",u.OPTIONS="OPTIONS",u))(gn||{}),Ot=(h=>(h.DOCUMENT="document",h.STYLESHEET="stylesheet",h.IMAGE="image",h.MEDIA="media",h.FONT="font",h.SCRIPT="script",h.TEXTTRACK="texttrack",h.XHR="xhr",h.FETCH="fetch",h.EVENTSOURCE="eventsource",h.WEBSOCKET="websocket",h.MANIFEST="manifest",h.OTHER="other",h))(Ot||{});import Ma from"crypto";function ve(o){let e=Object.keys(o).filter(t=>isNaN(Number(t))).map(t=>o[t]);if(e.length===0)throw new Error("Enum has no values");return e}i(ve,"getEnumKeyTuples");function dt(o,e){let t=Object.keys(o).filter(s=>isNaN(Number(s))).map(s=>o[s]),n=e?.caseInsensitive??!0,r=new Map(t.map(s=>[n?s.toLowerCase():s,s]));return s=>{let a=n?s.toLowerCase():s,u=r.get(a);if(u===void 0)throw new Error(`Invalid enum value: "${s}"`);return u}}i(dt,"createEnumTransformer");function hn(o=new Date){let e=i(t=>String(t).padStart(2,"0"),"pad");return o.getFullYear()+e(o.getMonth()+1)+e(o.getDate())+"-"+e(o.getHours())+e(o.getMinutes())+e(o.getSeconds())}i(hn,"formattedTimeForFilename");function yn(){return Ma.randomBytes(16).toString("hex")}i(yn,"newTraceId");var Sn=class{static{i(this,"ToolSessionContext")}_sessionId;options;otelController;consoleMessages=[];httpRequests=[];initialized=!1;closed=!1;traceId;_numOfInFlightRequests=0;_lastNetworkActivityTimestamp=0;browserContext;page;constructor(e,t,n,r){this._sessionId=e,this.browserContext=t,this.page=n,this.options=r,this.otelController=new bn(this.browserContext)}async init(){if(this.closed)throw new Error("Session context is already closed");if(this.initialized)throw new Error("Session context is already initialized");let e=this,t=0;this.page.on("console",r=>{e.consoleMessages.push(e._toConsoleMessage(r,++t)),e.consoleMessages.length>Gt&&e.consoleMessages.splice(0,e.consoleMessages.length-Gt)}),this.page.on("pageerror",r=>{e.consoleMessages.push(e._errorToConsoleMessage(r,++t)),e.consoleMessages.length>Gt&&e.consoleMessages.splice(0,e.consoleMessages.length-Gt)});let n=0;this.page.on("request",async r=>{e.otelController.isOTELRequest(r)||(e._numOfInFlightRequests++,e._lastNetworkActivityTimestamp=Date.now())}),this.page.on("requestfinished",async r=>{e.otelController.isOTELRequest(r)||(e._numOfInFlightRequests--,e._lastNetworkActivityTimestamp=Date.now(),e.httpRequests.push(await e._toHttpRequest(r,++n)),e.httpRequests.length>jt&&e.httpRequests.splice(0,e.httpRequests.length-jt))}),this.page.on("requestfailed",async r=>{e.otelController.isOTELRequest(r)||(e._numOfInFlightRequests--,e._lastNetworkActivityTimestamp=Date.now(),e.httpRequests.push(await e._toHttpRequest(r,++n)),e.httpRequests.length>jt&&e.httpRequests.splice(0,e.httpRequests.length-jt))}),this.options.otelEnable&&(this.traceId=yn(),await this.otelController.init({traceId:this.traceId})),this.initialized=!0}_toConsoleMessageLevelName(e){switch(e){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(e,t){let n=Date.now(),r=this._toConsoleMessageLevelName(e.type()),s=fn[r].code;return{type:e.type(),text:e.text(),level:{name:r,code:s},location:{url:e.location().url,lineNumber:e.location().lineNumber,columnNumber:e.location().columnNumber},timestamp:n,sequenceNumber:t}}_errorToConsoleMessage(e,t){let n=Date.now();return e instanceof Error?{type:"error",text:e.message,level:{name:"error",code:3},timestamp:n,sequenceNumber:t}:{type:"error",text:String(e),level:{name:"error",code:3},timestamp:n,sequenceNumber:t}}_isBodyLikelyPresent(e,t){return!(t==="HEAD"||t==="OPTIONS"||e===204||e===304||e>=300&&e<400)}async _safeReadResponseBody(e){try{let n=e.request().method(),r=e.status();return this._isBodyLikelyPresent(r,n)?(await e.body()).toString("utf-8"):void 0}catch{return}}async _toHttpRequest(e,t){let n=await e.response();return{url:e.url(),method:e.method(),headers:e.headers(),body:e.postData()||void 0,resourceType:e.resourceType(),failure:e.failure()?.errorText,duration:e.timing().responseEnd,response:n?{status:n.status(),statusText:n.statusText(),headers:n.headers(),body:await this._safeReadResponseBody(n)}:void 0,ok:n?n.ok():!1,timestamp:Math.floor(e.timing().startTime),sequenceNumber:t}}numOfInFlightRequests(){return this._numOfInFlightRequests}lastNetworkActivityTimestamp(){return this._lastNetworkActivityTimestamp}sessionId(){return this._sessionId}async getTraceId(){return this.traceId}async setTraceId(e){if(!this.options.otelEnable)throw new Error("OTEL is not enabled");this.traceId=e,await this.otelController.setTraceId(this.page,e)}getConsoleMessages(){return this.consoleMessages}getHttpRequests(){return this.httpRequests}async close(){if(this.closed)return!1;be(`Closing OTEL controller of the session with id ${this._sessionId} ...`),await this.otelController.close();try{be(`Closing browser context of the session with id ${this._sessionId} ...`),await ni(this.browserContext)}catch(e){be(`Error occurred while closing browser context of the session with id ${this._sessionId} ...`,e)}return this.consoleMessages.length=0,this.httpRequests.length=0,this.closed=!0,!0}};var ri=class{static{i(this,"ToolExecutor")}sessionIdProvider;sessionContext;constructor(e){this.sessionIdProvider=e}async _createSessionContext(){let e=await ei(),t=await ti(e.browserContext),n=this.sessionIdProvider(),r=new Sn(n,e.browserContext,t,{otelEnable:Hr});return await r.init(),r}async _sessionContext(){return this.sessionContext||(this.sessionContext=await this._createSessionContext(),be(`Created session context on the first tool call for the session with id ${this.sessionContext.sessionId()}`)),this.sessionContext}async executeTool(e,t){be(`Executing tool ${e.name()} with input: ${ir(t)}`);try{let n=await this._sessionContext(),r=await e.handle(n,t);return be(`Executed tool ${e.name()} and got output: ${ir(r)}`),r}catch(n){throw be(`Error occurred while executing ${e.name()}: ${n}`),n}}};import{z as ii}from"zod";var wn=class{static{i(this,"TakeAriaSnapshot")}name(){return"a11y_take-aria-snapshot"}description(){return`
|
|
2
|
-
Captures an ARIA (accessibility) snapshot of the current page or a specific element.
|
|
3
|
-
If a selector is provided, the snapshot is scoped to that element; otherwise, the entire page is captured.
|
|
4
|
-
The output includes the page URL, title, and a YAML-formatted accessibility tree.
|
|
5
|
-
|
|
6
|
-
**UI Debugging Usage:**
|
|
7
|
-
- Use in combination with "a11y_take-ax-tree-snapshot" tool for comprehensive UI analysis
|
|
8
|
-
- Provides semantic structure and accessibility roles
|
|
9
|
-
- Helps identify accessibility issues and page hierarchy problems
|
|
10
|
-
`}inputSchema(){return{selector:ii.string().describe("CSS selector for element to take snapshot.").optional()}}outputSchema(){return{output:ii.string().describe("Includes the page URL, title, and a YAML-formatted accessibility tree.")}}async handle(e,t){let n=await e.page.locator(t.selector||"body").ariaSnapshot();return{output:`
|
|
11
|
-
- Page URL: ${e.page.url()}
|
|
12
|
-
- Page Title: ${await e.page.title()}
|
|
13
|
-
- Page/Component Snapshot:
|
|
14
|
-
\`\`\`yaml
|
|
15
|
-
${n}
|
|
16
|
-
\`\`\`
|
|
17
|
-
`.trim()}}};import{z as x}from"zod";var si=!0,ai=!0,ui=!1,li=!1,ci=!1,mi=80,pi=["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"],ka=new Set(["button","link","textbox","checkbox","radio","combobox","switch","tab","menuitem","dialog","heading","listbox","listitem","option"]),Da=12,di=2e3;function Aa(o){let e={};if(!o)return e;for(let t=0;t<o.length;t+=2){let n=String(o[t]),r=String(o[t+1]??"");e[n]=r}return e}i(Aa,"attrsToObj");var Tn=class{static{i(this,"TakeAxTreeSnapshot")}name(){return"accessibility_take-ax-tree-snapshot"}description(){return`
|
|
18
|
-
Captures a UI-focused snapshot by combining Chromium's Accessibility (AX) tree with runtime visual diagnostics.
|
|
19
|
-
|
|
20
|
-
Use this tool to detect UI issues like:
|
|
21
|
-
- Elements that exist semantically (AX role/name) but are visually hidden or off-screen
|
|
22
|
-
- Wrong layout/geometry (bounding box, viewport intersection)
|
|
23
|
-
- Styling issues (optional computed style subset)
|
|
24
|
-
- Overlap / stacking / occlusion issues (enable "checkOcclusion")
|
|
25
|
-
|
|
26
|
-
**UI Debugging Usage:**
|
|
27
|
-
- ALWAYS use "checkOcclusion:true" when investigating UI/layout problems
|
|
28
|
-
- Provides precise bounding boxes for overlap detection
|
|
29
|
-
- Use alongside "a11y_take-aria-snapshot" tool for complete UI analysis
|
|
30
|
-
|
|
31
|
-
Important notes for AI-driven UI debugging:
|
|
32
|
-
- "boundingBox" comes from "getBoundingClientRect()" (viewport coords). It represents the layout box, not every painted pixel
|
|
33
|
-
(e.g. shadows/pseudo-elements may extend beyond it).
|
|
34
|
-
- If something looks visible but clicks fail, enable "checkOcclusion". This samples multiple points (center + corners)
|
|
35
|
-
and uses "elementFromPoint()" to identify if another element is actually on top.
|
|
36
|
-
- When not occluded, "topElement" is null (even if "elementFromPoint" returns the element itself or its descendant).
|
|
37
|
-
- "selectorHint" is a best-effort locator (prefers "data-testid" / "data-selector" / "id"). It may not be unique.
|
|
38
|
-
- Use "onlyVisible" / "onlyInViewport" to reduce output when focusing on what the user currently sees.
|
|
39
|
-
`.trim()}inputSchema(){return{roles:x.array(x.enum(["button","link","textbox","checkbox","radio","combobox","switch","tab","menuitem","dialog","heading","listbox","listitem","option"])).describe("Optional role allowlist. If omitted, a built-in set of interactive roles is used.").optional(),includeStyles:x.boolean().describe("Whether to include computed CSS styles for each node. Styles are extracted via runtime getComputedStyle().").optional().default(si),includeRuntimeVisual:x.boolean().describe("Whether to compute runtime visual information (bounding box, visibility, viewport).").optional().default(ai),checkOcclusion:x.boolean().describe("If true, checks whether each element is visually occluded by another element using elementFromPoint() sampled at multiple points. Disabled by default.").optional().default(ui),onlyVisible:x.boolean().describe("If true, only visually visible nodes are returned.").optional().default(li),onlyInViewport:x.boolean().describe("If true, only nodes intersecting the viewport are returned.").optional().default(ci),textPreviewMaxLength:x.number().int().positive().describe("Maximum length of the text preview extracted from each element.").optional().default(mi),styleProperties:x.array(x.string()).describe("List of CSS computed style properties to extract.").optional().default([...pi])}}outputSchema(){return{url:x.string().describe("The current page URL at the time the AX snapshot was captured."),title:x.string().describe("The document title of the page at the time of the snapshot."),axNodeCount:x.number().int().nonnegative().describe("Total number of nodes returned by Chromium Accessibility.getFullAXTree before filtering."),candidateCount:x.number().int().nonnegative().describe("Number of DOM-backed AX nodes that passed role filtering before enrichment."),enrichedCount:x.number().int().nonnegative().describe("Number of nodes included in the final enriched snapshot output."),truncatedBySafetyCap:x.boolean().describe("Indicates whether the result set was truncated by an internal safety cap to prevent excessive output size."),nodes:x.array(x.object({axNodeId:x.string().describe("Unique identifier of the accessibility node within the AX tree."),parentAxNodeId:x.string().nullable().describe("Parent AX node id in the full AX tree. Null if this node is a root."),childAxNodeIds:x.array(x.string()).describe("Child AX node ids in the full AX tree (may include nodes not present in the filtered output)."),role:x.string().nullable().describe("ARIA role of the accessibility node (e.g. button, link, textbox)."),name:x.string().nullable().describe("Accessible name computed by the browser accessibility engine."),ignored:x.boolean().nullable().describe("Whether the accessibility node is marked as ignored."),backendDOMNodeId:x.number().int().describe("Chromium backend DOM node identifier used to map AX nodes to DOM elements."),domNodeId:x.number().int().nullable().describe("Resolved DOM nodeId from CDP if available; may be null because nodeId is not guaranteed to be stable/resolved."),frameId:x.string().nullable().describe("Frame identifier if the node belongs to an iframe or subframe."),localName:x.string().nullable().describe("Lowercased DOM tag name of the mapped element (e.g. div, button, input)."),id:x.string().nullable().describe("DOM id attribute of the mapped element."),className:x.string().nullable().describe("DOM class attribute of the mapped element."),selectorHint:x.string().nullable().describe("Best-effort selector hint for targeting this element (prefers data-testid/data-selector/id)."),textPreview:x.string().nullable().describe("Short preview of rendered text content or aria-label, truncated to the configured maximum length."),value:x.any().nullable().describe("Raw AX value payload associated with the node, if present."),description:x.any().nullable().describe("Raw AX description payload associated with the node, if present."),properties:x.array(x.any()).nullable().describe("Additional AX properties exposed by the accessibility tree."),styles:x.record(x.string(),x.string()).optional().describe("Subset of computed CSS styles for the element as string key-value pairs."),runtime:x.object({boundingBox:x.object({x:x.number().describe("X coordinate of the element relative to the viewport."),y:x.number().describe("Y coordinate of the element relative to the viewport."),width:x.number().describe("Width of the element in CSS pixels."),height:x.number().describe("Height of the element in CSS pixels.")}).nullable().describe("Bounding box computed at runtime using getBoundingClientRect."),isVisible:x.boolean().describe("Whether the element is considered visually visible (display, visibility, opacity, and size)."),isInViewport:x.boolean().describe("Whether the element intersects the current viewport."),occlusion:x.object({samplePoints:x.array(x.object({x:x.number().describe("Sample point X (viewport coordinates) used for occlusion testing."),y:x.number().describe("Sample point Y (viewport coordinates) used for occlusion testing."),hit:x.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:x.boolean().describe("True if at least one sample point is covered by another element."),topElement:x.object({localName:x.string().nullable().describe("Tag name of the occluding element."),id:x.string().nullable().describe("DOM id of the occluding element (may be null if none)."),className:x.string().nullable().describe("DOM class of the occluding element (may be null if none)."),selectorHint:x.string().nullable().describe("Best-effort selector hint for the occluding element."),boundingBox:x.object({x:x.number().describe("X coordinate of the occluding element bounding box."),y:x.number().describe("Y coordinate of the occluding element bounding box."),width:x.number().describe("Width of the occluding element bounding box."),height:x.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:x.object({x:x.number().describe("Intersection rect X."),y:x.number().describe("Intersection rect Y."),width:x.number().describe("Intersection rect width."),height:x.number().describe("Intersection rect height."),area:x.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(e,t){let n=e.page,r=t.includeRuntimeVisual??ai,s=t.includeStyles??si,a=t.checkOcclusion??ui,u=t.onlyVisible??li,l=t.onlyInViewport??ci;if((u||l)&&!r)throw new Error("onlyVisible/onlyInViewport require includeRuntimeVisual=true.");if(a&&!r)throw new Error("checkOcclusion requires includeRuntimeVisual=true.");let m=t.textPreviewMaxLength??mi,c=t.styleProperties&&t.styleProperties.length>0?t.styleProperties:pi,p=Array.from(c).map(C=>C.toLowerCase()),b=t.roles&&t.roles.length>0?new Set(t.roles):new Set(Array.from(ka)),h=await n.context().newCDPSession(n);try{await h.send("DOM.enable"),await h.send("Accessibility.enable"),r&&await h.send("Runtime.enable");let C=await h.send("Accessibility.getFullAXTree"),M=C.nodes??C,y=new Map,T=new Map;for(let U of M){let K=String(U.nodeId),Te=(U.childIds??[]).map(pe=>String(pe));T.set(K,Te);for(let pe of Te)y.set(pe,K)}let W=M.filter(U=>{if(typeof U.backendDOMNodeId!="number")return!1;let K=U.role?.value??null;return K?b.has(String(K)):!1}),ie=W.length,Y=W.length>di;Y&&(W=W.slice(0,di));let k=[...W],B=[],F=[],q=i(async()=>{for(;k.length>0;){let U=k.shift();if(!U)return;let K=String(U.nodeId),ue=y.get(K)??null,Te=T.get(K)??[],pe=U.backendDOMNodeId,J=null,w=null,le=null,G=null,H=null;try{let Pe=(await h.send("DOM.describeNode",{backendNodeId:pe}))?.node;if(Pe){let Ne=Pe.nodeId;typeof Ne=="number"&&Ne>0?J=Ne:J=null,w=Pe.localName??(Pe.nodeName?String(Pe.nodeName).toLowerCase():null);let lt=Aa(Pe.attributes);le=lt.id??null,G=lt.class??null}}catch{}if(r)try{H=(await h.send("DOM.resolveNode",{backendNodeId:pe}))?.object?.objectId??null}catch{}let X=U.role?.value?String(U.role.value):null,Oe=U.name?.value?String(U.name.value):null,xe=typeof U.ignored=="boolean"?U.ignored:null,qe={axNodeId:K,parentAxNodeId:ue,childAxNodeIds:Te,role:X,name:Oe,ignored:xe,backendDOMNodeId:pe,domNodeId:J,frameId:U.frameId??null,localName:w,id:le,className:G,selectorHint:null,textPreview:null,value:U.value??null,description:U.description??null,properties:Array.isArray(U.properties)?U.properties:null},Ie=B.push(qe)-1;F[Ie]=H}},"worker"),z=Array.from({length:Da},()=>q());if(await Promise.all(z),r){let K=(await h.send("Runtime.evaluate",{expression:"globalThis",returnByValue:!1}))?.result?.objectId;if(K){let ue=F.map(J=>J?{objectId:J}:{value:null}),pe=(await h.send("Runtime.callFunctionOn",{objectId:K,returnByValue:!0,functionDeclaration:`
|
|
40
|
-
function(textMax, includeStyles, styleProps, checkOcclusion, ...els) {
|
|
41
|
-
function selectorHintFor(el) {
|
|
42
|
-
if (!(el instanceof Element)) { return null; }
|
|
43
|
-
|
|
44
|
-
const dt = el.getAttribute('data-testid')
|
|
45
|
-
|| el.getAttribute('data-test-id')
|
|
46
|
-
|| el.getAttribute('data-test');
|
|
47
|
-
|
|
48
|
-
if (dt && dt.trim()) { return '[data-testid="' + dt.replace(/"/g, '\\\\\\"') + '"]'; }
|
|
49
|
-
|
|
50
|
-
const ds = el.getAttribute('data-selector');
|
|
51
|
-
if (ds && ds.trim()) { return '[data-selector="' + ds.replace(/"/g, '\\\\\\"') + '"]'; }
|
|
52
|
-
|
|
53
|
-
if (el.id) { return '#' + CSS.escape(el.id); }
|
|
54
|
-
|
|
55
|
-
return el.tagName.toLowerCase();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function textPreviewFor(el) {
|
|
59
|
-
if (!(el instanceof Element)) { return null; }
|
|
60
|
-
|
|
61
|
-
const aria = el.getAttribute('aria-label');
|
|
62
|
-
if (aria && aria.trim()) { return aria.trim().slice(0, textMax); }
|
|
63
|
-
|
|
64
|
-
const txt = (el.innerText || el.textContent || '').trim();
|
|
65
|
-
if (!txt) { return null; }
|
|
66
|
-
|
|
67
|
-
return txt.slice(0, textMax);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function pickStyles(el) {
|
|
71
|
-
if (!includeStyles) { return undefined; }
|
|
72
|
-
if (!(el instanceof Element)) { return undefined; }
|
|
73
|
-
|
|
74
|
-
const s = getComputedStyle(el);
|
|
75
|
-
const out = {};
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < styleProps.length; i++) {
|
|
78
|
-
const prop = styleProps[i];
|
|
79
|
-
try { out[prop] = s.getPropertyValue(prop); } catch {}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return out;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function intersectRects(a, b) {
|
|
86
|
-
const x1 = Math.max(a.left, b.left);
|
|
87
|
-
const y1 = Math.max(a.top, b.top);
|
|
88
|
-
const x2 = Math.min(a.right, b.right);
|
|
89
|
-
const y2 = Math.min(a.bottom, b.bottom);
|
|
90
|
-
|
|
91
|
-
const w = Math.max(0, x2 - x1);
|
|
92
|
-
const h = Math.max(0, y2 - y1);
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
x: x1,
|
|
96
|
-
y: y1,
|
|
97
|
-
width: w,
|
|
98
|
-
height: h,
|
|
99
|
-
area: w * h,
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function occlusionInfoFor(el) {
|
|
104
|
-
if (!(el instanceof Element)) {
|
|
105
|
-
return {
|
|
106
|
-
samplePoints: [],
|
|
107
|
-
isOccluded: false,
|
|
108
|
-
topElement: null,
|
|
109
|
-
intersection: null,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const r = el.getBoundingClientRect();
|
|
114
|
-
const hasBox = Number.isFinite(r.left) && Number.isFinite(r.top) && r.width > 0 && r.height > 0;
|
|
115
|
-
|
|
116
|
-
if (!hasBox) {
|
|
117
|
-
return {
|
|
118
|
-
samplePoints: [],
|
|
119
|
-
isOccluded: false,
|
|
120
|
-
topElement: null,
|
|
121
|
-
intersection: null,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Sample center + 4 corners (inset) to better detect partial occlusion / overlap.
|
|
126
|
-
const inset = Math.max(1, Math.min(6, Math.floor(Math.min(r.width, r.height) / 4)));
|
|
127
|
-
const points = [
|
|
128
|
-
{ x: r.left + r.width / 2, y: r.top + r.height / 2 }, // center
|
|
129
|
-
{ x: r.left + inset, y: r.top + inset }, // top-left
|
|
130
|
-
{ x: r.right - inset, y: r.top + inset }, // top-right
|
|
131
|
-
{ x: r.left + inset, y: r.bottom - inset }, // bottom-left
|
|
132
|
-
{ x: r.right - inset, y: r.bottom - inset }, // bottom-right
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
let chosenTop = null;
|
|
136
|
-
let chosenTopRect = null;
|
|
137
|
-
let chosenIntersection = null;
|
|
138
|
-
let anyHit = false;
|
|
139
|
-
|
|
140
|
-
const samples = [];
|
|
141
|
-
|
|
142
|
-
for (const p of points) {
|
|
143
|
-
const topEl = document.elementFromPoint(p.x, p.y);
|
|
144
|
-
|
|
145
|
-
// Consider it "hit" only if topEl is not the element itself and not a descendant.
|
|
146
|
-
const hit = !!(topEl && topEl !== el && !el.contains(topEl));
|
|
147
|
-
|
|
148
|
-
samples.push({ x: p.x, y: p.y, hit });
|
|
149
|
-
|
|
150
|
-
if (hit) {
|
|
151
|
-
anyHit = true;
|
|
152
|
-
|
|
153
|
-
// Prefer the top element with the largest intersection area with this element's rect.
|
|
154
|
-
const topRect = topEl.getBoundingClientRect();
|
|
155
|
-
const inter = intersectRects(r, topRect);
|
|
156
|
-
|
|
157
|
-
if (!chosenTop || (chosenIntersection && inter.area > chosenIntersection.area)) {
|
|
158
|
-
chosenTop = topEl;
|
|
159
|
-
chosenTopRect = topRect;
|
|
160
|
-
chosenIntersection = inter;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// If not occluded, DO NOT return topElement at all (prevents "self occlusion" noise).
|
|
166
|
-
if (!anyHit || !chosenTop) {
|
|
167
|
-
return {
|
|
168
|
-
samplePoints: samples,
|
|
169
|
-
isOccluded: false,
|
|
170
|
-
topElement: null,
|
|
171
|
-
intersection: null,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const id = chosenTop.id ? chosenTop.id : null;
|
|
176
|
-
const className = chosenTop.getAttribute('class') ? chosenTop.getAttribute('class') : null;
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
samplePoints: samples,
|
|
180
|
-
isOccluded: true,
|
|
181
|
-
topElement: {
|
|
182
|
-
localName: chosenTop.tagName ? chosenTop.tagName.toLowerCase() : null,
|
|
183
|
-
id: id,
|
|
184
|
-
className: className,
|
|
185
|
-
selectorHint: selectorHintFor(chosenTop),
|
|
186
|
-
boundingBox: chosenTopRect ? {
|
|
187
|
-
x: chosenTopRect.x,
|
|
188
|
-
y: chosenTopRect.y,
|
|
189
|
-
width: chosenTopRect.width,
|
|
190
|
-
height: chosenTopRect.height,
|
|
191
|
-
} : null,
|
|
192
|
-
},
|
|
193
|
-
intersection: chosenIntersection ? {
|
|
194
|
-
x: chosenIntersection.x,
|
|
195
|
-
y: chosenIntersection.y,
|
|
196
|
-
width: chosenIntersection.width,
|
|
197
|
-
height: chosenIntersection.height,
|
|
198
|
-
area: chosenIntersection.area,
|
|
199
|
-
} : null,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const vw = innerWidth;
|
|
204
|
-
const vh = innerHeight;
|
|
205
|
-
|
|
206
|
-
return els.map((el) => {
|
|
207
|
-
if (!(el instanceof Element)) {
|
|
208
|
-
return {
|
|
209
|
-
selectorHint: null,
|
|
210
|
-
textPreview: null,
|
|
211
|
-
styles: undefined,
|
|
212
|
-
runtime: {
|
|
213
|
-
boundingBox: null,
|
|
214
|
-
isVisible: false,
|
|
215
|
-
isInViewport: false,
|
|
216
|
-
occlusion: checkOcclusion ? {
|
|
217
|
-
samplePoints: [],
|
|
218
|
-
isOccluded: false,
|
|
219
|
-
topElement: null,
|
|
220
|
-
intersection: null,
|
|
221
|
-
} : undefined,
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const r = el.getBoundingClientRect();
|
|
227
|
-
const s = getComputedStyle(el);
|
|
228
|
-
|
|
229
|
-
const isVisible =
|
|
230
|
-
s.display !== 'none'
|
|
231
|
-
&& s.visibility !== 'hidden'
|
|
232
|
-
&& parseFloat(s.opacity || '1') > 0
|
|
233
|
-
&& r.width > 0
|
|
234
|
-
&& r.height > 0;
|
|
235
|
-
|
|
236
|
-
const isInViewport =
|
|
237
|
-
r.right > 0
|
|
238
|
-
&& r.bottom > 0
|
|
239
|
-
&& r.left < vw
|
|
240
|
-
&& r.top < vh;
|
|
241
|
-
|
|
242
|
-
const occlusion = checkOcclusion ? occlusionInfoFor(el) : undefined;
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
selectorHint: selectorHintFor(el),
|
|
246
|
-
textPreview: textPreviewFor(el),
|
|
247
|
-
styles: pickStyles(el),
|
|
248
|
-
runtime: {
|
|
249
|
-
boundingBox: {
|
|
250
|
-
x: r.x,
|
|
251
|
-
y: r.y,
|
|
252
|
-
width: r.width,
|
|
253
|
-
height: r.height,
|
|
254
|
-
},
|
|
255
|
-
isVisible: isVisible,
|
|
256
|
-
isInViewport: isInViewport,
|
|
257
|
-
occlusion: occlusion,
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
`,arguments:[{value:m},{value:s},{value:p},{value:a},...ue]}))?.result?.value??[];for(let J=0;J<B.length;J++){let w=pe[J];w&&(B[J].selectorHint=w.selectorHint??null,B[J].textPreview=w.textPreview??null,w.styles&&(B[J].styles=w.styles),w.runtime&&(B[J].runtime=w.runtime))}}}let Z=B;return(u||l)&&(Z=Z.filter(U=>!(!U.runtime||u&&!U.runtime.isVisible||l&&!U.runtime.isInViewport))),{url:String(n.url()),title:String(await n.title()),axNodeCount:M.length,candidateCount:ie,enrichedCount:Z.length,truncatedBySafetyCap:Y,nodes:Z}}finally{await h.detach().catch(()=>{})}}};var bi=[new wn,new Tn];import{z as at}from"zod";var fi=5e4,xn=class{static{i(this,"GetAsHtml")}name(){return"content_get-as-html"}description(){return`
|
|
263
|
-
Gets the HTML content of the current page.
|
|
264
|
-
By default, all <script> tags are removed from the output unless "removeScripts" is explicitly set to "false".
|
|
265
|
-
`}inputSchema(){return{selector:at.string().describe("CSS selector to limit the HTML content to a specific container.").optional(),removeScripts:at.boolean().describe('Remove all script tags from the HTML (default: "true").').optional().default(!0),removeComments:at.boolean().describe('Remove all HTML comments (default: "false").').optional().default(!1),removeStyles:at.boolean().describe('Remove all style tags from the HTML (default: "false").').optional().default(!1),removeMeta:at.boolean().describe('Remove all meta tags from the HTML (default: "false").').optional().default(!1),cleanHtml:at.boolean().describe('Perform comprehensive HTML cleaning (default: "false").').optional().default(!1),minify:at.boolean().describe('Minify the HTML output (default: "false").').optional().default(!1),maxLength:at.number().int().positive().describe(`Maximum number of characters to return (default: "${fi}").`).optional().default(fi)}}outputSchema(){return{output:at.string().describe("The requested HTML content of the page.")}}async handle(e,t){let{selector:n,removeScripts:r,removeComments:s,removeStyles:a,removeMeta:u,minify:l,cleanHtml:m,maxLength:c}=t,p;if(n){let T=await e.page.$(n);if(!T)throw new Error(`Element with selector "${n}" not found`);p=await T.evaluate(W=>W.outerHTML)}else p=await e.page.content();let b=r||m,h=s||m,C=a||m,M=u||m;(b||h||C||M||l)&&(p=await e.page.evaluate(({html:T,removeScripts:W,removeComments:ie,removeStyles:Y,removeMeta:k,minify:B})=>{let F=document.createElement("template");F.innerHTML=T;let q=F.content;if(W&&q.querySelectorAll("script").forEach(j=>j.remove()),Y&&q.querySelectorAll("style").forEach(j=>j.remove()),k&&q.querySelectorAll("meta").forEach(j=>j.remove()),ie){let Z=i(j=>{let U=j.childNodes;for(let K=U.length-1;K>=0;K--){let ue=U[K];ue.nodeType===8?j.removeChild(ue):ue.nodeType===1&&Z(ue)}},"removeComments");Z(q)}let z=F.innerHTML;return B&&(z=z.replace(/>\s+</g,"><").trim()),z},{html:p,removeScripts:b,removeComments:h,removeStyles:C,removeMeta:M,minify:l}));let y=p;return y.length>c&&(y=y.slice(0,c)+`
|
|
266
|
-
<!-- Output truncated due to size limits -->`),{output:y}}};import{z as sr}from"zod";var gi=5e4,In=class{static{i(this,"GetAsText")}name(){return"content_get-as-text"}description(){return"Gets the visible text content of the current page."}inputSchema(){return{selector:sr.string().describe("CSS selector to limit the text content to a specific container.").optional(),maxLength:sr.number().int().positive().describe(`Maximum number of characters to return (default: "${gi}").`).optional().default(gi)}}outputSchema(){return{output:sr.string().describe("The requested text content of the page.")}}async handle(e,t){let{selector:n,maxLength:r}=t,a=await e.page.evaluate(({selector:u})=>{let l=u?document.querySelector(u):document.body;if(!l)throw new Error(`Element with selector "${u}" not found`);let m=document.createTreeWalker(l,NodeFilter.SHOW_TEXT,{acceptNode:i(b=>{let h=window.getComputedStyle(b.parentElement);return h.display!=="none"&&h.visibility!=="hidden"?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT},"acceptNode")}),c="",p;for(;p=m.nextNode();){let b=p.textContent?.trim();b&&(c+=b+`
|
|
267
|
-
`)}return c.trim()},{selector:n});return a.length>r&&(a=a.slice(0,r)+`
|
|
268
|
-
[Output truncated due to size limits]`),{output:a}}};import La from"os";import Ba from"path";import{z as tt}from"zod";var hi=(r=>(r.PIXEL="px",r.INCH="in",r.CENTIMETER="cm",r.MILLIMETER="mm",r))(hi||{}),Cn=(p=>(p.LETTER="Letter",p.LEGAL="Legal",p.TABLOID="Tabloid",p.LEDGER="Ledger",p.A0="A0",p.A1="A1",p.A2="A2",p.A3="A3",p.A4="A4",p.A5="A5",p.A6="A6",p))(Cn||{}),ar="page",bt="1cm",Fa="A4",Ha={top:bt,right:bt,bottom:bt,left:bt},vn=class{static{i(this,"SaveAsPdf")}name(){return"content_save-as-pdf"}description(){return"Saves the current page as a PDF file."}inputSchema(){return{outputPath:tt.string().describe("Directory path where PDF will be saved. By default OS tmp directory is used.").optional().default(La.tmpdir()),name:tt.string().describe(`Name of the save/export. Default value is "${ar}". Note that final saved/exported file name is in the "{name}-{time}.pdf" format in which "{time}" is in the "YYYYMMDD-HHmmss" format.`).optional().default(ar),format:tt.enum(ve(Cn)).transform(dt(Cn)).describe(`Page format. Valid values are: ${ve(Cn)}.`).optional().default(Fa),printBackground:tt.boolean().describe('Whether to print background graphics (default: "false").').optional().default(!1),margin:tt.object({top:tt.string().describe("Top margin.").default(bt),right:tt.string().describe("Right margin.").default(bt),bottom:tt.string().describe("Bottom margin.").default(bt),left:tt.string().describe("Left margin.").default(bt)}).describe(`Page margins. Numeric margin values labeled with units ("100px", "10cm", etc ...). Unlabeled values are treated as pixels. Valid units are: ${ve(hi)}.`).optional()}}outputSchema(){return{filePath:tt.string().describe("Full path of the saved PDF file.")}}async handle(e,t){let n=`${t.name||ar}-${hn()}.pdf`,r=Ba.resolve(t.outputPath,n),s={path:r,format:t.format,printBackground:t.printBackground,margin:t.margin||Ha};return await e.page.pdf(s),{filePath:r}}};import Wa from"os";import Ua from"path";import En from"jpeg-js";import{PNG as qa}from"pngjs";import{z as $e}from"zod";var On=(t=>(t.PNG="png",t.JPEG="jpeg",t))(On||{}),ur="screenshot",yi="png",Si=100,Pn=class{static{i(this,"TakeScreenshot")}name(){return"content_take-screenshot"}description(){return`Takes a screenshot of the current page or a specific element.
|
|
269
|
-
The screenshot is always saved to the file system and the file path is returned.
|
|
270
|
-
By default, the image data is NOT included in the response to reduce payload size.
|
|
271
|
-
If the AI assistant cannot access the MCP server's file system (e.g., remote MCP server,
|
|
272
|
-
containerized environment, or different machine), set "includeBase64" to true to receive
|
|
273
|
-
the image data directly in the response.`}inputSchema(){return{outputPath:$e.string().describe("Directory path where screenshot will be saved. By default OS tmp directory is used.").optional().default(Wa.tmpdir()),name:$e.string().describe(`Name of the screenshot. Default value is "${ur}". Note that final saved/exported file name is in the "{name}-{time}.{type}" format in which "{time}" is in the "YYYYMMDD-HHmmss" format.`).optional().default(ur),selector:$e.string().describe("CSS selector for element to take screenshot.").optional(),fullPage:$e.boolean().describe('Whether to take a screenshot of the full scrollable page, instead of the currently visible viewport (default: "false").').optional().default(!1),type:$e.enum(ve(On)).transform(dt(On)).describe(`Page format. Valid values are: ${ve(On)}`).optional().default(yi),quality:$e.number().int().min(0).max(Si).describe("The quality of the image, between 0-100. Not applicable to png images.").optional(),includeBase64:$e.boolean().describe("If true, includes the screenshot image data (base64-encoded) in the response. Default is false since the file path is usually sufficient when the AI assistant can access the MCP server file system. Set to true when the AI assistant cannot access the file system where the MCP server runs (e.g., remote server, container, or different machine) and needs to receive the image directly in the response.").optional().default(!1)}}outputSchema(){return{filePath:$e.string().describe("Full path of the saved screenshot file."),image:$e.object({data:$e.any().describe("Base64-encoded image data."),mimeType:$e.string().describe("MIME type of the image.")}).optional().describe('Image data included only when "includeBase64" input parameter is set to true.')}}_scaleImageToSize(e,t){let{data:n,width:r,height:s}=e,a=Math.max(1,Math.floor(t.width)),u=Math.max(1,Math.floor(t.height));if(r===a&&s===u)return e;if(r<=0||s<=0)throw new Error("Invalid input image");if(t.width<=0||t.height<=0||!isFinite(t.width)||!isFinite(t.height))throw new Error("Invalid output dimensions");let l=i((k,B,F)=>k<B?B:k>F?F:k,"clamp"),m=i((k,B)=>{let F=k*k,q=F*k;B[0]=-.5*k+1*F-.5*q,B[1]=1-2.5*F+1.5*q,B[2]=.5*k+2*F-1.5*q,B[3]=-.5*F+.5*q},"weights"),c=r*4,p=a*4,b=new Int32Array(a*4),h=new Float32Array(a*4),C=new Float32Array(4),M=r/a;for(let k=0;k<a;k++){let B=(k+.5)*M-.5,F=Math.floor(B),q=B-F;m(q,C);let z=k*4,Z=l(F-1,0,r-1),j=l(F+0,0,r-1),U=l(F+1,0,r-1),K=l(F+2,0,r-1);b[z+0]=Z<<2,b[z+1]=j<<2,b[z+2]=U<<2,b[z+3]=K<<2,h[z+0]=C[0],h[z+1]=C[1],h[z+2]=C[2],h[z+3]=C[3]}let y=new Int32Array(u*4),T=new Float32Array(u*4),W=new Float32Array(4),ie=s/u;for(let k=0;k<u;k++){let B=(k+.5)*ie-.5,F=Math.floor(B),q=B-F;m(q,W);let z=k*4,Z=l(F-1,0,s-1),j=l(F+0,0,s-1),U=l(F+1,0,s-1),K=l(F+2,0,s-1);y[z+0]=Z*c,y[z+1]=j*c,y[z+2]=U*c,y[z+3]=K*c,T[z+0]=W[0],T[z+1]=W[1],T[z+2]=W[2],T[z+3]=W[3]}let Y=new Uint8Array(a*u*4);for(let k=0;k<u;k++){let B=k*4,F=y[B+0],q=y[B+1],z=y[B+2],Z=y[B+3],j=T[B+0],U=T[B+1],K=T[B+2],ue=T[B+3],Te=k*p;for(let pe=0;pe<a;pe++){let J=pe*4,w=b[J+0],le=b[J+1],G=b[J+2],H=b[J+3],X=h[J+0],Oe=h[J+1],xe=h[J+2],qe=h[J+3],Ie=Te+(pe<<2);for(let ne=0;ne<4;ne++){let Pe=n[F+w+ne]*X+n[F+le+ne]*Oe+n[F+G+ne]*xe+n[F+H+ne]*qe,Ne=n[q+w+ne]*X+n[q+le+ne]*Oe+n[q+G+ne]*xe+n[q+H+ne]*qe,lt=n[z+w+ne]*X+n[z+le+ne]*Oe+n[z+G+ne]*xe+n[z+H+ne]*qe,Ye=n[Z+w+ne]*X+n[Z+le+ne]*Oe+n[Z+G+ne]*xe+n[Z+H+ne]*qe,Je=Pe*j+Ne*U+lt*K+Ye*ue;Y[Ie+ne]=Je<0?0:Je>255?255:Je|0}}}return{data:Buffer.from(Y.buffer),width:a,height:u}}_scaleImageToFitMessage(e,t){let r=12058624e-1,s=1568,a=t==="png"?qa.sync.read(e):En.decode(e,{maxMemoryUsageInMB:512}),u=a.width*a.height,l=Math.min(s/a.width,s/a.height,Math.sqrt(r/u));l>1&&(l=1);let m=a.width*l|0,c=a.height*l|0,p=this._scaleImageToSize(a,{width:m,height:c}),b,h=t,C=t==="png"?75:70;t==="png"?(b=En.encode(p,C).data,h="jpeg"):b=En.encode(p,C).data;let M=0,y=5;for(;b.length>819200&&M<y;)C=Math.max(50,C-10),C<=50&&b.length>819200&&(l*=.85,m=Math.max(200,a.width*l|0),c=Math.max(200,a.height*l|0),p=this._scaleImageToSize(a,{width:m,height:c})),b=En.encode(p,C).data,M++;return b}async handle(e,t){let n=t.type||yi,r=`${t.name||ur}-${hn()}.${n}`,s=Ua.resolve(t.outputPath,r),a=n==="png"?void 0:t.quality??Si,u={path:s,type:n,fullPage:!!t.fullPage,quality:a};if(t.selector){let c=await e.page.$(t.selector);if(!c)throw new Error(`Element not found: ${t.selector}`);u.element=c}let l=await e.page.screenshot(u),m={filePath:s};return t.includeBase64&&(m.image={data:this._scaleImageToFitMessage(l,n),mimeType:`image/${n}`}),m}};var wi=[new xn,new In,new vn,new Pn];import{z as Re}from"zod";var Va={maxDepth:3,maxProperties:50,maxArrayLength:100,maxStringLength:1e3},Rn=class{constructor(e,t){this.page=e;this.options={...Va,...t}}static{i(this,"V8Api")}cdp=null;enabled=!1;scripts=new Map;scriptsByUrl=new Map;eventHandlers={};options;getOptions(){return{...this.options}}isEnabled(){return this.enabled}async getCdp(){if(!this.cdp){let e=this.page;this.cdp=await e.context().newCDPSession(e)}return this.cdp}on(e,t){this.eventHandlers[e]=t}off(e){delete this.eventHandlers[e]}async enable(){if(this.enabled)return;let e=await this.getCdp();e.on("Debugger.scriptParsed",t=>{let n=t;if(this.scripts.set(n.scriptId,n),n.url){let r=this.scriptsByUrl.get(n.url)||[];r.push(n),this.scriptsByUrl.set(n.url,r)}this.eventHandlers.scriptParsed&&this.eventHandlers.scriptParsed(n)}),e.on("Debugger.paused",t=>{let n=t;this.eventHandlers.paused&&this.eventHandlers.paused(n)}),e.on("Debugger.resumed",()=>{this.eventHandlers.resumed&&this.eventHandlers.resumed()}),await e.send("Debugger.enable",{maxScriptsCacheSize:1e8}),await e.send("Runtime.enable"),this.enabled=!0}async disable(){if(!(!this.enabled||!this.cdp)){try{await this.cdp.send("Debugger.disable"),await this.cdp.send("Runtime.disable")}catch{}this.scripts.clear(),this.scriptsByUrl.clear(),this.enabled=!1}}async detach(){if(await this.disable(),this.cdp){try{await this.cdp.detach()}catch{}this.cdp=null}}getScripts(){return Array.from(this.scripts.values())}getScript(e){return this.scripts.get(e)}findScriptsByUrl(e){let t=new RegExp(e),n=[];for(let r of this.scripts.values())r.url&&t.test(r.url)&&n.push(r);return n}async setBreakpointByUrl(e){let t=await this.getCdp(),n={lineNumber:e.lineNumber};e.urlRegex?n.urlRegex=e.urlRegex:e.url&&(n.url=e.url),e.columnNumber!==void 0&&(n.columnNumber=e.columnNumber),e.condition&&(n.condition=e.condition);let r=await t.send("Debugger.setBreakpointByUrl",n);return{breakpointId:r.breakpointId,locations:r.locations||[]}}async setBreakpoint(e,t){let n=await this.getCdp(),r={location:{scriptId:e.scriptId,lineNumber:e.lineNumber,columnNumber:e.columnNumber}};t&&(r.condition=t);let s=await n.send("Debugger.setBreakpoint",r);return{breakpointId:s.breakpointId,actualLocation:s.actualLocation}}async removeBreakpoint(e){await(await this.getCdp()).send("Debugger.removeBreakpoint",{breakpointId:e})}async resume(){await(await this.getCdp()).send("Debugger.resume")}async stepOver(){await(await this.getCdp()).send("Debugger.stepOver")}async stepInto(){await(await this.getCdp()).send("Debugger.stepInto")}async stepOut(){await(await this.getCdp()).send("Debugger.stepOut")}async pause(){await(await this.getCdp()).send("Debugger.pause")}async setPauseOnExceptions(e){await(await this.getCdp()).send("Debugger.setPauseOnExceptions",{state:e})}async evaluateOnCallFrame(e,t,n){let s=await(await this.getCdp()).send("Debugger.evaluateOnCallFrame",{callFrameId:e,expression:t,returnByValue:n?.returnByValue??!0,generatePreview:n?.generatePreview??!0,throwOnSideEffect:n?.throwOnSideEffect??!1});return{result:s.result,exceptionDetails:s.exceptionDetails}}async getProperties(e,t){let r=await(await this.getCdp()).send("Runtime.getProperties",{objectId:e,ownProperties:t?.ownProperties??!0,accessorPropertiesOnly:t?.accessorPropertiesOnly??!1,generatePreview:t?.generatePreview??!0});return{result:r.result||[],internalProperties:r.internalProperties,privateProperties:r.privateProperties}}async getScopeVariables(e,t){if(!e.object.objectId)return{};let n={maxDepth:t?.maxDepth??this.options.maxDepth,maxProperties:t?.maxProperties??this.options.maxProperties,maxArrayLength:t?.maxArrayLength??this.options.maxArrayLength},{result:r}=await this.getProperties(e.object.objectId,{ownProperties:!0,generatePreview:!0}),s={},a=new Set,u=0;for(let l of r){if(u>=n.maxProperties){s.__truncated__=`${r.length-u} more properties`;break}l.value&&(s[l.name]=await this.extractValueDeep(l.value,n.maxDepth,n.maxProperties,n.maxArrayLength,a),u++)}return s}extractValue(e){return this._extractValueShallow(e)}async extractValueDeep(e,t,n,r,s=new Set){let a=t??this.options.maxDepth,u=n??this.options.maxProperties,l=r??this.options.maxArrayLength;if(e.value!==void 0)return typeof e.value=="string"&&e.value.length>this.options.maxStringLength?e.value.slice(0,this.options.maxStringLength)+`... (${e.value.length} chars)`:e.value;if(e.unserializableValue)return e.unserializableValue==="NaN"?NaN:e.unserializableValue==="Infinity"?1/0:e.unserializableValue==="-Infinity"?-1/0:e.unserializableValue==="-0"?-0:e.unserializableValue.endsWith("n")?`BigInt(${e.unserializableValue.slice(0,-1)})`:e.unserializableValue;if(e.subtype==="null")return null;if(e.type!=="undefined"){if(e.type==="function")return`[function${e.description?`: ${e.description.split(`
|
|
274
|
-
`)[0]}`:""}]`;if(!e.objectId)return this._extractValueShallow(e);if(a<=0)return this._getShallowDescription(e);if(s.has(e.objectId))return"[Circular]";s.add(e.objectId);try{return e.subtype==="array"?await this._extractArray(e.objectId,a-1,u,l,s):e.subtype==="map"?await this._extractMap(e.objectId,a-1,u,l,s):e.subtype==="set"?await this._extractSet(e.objectId,a-1,l,s):e.subtype==="date"?e.description||"[Date]":e.subtype==="regexp"?e.description||"[RegExp]":e.subtype==="error"?e.description||"[Error]":e.subtype==="promise"?`[Promise: ${e.description||"pending"}]`:e.subtype==="node"||e.className?.includes("Element")||e.className?.includes("Node")?`[DOM: ${e.description||e.className||"Node"}]`:e.type==="object"?await this._extractObject(e.objectId,a-1,u,l,s,e.className):this._extractValueShallow(e)}catch{return this._getShallowDescription(e)}}}async _extractArray(e,t,n,r,s){let{result:a}=await this.getProperties(e,{ownProperties:!0,generatePreview:!0}),u=[],l=0;for(let m of a)if(m.name==="length"&&m.value?.value!==void 0){l=m.value.value;break}for(let m of a){let c=parseInt(m.name,10);if(!(isNaN(c)||c<0)){if(u.length>=r)break;m.value&&(u[c]=await this.extractValueDeep(m.value,t,n,r,s))}}return l>r&&(u.__truncated__=`${l-r} more items`),u}async _extractMap(e,t,n,r,s){let{internalProperties:a}=await this.getProperties(e,{ownProperties:!0,generatePreview:!0}),u={__type__:"Map"},l=[],m=a?.find(c=>c.name==="[[Entries]]");if(m?.value?.objectId){let{result:c}=await this.getProperties(m.value.objectId,{ownProperties:!0,generatePreview:!0}),p=0;for(let b of c){if(p>=r)break;let h=parseInt(b.name,10);if(isNaN(h)||!b.value?.objectId)continue;let{result:C}=await this.getProperties(b.value.objectId,{ownProperties:!0,generatePreview:!0}),M=C.find(T=>T.name==="key"),y=C.find(T=>T.name==="value");if(M?.value&&y?.value){let T=await this.extractValueDeep(M.value,t,n,r,s),W=await this.extractValueDeep(y.value,t,n,r,s);l.push([T,W])}p++}}return u.entries=l,u}async _extractSet(e,t,n,r){let{internalProperties:s}=await this.getProperties(e,{ownProperties:!0,generatePreview:!0}),a={__type__:"Set"},u=[],l=s?.find(m=>m.name==="[[Entries]]");if(l?.value?.objectId){let{result:m}=await this.getProperties(l.value.objectId,{ownProperties:!0,generatePreview:!0}),c=0;for(let p of m){if(c>=n)break;let b=parseInt(p.name,10);isNaN(b)||(p.value&&u.push(await this.extractValueDeep(p.value,t,50,n,r)),c++)}}return a.values=u,a}async _extractObject(e,t,n,r,s,a){let{result:u}=await this.getProperties(e,{ownProperties:!0,generatePreview:!0}),l={};a&&a!=="Object"&&(l.__class__=a);let m=0;for(let c of u){if(m>=n){l.__truncated__=`${u.length-m} more properties`;break}c.isOwn&&(c.name.startsWith("__")&&c.name.endsWith("__")||c.value&&(l[c.name]=await this.extractValueDeep(c.value,t,n,r,s),m++))}return l}_extractValueShallow(e){if(e.value!==void 0)return e.value;if(e.unserializableValue)return e.unserializableValue==="NaN"?NaN:e.unserializableValue==="Infinity"?1/0:e.unserializableValue==="-Infinity"?-1/0:e.unserializableValue==="-0"?-0:e.unserializableValue.endsWith("n")?`BigInt(${e.unserializableValue.slice(0,-1)})`:e.unserializableValue;if(e.subtype==="null")return null;if(e.type!=="undefined")return this._getShallowDescription(e)}_getShallowDescription(e){if(e.description){let t=e.description.split(`
|
|
275
|
-
`)[0];return t.length>100?`[${e.type}${e.subtype?`:${e.subtype}`:""}] ${t.slice(0,100)}...`:`[${e.type}${e.subtype?`:${e.subtype}`:""}] ${t}`}return`[${e.type}${e.subtype?`:${e.subtype}`:""}]`}async getScriptSource(e){return await(await this.getCdp()).send("Debugger.getScriptSource",{scriptId:e})}async setScriptSource(e,t,n){let s=await(await this.getCdp()).send("Debugger.setScriptSource",{scriptId:e,scriptSource:t,dryRun:n??!1});return{callFrames:s.callFrames,stackChanged:s.stackChanged,asyncStackTrace:s.asyncStackTrace,exceptionDetails:s.exceptionDetails}}};var Ti="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",xi=new Map;for(let o=0;o<Ti.length;o++)xi.set(Ti[o],o);function za(o){let e=[],t=0,n=0;for(let r of o){let s=xi.get(r);if(s===void 0)throw new Error(`Invalid base64 character: ${r}`);let a=(s&32)!==0;if(n+=(s&31)<<t,a)t+=5;else{let u=(n&1)!==0;n=n>>1,u&&(n=-n),e.push(n),n=0,t=0}}return e}i(za,"decodeVLQ");function $a(o,e){let t=[],n=o.split(";"),r=0,s=0,a=0,u=0,l=0;for(let m of n){let c=0,p=m.split(",");for(let b of p){if(!b)continue;let h=za(b);if(h.length===0)continue;c+=h[0];let C={generatedLine:r,generatedColumn:c};h.length>=4&&(s+=h[1],a+=h[2],u+=h[3],C.source=e[s],C.originalLine=a,C.originalColumn=u,h.length>=5&&(l+=h[4])),t.push(C)}r++}return t}i($a,"parseMappings");var Nn=class{constructor(e){this.page=e}static{i(this,"SourceMapResolver")}scripts=new Map;scriptsByUrl=new Map;sourceToScripts=new Map;fetchCache=new Map;registerScript(e){let t={scriptId:e.scriptId,url:e.url,sourceMapURL:e.sourceMapURL,originalSources:[]};if(this.scripts.set(e.scriptId,t),e.url){let n=this.scriptsByUrl.get(e.url)||[];n.push(t),this.scriptsByUrl.set(e.url,n)}}async loadSourceMap(e){let t=this.scripts.get(e);if(!t||!t.sourceMapURL)return!1;if(t.sourceMap)return!0;try{let n=await this._fetchSourceMap(t.sourceMapURL,t.url);if(!n)return!1;t.sourceMap=n,t.mappings=$a(n.mappings,n.sources),t.originalSources=n.sources.map(r=>this._resolveSourcePath(r,n.sourceRoot,t.url));for(let r of t.originalSources){let s=this.sourceToScripts.get(r)||[];s.push(t),this.sourceToScripts.set(r,s)}return!0}catch(n){return be(`Failed to load source map for ${t.url}:`,n),!1}}async loadAllSourceMaps(){let e=0;for(let t of this.scripts.keys())await this.loadSourceMap(t)&&e++;return e}originalToGenerated(e,t,n=0){let r=this._normalizeSourcePath(e),s=this._findScriptsForSource(r);if(s.length!==0)for(let a of s){if(!a.mappings)continue;let u=a.originalSources.findIndex(c=>this._normalizeSourcePath(c)===r||c.endsWith(r)||r.endsWith(c));if(u===-1)continue;let l,m=1/0;for(let c of a.mappings)if(c.source===a.sourceMap?.sources[u]&&c.originalLine!==void 0&&c.originalLine===t){let p=c.originalColumn!==void 0?Math.abs(c.originalColumn-n):0;p<m&&(m=p,l=c)}if(l)return{scriptId:a.scriptId,location:{line:l.generatedLine,column:l.generatedColumn}}}}generatedToOriginal(e,t,n=0){let r=this.scripts.get(e);if(!r?.mappings)return;let s,a=1/0;for(let u of r.mappings){if(u.generatedLine!==t||u.source===void 0)continue;let l=Math.abs(u.generatedColumn-n);l<a&&(a=l,s=u)}if(s&&s.source!==void 0&&s.originalLine!==void 0){let u=r.sourceMap?.sources.indexOf(s.source);return{source:u!==void 0&&u>=0?r.originalSources[u]:s.source,line:s.originalLine,column:s.originalColumn??0,name:s.name}}}getOriginalSources(){return Array.from(this.sourceToScripts.keys())}findScriptsForSource(e){let t=this._normalizeSourcePath(e);return this._findScriptsForSource(t)}hasSourceMaps(){for(let e of this.scripts.values())if(e.sourceMap)return!0;return!1}clear(){this.scripts.clear(),this.scriptsByUrl.clear(),this.sourceToScripts.clear(),this.fetchCache.clear()}_findScriptsForSource(e){let t=this.sourceToScripts.get(e);if(t&&t.length>0)return t;let n=[];for(let[r,s]of this.sourceToScripts.entries()){let a=this._normalizeSourcePath(r);(a.endsWith(e)||e.endsWith(a))&&n.push(...s)}return n}_normalizeSourcePath(e){let t=e.replace(/^webpack:\/\/[^/]*\//,"").replace(/^file:\/\//,"").replace(/^\.*\//,"");for(t=t.replace(/\\/g,"/");t.startsWith("./");)t=t.slice(2);return t}_resolveSourcePath(e,t,n){return e.startsWith("webpack://")||e.startsWith("http://")||e.startsWith("https://")||t&&(e=t.replace(/\/$/,"")+"/"+e),e}async _fetchSourceMap(e,t){if(e.startsWith("data:"))return this._parseDataUrl(e);let n=this._resolveUrl(e,t);if(this.fetchCache.has(n))return this.fetchCache.get(n)||null;try{let r=await this._fetchViaBrowser(n);return this.fetchCache.set(n,r),r}catch(r){return be(`Failed to fetch source map from ${n}:`,r),this.fetchCache.set(n,null),null}}_parseDataUrl(e){try{let t=e.match(/^data:[^,]*(?:;base64)?,(.*)$/);if(!t)return null;let n=e.includes(";base64,"),r=t[1],s=n?Buffer.from(r,"base64").toString("utf-8"):decodeURIComponent(r);return JSON.parse(s)}catch{return null}}_resolveUrl(e,t){if(e.startsWith("http://")||e.startsWith("https://"))return e;try{return new URL(e,t).href}catch{return t.replace(/[^/]*$/,"")+e}}async _fetchViaBrowser(e){try{let t=await this.page.evaluate(async n=>{try{let r=await fetch(n);return r.ok?{data:await r.text()}:{error:`HTTP ${r.status}`}}catch(r){return{error:r.message||"Fetch failed"}}},e);return"error"in t?(be(`Browser fetch error for ${e}: ${t.error}`),null):JSON.parse(t.data)}catch(t){return be(`Failed to fetch ${e} via browser:`,t),null}}};var lr={maxSnapshots:1e3,maxCallStackDepth:20,maxFramesWithScopes:5,maxAsyncStackSegments:10,maxFramesPerAsyncSegment:10,maxDOMMutations:100,maxDOMHtmlSnippetLength:200,maxPendingRequests:1e3,maxResponseBodyLength:1e4,networkCleanupTimeoutMs:5e3},cr=new WeakMap;function Pt(){let o=Date.now(),e=Math.floor(Math.random()*1e6);return`${o.toString(36)}-${e.toString(36)}`}i(Pt,"_generateId");function Ga(o,e){try{let t=o.trim();return/^[=<>!%]/.test(t)&&(t=`hitCount ${t}`),!!new Function("hitCount",`return (${t});`)(e)}catch{return!1}}i(Ga,"_evaluateHitCondition");async function ja(o,e,t){let n={};for(let r of t.values())try{let s=await o.evaluateOnCallFrame(e,r.expression);if(s.exceptionDetails)n[r.expression]=`[Error: ${s.exceptionDetails.text||"Evaluation failed"}]`;else{let a=await o.extractValueDeep(s.result,2);n[r.expression]=a}}catch(s){n[r.expression]=`[Error: ${s.message||"Unknown error"}]`}return n}i(ja,"_evaluateWatchExpressionsOnFrame");function Ka(o,e,t,n){let r=cr.get(o);if(r)return r;let s=new Rn(e,t),a=new Nn(e),u={...lr,...n},l={v8Api:s,sourceMapResolver:a,probes:new Map,watchExpressions:new Map,domBreakpoints:new Map,networkBreakpoints:new Map,snapshots:[],snapshotSequence:0,config:u,enabled:!1,sourceMapsLoaded:!1,exceptionBreakpoint:"none",networkInterceptionEnabled:!1,recentDOMMutations:[]};return cr.set(o,l),l}i(Ka,"_ensureStore");function fe(o){return cr.get(o)}i(fe,"_getStore");async function Xa(o,e,t=3){let n=[];for(let r of e.scopeChain)if(!(r.type==="global"||r.type==="script"||r.type==="with"||r.type==="eval"||r.type==="wasm-expression-stack")){if(n.length>=t)break;try{let s=await o.getScopeVariables(r),a=[];for(let[u,l]of Object.entries(s))a.push({name:u,value:l,type:typeof l});n.push({type:r.type,name:r.name,variables:a})}catch{}}return n}i(Xa,"_captureScopes");async function Ya(o,e,t,n){let r=[];t&&(r=await Xa(o,e));let s;if(n){let a=n.generatedToOriginal(e.location.scriptId,e.location.lineNumber,e.location.columnNumber??0);a&&(s={source:a.source,line:a.line+1,column:a.column!==void 0?a.column+1:void 0,name:a.name})}return{functionName:e.functionName||"(anonymous)",url:e.url||"",lineNumber:e.location.lineNumber+1,columnNumber:e.location.columnNumber!==void 0?e.location.columnNumber+1:void 0,scriptId:e.location.scriptId,scopes:r,originalLocation:s}}i(Ya,"_callFrameToSnapshot");function Ja(o,e,t,n){if(!o)return;let r=t??lr.maxAsyncStackSegments,s=n??lr.maxFramesPerAsyncSegment,a=[],u=o,l=0;for(;u&&l<r;){let m=[];for(let c of u.callFrames.slice(0,s)){let p;if(e){let b=e.generatedToOriginal(c.location.scriptId,c.location.lineNumber,c.location.columnNumber??0);b&&(p={source:b.source,line:b.line+1,column:b.column!==void 0?b.column+1:void 0,name:b.name})}m.push({functionName:c.functionName||"(anonymous)",url:c.url||"",lineNumber:c.location.lineNumber+1,columnNumber:c.location.columnNumber!==void 0?c.location.columnNumber+1:void 0,originalLocation:p})}m.length>0&&a.push({description:u.description,callFrames:m}),u=u.parent,l++}if(a.length!==0)return{segments:a}}i(Ja,"_convertAsyncStackTrace");async function He(o,e,t){let n=Ka(o,e,t?.v8Options,t?.config);if(!n.enabled){await n.v8Api.enable(),n.v8Api.on("scriptParsed",r=>{n.sourceMapResolver.registerScript(r)});for(let r of n.v8Api.getScripts())n.sourceMapResolver.registerScript(r);n.v8Api.on("paused",async r=>{let s=Date.now();try{let a=r.reason==="exception"||r.reason==="promiseRejection",u=r.reason==="DOM",l=r.hitBreakpoints||[],m,c=!0,p,b;if(u&&r.data){let y=r.data;for(let T of n.domBreakpoints.values())if(T.enabled&&(T.nodeId===y.nodeId||!y.nodeId)){p=T,b={type:T.type,selector:T.selector,targetNode:y.targetNode?`<${y.targetNode.nodeName?.toLowerCase()||"unknown"}>`:void 0,attributeName:y.attributeName||T.attributeName};break}}if(p&&b)try{let y=await e.evaluate(T=>{let W=window;if(!W.__domBreakpointMutations)return null;let ie=W.__domBreakpointMutations;for(let Y=ie.length-1;Y>=0;Y--)if(ie[Y].breakpointId===T)return ie[Y];return null},p.id);y&&(b.oldValue=y.oldValue,b.newValue=y.newValue,b.targetNode=y.targetOuterHTML,y.attributeName&&(b.attributeName=y.attributeName))}catch{}for(let y of n.probes.values()){if(!y.enabled)continue;if(y.v8BreakpointIds.some(W=>l.includes(W))){m=y,y.hitCondition&&(c=Ga(y.hitCondition,y.hitCount+1));break}}let h=m!==void 0&&c,C=a&&n.exceptionBreakpoint!=="none",M=p!==void 0;if(m&&(m.hitCount++,m.lastHitAt=Date.now()),p&&(p.hitCount++,p.lastHitAt=Date.now()),(h||C||M)&&r.callFrames.length>0){let y=r.callFrames[0],T;if(m&&m.kind==="logpoint"&&m.logExpression)try{let j=await n.v8Api.evaluateOnCallFrame(y.callFrameId,m.logExpression,{returnByValue:!0,generatePreview:!0});T=n.v8Api.extractValue(j.result)}catch{T="[evaluation error]"}let W;if(a&&r.data){let j=r.data;W={type:r.reason==="promiseRejection"?"promiseRejection":"exception",message:j.description||j.value||String(j),name:j.className,stack:j.description}}let ie,Y=n.sourceMapResolver.generatedToOriginal(y.location.scriptId,y.location.lineNumber,y.location.columnNumber??0);Y&&(ie={source:Y.source,line:Y.line+1,column:Y.column!==void 0?Y.column+1:void 0,name:Y.name});let k=m?.id??p?.id??"__exception__",B=m?.kind==="logpoint",F=[];if(!B){let j=r.callFrames.slice(0,n.config.maxCallStackDepth);for(let U=0;U<j.length;U++){let K=j[U],ue=U<n.config.maxFramesWithScopes,Te=await Ya(n.v8Api,K,ue,n.sourceMapResolver);F.push(Te)}}let q;B||(q=Ja(r.asyncStackTrace,n.sourceMapResolver,n.config.maxAsyncStackSegments,n.config.maxFramesPerAsyncSegment));let z;!B&&n.watchExpressions.size>0&&(z=await ja(n.v8Api,y.callFrameId,n.watchExpressions));let Z={id:Pt(),probeId:k,timestamp:Date.now(),sequenceNumber:++n.snapshotSequence,url:y.url||"",lineNumber:y.location.lineNumber+1,columnNumber:y.location.columnNumber!==void 0?y.location.columnNumber+1:void 0,originalLocation:ie,exception:W,domChange:b,callStack:F,asyncStackTrace:q,logResult:T,watchResults:z,captureTimeMs:Date.now()-s};n.snapshots.push(Z),n.snapshots.length>n.config.maxSnapshots&&n.snapshots.splice(0,n.snapshots.length-n.config.maxSnapshots)}}finally{await n.v8Api.resume()}}),n.enabled=!0,n.sourceMapResolver.loadAllSourceMaps().then(()=>{n.sourceMapsLoaded=!0}).catch(()=>{})}}i(He,"enableDebugging");function R(o){return fe(o)?.enabled??!1}i(R,"isDebuggingEnabled");async function Ci(o,e){let t=fe(o);if(!t||!t.enabled)throw new Error("Debugging is not enabled");await t.v8Api.setPauseOnExceptions(e),t.exceptionBreakpoint=e}i(Ci,"setExceptionBreakpoint");function Jt(o){return fe(o)?.exceptionBreakpoint??"none"}i(Jt,"getExceptionBreakpoint");function vi(o){return fe(o)?.sourceMapResolver.hasSourceMaps()??!1}i(vi,"hasSourceMaps");async function _n(o,e){let t=fe(o);if(!t||!t.enabled)throw new Error("Debugging is not enabled");let n=Pt(),r;e.condition?r=`(${e.condition})`:r="true";let s=e.lineNumber-1,a=(e.columnNumber??1)-1,u=t.sourceMapResolver.originalToGenerated(e.urlPattern,s,a),l,m=0;if(u)l=(await t.v8Api.setBreakpoint({scriptId:u.scriptId,lineNumber:u.location.line,columnNumber:u.location.column},r)).breakpointId,m=1;else{let b=e.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*").replace(/\\\?/g,"."),h=await t.v8Api.setBreakpointByUrl({urlRegex:b,lineNumber:e.lineNumber-1,columnNumber:e.columnNumber?e.columnNumber-1:void 0,condition:r});l=h.breakpointId,m=h.locations.length}let c={id:n,kind:e.kind,enabled:!0,urlPattern:e.urlPattern,lineNumber:e.lineNumber,columnNumber:e.columnNumber,condition:e.condition,logExpression:e.logExpression,hitCondition:e.hitCondition,v8BreakpointIds:[l],resolvedLocations:m,hitCount:0,createdAt:Date.now()};return t.probes.set(n,c),c}i(_n,"createProbe");async function ft(o,e){let t=fe(o);if(!t)return!1;let n=t.probes.get(e);if(!n)return!1;for(let r of n.v8BreakpointIds)try{await t.v8Api.removeBreakpoint(r)}catch{}return t.probes.delete(e),!0}i(ft,"removeProbe");function we(o){let e=fe(o);return e?Array.from(e.probes.values()):[]}i(we,"listProbes");function Mn(o,e){let t=fe(o);if(t)return t.probes.get(e)}i(Mn,"getProbe");function gt(o){let e=fe(o);return e?[...e.snapshots]:[]}i(gt,"getSnapshots");function nt(o,e){let t=fe(o);return t?t.snapshots.filter(n=>n.probeId===e):[]}i(nt,"getSnapshotsByProbe");function ke(o,e){let t=fe(o);if(!t)return 0;let n=t.snapshots.length;return t.snapshots=t.snapshots.filter(r=>r.probeId!==e),n-t.snapshots.length}i(ke,"clearSnapshotsByProbe");function Ei(o){let e=fe(o);if(!e||e.snapshots.length===0)return{totalSnapshots:0,snapshotsByProbe:{},averageCaptureTimeMs:0};let t={},n=0;for(let r of e.snapshots)t[r.probeId]=(t[r.probeId]||0)+1,n+=r.captureTimeMs;return{totalSnapshots:e.snapshots.length,snapshotsByProbe:t,oldestTimestamp:e.snapshots[0].timestamp,newestTimestamp:e.snapshots[e.snapshots.length-1].timestamp,averageCaptureTimeMs:n/e.snapshots.length}}i(Ei,"getSnapshotStats");function Oi(o,e){let t=fe(o);if(!t)throw new Error("Debug store not initialized");let n=Pt(),r={id:n,expression:e,createdAt:Date.now()};return t.watchExpressions.set(n,r),r}i(Oi,"addWatchExpression");function Pi(o,e){let t=fe(o);return t?t.watchExpressions.delete(e):!1}i(Pi,"removeWatchExpression");function kn(o){let e=fe(o);return e?Array.from(e.watchExpressions.values()):[]}i(kn,"listWatchExpressions");function Ri(o){let e=fe(o);if(!e)return 0;let t=e.watchExpressions.size;return e.watchExpressions.clear(),t}i(Ri,"clearWatchExpressions");async function Ni(o,e,t){let n=fe(o);if(!n||!n.enabled)throw new Error("Debugging is not enabled");let r=await n.v8Api.getCdp();await r.send("DOM.enable");let{root:s}=await r.send("DOM.getDocument",{depth:0}),{nodeId:a}=await r.send("DOM.querySelector",{nodeId:s.nodeId,selector:t.selector});if(!a||a===0)throw new Error(`Element not found: ${t.selector}`);let u=t.type==="subtree-modified"?"subtree-modified":t.type==="attribute-modified"?"attribute-modified":"node-removed";await r.send("DOMDebugger.setDOMBreakpoint",{nodeId:a,type:u});let l=Pt(),m={id:l,selector:t.selector,type:t.type,attributeName:t.attributeName,enabled:!0,nodeId:a,hitCount:0,createdAt:Date.now()};return n.domBreakpoints.set(l,m),await e.evaluate(({selector:c,breakpointId:p,type:b,attrName:h,maxMutations:C,maxHtmlSnippetLength:M})=>{let y=document.querySelector(c);if(!y)return;let T=window;T.__domBreakpointData=T.__domBreakpointData||{},T.__domBreakpointMutations=T.__domBreakpointMutations||[],T.__domBreakpointData[p]={selector:c,type:b,attrName:h,currentAttrs:{}};for(let Y of y.attributes)T.__domBreakpointData[p].currentAttrs[Y.name]=Y.value;let W=new MutationObserver(Y=>{for(let k of Y){let B=k.target;if(k.type==="attributes"){let F=k.attributeName||"";if(h&&h!==F)continue;let q={breakpointId:p,selector:c,type:"attribute-modified",attributeName:F,oldValue:k.oldValue,newValue:B.getAttribute(F),targetOuterHTML:B.outerHTML.substring(0,M),timestamp:Date.now()};T.__domBreakpointMutations.push(q),T.__domBreakpointMutations.length>C&&T.__domBreakpointMutations.shift(),T.__domBreakpointData[p].currentAttrs[F]=B.getAttribute(F)}else if(k.type==="childList"){let F={breakpointId:p,selector:c,type:"subtree-modified",addedNodes:k.addedNodes.length,removedNodes:k.removedNodes.length,targetOuterHTML:B.outerHTML.substring(0,M),timestamp:Date.now()};T.__domBreakpointMutations.push(F),T.__domBreakpointMutations.length>C&&T.__domBreakpointMutations.shift()}}}),ie={attributes:b==="attribute-modified",attributeOldValue:b==="attribute-modified",childList:b==="subtree-modified"||b==="node-removed",subtree:b==="subtree-modified"};h&&(ie.attributeFilter=[h]),W.observe(y,ie),T.__domBreakpointObservers=T.__domBreakpointObservers||{},T.__domBreakpointObservers[p]=W},{selector:t.selector,breakpointId:l,type:t.type,attrName:t.attributeName,maxMutations:n.config.maxDOMMutations,maxHtmlSnippetLength:n.config.maxDOMHtmlSnippetLength}),m}i(Ni,"setDOMBreakpoint");async function mr(o,e,t){let n=fe(o);if(!n)return!1;let r=n.domBreakpoints.get(e);if(!r||!r.nodeId)return!1;try{let s=await n.v8Api.getCdp(),a=r.type==="subtree-modified"?"subtree-modified":r.type==="attribute-modified"?"attribute-modified":"node-removed";await s.send("DOMDebugger.removeDOMBreakpoint",{nodeId:r.nodeId,type:a})}catch{}if(t)try{await t.evaluate(s=>{let a=window;a.__domBreakpointObservers&&a.__domBreakpointObservers[s]&&(a.__domBreakpointObservers[s].disconnect(),delete a.__domBreakpointObservers[s]),a.__domBreakpointData&&delete a.__domBreakpointData[s],a.__domBreakpointMutations&&(a.__domBreakpointMutations=a.__domBreakpointMutations.filter(u=>u.breakpointId!==s))},e)}catch{}return n.domBreakpoints.delete(e)}i(mr,"removeDOMBreakpoint");function ht(o){let e=fe(o);return e?Array.from(e.domBreakpoints.values()):[]}i(ht,"listDOMBreakpoints");async function _i(o,e){let t=fe(o);if(!t)return 0;let n=Array.from(t.domBreakpoints.keys());for(let r of n)await mr(o,r,e);return n.length}i(_i,"clearDOMBreakpoints");async function Qa(o,e){if(o.networkInterceptionEnabled)return;let t=await o.v8Api.getCdp();await t.send("Fetch.enable",{patterns:[{urlPattern:"*",requestStage:"Request"}]}),t.on("Fetch.requestPaused",async r=>{let s=r.requestId,a=r.request.url,u=r.request.method;try{let l;for(let m of o.networkBreakpoints.values()){if(!m.enabled)continue;let c=m.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1");if(new RegExp(c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*")).test(a)&&!(m.method&&m.method.toUpperCase()!==u.toUpperCase())&&m.timing==="request"){l=m;break}}if(l&&l.timing==="request"){let m={url:a,method:u,requestHeaders:r.request.headers,requestBody:r.request.postData,resourceType:r.resourceType,timing:"request"},c={id:Pt(),probeId:l.id,timestamp:Date.now(),sequenceNumber:++o.snapshotSequence,url:a,lineNumber:0,networkRequest:m,callStack:[],captureTimeMs:0};o.watchExpressions.size>0&&(c.watchResults=await Ii(o,e)),o.snapshots.push(c),o.snapshots.length>o.config.maxSnapshots&&o.snapshots.splice(0,o.snapshots.length-o.config.maxSnapshots),l.hitCount++,l.lastHitAt=Date.now()}await t.send("Fetch.continueRequest",{requestId:s})}catch{try{await t.send("Fetch.continueRequest",{requestId:s})}catch{}}}),await t.send("Network.enable");let n=new Map;t.on("Network.requestWillBeSent",r=>{if(n.set(r.requestId,{method:r.request.method,postData:r.request.postData}),n.size>o.config.maxPendingRequests){let s=n.keys().next().value;s&&n.delete(s)}}),t.on("Network.responseReceived",async r=>{let s=r.requestId,a=r.response.url,u=n.get(s),l=u?.method||r.type||"GET",m=r.response.status;for(let c of o.networkBreakpoints.values()){if(!c.enabled||c.timing!=="response")continue;let p=c.urlPattern.replace(/\\([.*+?^${}()|[\]\\/-])/g,"$1");if(!new RegExp(p.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/\\\*/g,".*")).test(a)||c.method&&c.method.toUpperCase()!==l.toUpperCase()||c.onError&&m<400)continue;let h;try{let y=await t.send("Network.getResponseBody",{requestId:s});y.base64Encoded?h=Buffer.from(y.body,"base64").toString("utf-8"):h=y.body,h&&h.length>o.config.maxResponseBodyLength&&(h=h.substring(0,o.config.maxResponseBodyLength)+"... [truncated]")}catch{}let C={url:a,method:l,requestBody:u?.postData,status:m,statusText:r.response.statusText,responseHeaders:r.response.headers,responseBody:h,resourceType:r.type,timing:"response"},M={id:Pt(),probeId:c.id,timestamp:Date.now(),sequenceNumber:++o.snapshotSequence,url:a,lineNumber:0,networkRequest:C,callStack:[],captureTimeMs:0};o.watchExpressions.size>0&&(M.watchResults=await Ii(o,e)),o.snapshots.push(M),o.snapshots.length>o.config.maxSnapshots&&o.snapshots.splice(0,o.snapshots.length-o.config.maxSnapshots),c.hitCount++,c.lastHitAt=Date.now(),n.delete(s);break}}),t.on("Network.loadingFinished",r=>{setTimeout(()=>{n.delete(r.requestId)},o.config.networkCleanupTimeoutMs)}),o.networkInterceptionEnabled=!0}i(Qa,"_enableNetworkInterception");async function Ii(o,e){let t={};for(let n of o.watchExpressions.values())try{let r=await e.evaluate(s=>{try{return(0,eval)(s)}catch(a){return`[Error: ${a.message}]`}},n.expression);t[n.expression]=r}catch(r){t[n.expression]=`[Error: ${r.message}]`}return t}i(Ii,"_evaluateWatchExpressions");async function Mi(o,e,t){let n=fe(o);if(!n||!n.enabled)throw new Error("Debugging is not enabled");await Qa(n,e);let r=Pt(),s={id:r,urlPattern:t.urlPattern,method:t.method,timing:t.timing||"request",onError:t.onError,enabled:!0,hitCount:0,createdAt:Date.now()};return n.networkBreakpoints.set(r,s),s}i(Mi,"setNetworkBreakpoint");function ki(o,e){let t=fe(o);return t?t.networkBreakpoints.delete(e):!1}i(ki,"removeNetworkBreakpoint");function yt(o){let e=fe(o);return e?Array.from(e.networkBreakpoints.values()):[]}i(yt,"listNetworkBreakpoints");function Di(o){let e=fe(o);if(!e)return 0;let t=e.networkBreakpoints.size;return e.networkBreakpoints.clear(),t}i(Di,"clearNetworkBreakpoints");var Dn=class{static{i(this,"Status")}name(){return"debug_status"}description(){return`
|
|
276
|
-
Returns the current debugging status including:
|
|
277
|
-
- Whether debugging is enabled
|
|
278
|
-
- Source map status
|
|
279
|
-
- Exceptionpoint state
|
|
280
|
-
- Count of tracepoints, logpoints, watches, dompoints and netpoints
|
|
281
|
-
- Snapshot statistics
|
|
282
|
-
`}inputSchema(){return{}}outputSchema(){return{enabled:Re.boolean().describe("Whether debugging is enabled"),hasSourceMaps:Re.boolean().describe("Whether source maps are loaded"),exceptionBreakpoint:Re.string().describe("Exceptionpoint state (none, uncaught, all)"),tracepointCount:Re.number().describe("Number of tracepoints"),logpointCount:Re.number().describe("Number of logpoints"),watchExpressionCount:Re.number().describe("Number of watch expressions"),dompointCount:Re.number().describe("Number of dompoints"),netpointCount:Re.number().describe("Number of netpoints"),snapshotStats:Re.object({totalSnapshots:Re.number(),snapshotsByProbe:Re.record(Re.number()),oldestTimestamp:Re.number().optional(),newestTimestamp:Re.number().optional(),averageCaptureTimeMs:Re.number()}).nullable().describe("Snapshot statistics")}}async handle(e,t){if(!R(e.browserContext))return{enabled:!1,hasSourceMaps:!1,exceptionBreakpoint:"none",tracepointCount:0,logpointCount:0,watchExpressionCount:0,dompointCount:0,netpointCount:0,snapshotStats:null};let r=we(e.browserContext),s=r.filter(u=>u.kind==="tracepoint").length,a=r.filter(u=>u.kind==="logpoint").length;return{enabled:!0,hasSourceMaps:vi(e.browserContext),exceptionBreakpoint:Jt(e.browserContext),tracepointCount:s,logpointCount:a,watchExpressionCount:kn(e.browserContext).length,dompointCount:ht(e.browserContext).length,netpointCount:yt(e.browserContext).length,snapshotStats:Ei(e.browserContext)}}};import{z as Ue}from"zod";var An=class{static{i(this,"PutTracepoint")}name(){return"debug_put-tracepoint"}description(){return`
|
|
283
|
-
Puts a non-blocking tracepoint at the specified location.
|
|
284
|
-
When hit, a snapshot of the call stack and local variables is captured
|
|
285
|
-
automatically without pausing execution.
|
|
286
|
-
|
|
287
|
-
The urlPattern matches script URLs. Special characters are auto-escaped.
|
|
288
|
-
Examples:
|
|
289
|
-
- "app.js" matches scripts containing "app.js"
|
|
290
|
-
- "bundle.min.js" matches scripts containing "bundle.min.js"
|
|
291
|
-
|
|
292
|
-
DO NOT escape characters yourself (e.g., don't use "app\\.js").
|
|
293
|
-
|
|
294
|
-
Returns resolvedLocations: number of scripts where the tracepoint was set.
|
|
295
|
-
If 0, the pattern didn't match any loaded scripts.
|
|
296
|
-
`}inputSchema(){return{urlPattern:Ue.string().describe('Script URL pattern (e.g., "app.js"). Auto-escaped, do not add backslashes.'),lineNumber:Ue.number().int().positive().describe("Line number (1-based)"),columnNumber:Ue.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),condition:Ue.string().describe("Conditional expression - only triggers if this evaluates to true").optional(),hitCondition:Ue.string().describe('Hit count condition, e.g., "== 5" (5th hit), ">= 10" (10th and after), "% 10 == 0" (every 10th)').optional()}}outputSchema(){return{id:Ue.string().describe("Tracepoint ID"),urlPattern:Ue.string().describe("URL pattern"),lineNumber:Ue.number().describe("Line number"),columnNumber:Ue.number().optional().describe("Column number"),condition:Ue.string().optional().describe("Condition expression"),hitCondition:Ue.string().optional().describe("Hit count condition"),resolvedLocations:Ue.number().describe("Number of locations where tracepoint was resolved")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=await _n(e.browserContext,{kind:"tracepoint",urlPattern:t.urlPattern,lineNumber:t.lineNumber,columnNumber:t.columnNumber,condition:t.condition,hitCondition:t.hitCondition});return{id:n.id,urlPattern:n.urlPattern,lineNumber:n.lineNumber,columnNumber:n.columnNumber,condition:n.condition,hitCondition:n.hitCondition,resolvedLocations:n.resolvedLocations}}};import{z as pr}from"zod";var Ln=class{static{i(this,"RemoveTracepoint")}name(){return"debug_remove-tracepoint"}description(){return"Removes a tracepoint by its ID."}inputSchema(){return{id:pr.string().describe("Tracepoint ID to remove")}}outputSchema(){return{success:pr.boolean().describe("Whether the tracepoint was removed"),message:pr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{success:!1,message:"No active tracepoints"};let n=Mn(e.browserContext,t.id);if(!n||n.kind!=="tracepoint")return{success:!1,message:`Tracepoint ${t.id} not found`};let r=await ft(e.browserContext,t.id);return{success:r,message:r?`Tracepoint ${t.id} removed`:"Failed to remove tracepoint"}}};import{z as Be}from"zod";var Bn=class{static{i(this,"ListTracepoints")}name(){return"debug_list-tracepoints"}description(){return"Lists all active tracepoints."}inputSchema(){return{}}outputSchema(){return{tracepoints:Be.array(Be.object({id:Be.string(),urlPattern:Be.string(),lineNumber:Be.number(),columnNumber:Be.number().optional(),condition:Be.string().optional(),hitCondition:Be.string().optional(),enabled:Be.boolean(),resolvedLocations:Be.number(),hitCount:Be.number(),lastHitAt:Be.number().optional()})).describe("List of tracepoints"),total:Be.number().describe("Total count")}}async handle(e,t){if(!R(e.browserContext))return{tracepoints:[],total:0};let r=we(e.browserContext).filter(s=>s.kind==="tracepoint").map(s=>({id:s.id,urlPattern:s.urlPattern,lineNumber:s.lineNumber,columnNumber:s.columnNumber,condition:s.condition,hitCondition:s.hitCondition,enabled:s.enabled,resolvedLocations:s.resolvedLocations,hitCount:s.hitCount,lastHitAt:s.lastHitAt}));return{tracepoints:r,total:r.length}}};import{z as Ai}from"zod";var Fn=class{static{i(this,"ClearTracepoints")}name(){return"debug_clear-tracepoints"}description(){return"Removes all tracepoints."}inputSchema(){return{}}outputSchema(){return{clearedCount:Ai.number().describe("Number of tracepoints cleared"),message:Ai.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No tracepoints to clear"};let n=we(e.browserContext).filter(s=>s.kind==="tracepoint"),r=0;for(let s of n)await ft(e.browserContext,s.id)&&r++;return{clearedCount:r,message:`Cleared ${r} tracepoint(s)`}}};import{z as De}from"zod";var Hn=class{static{i(this,"PutLogpoint")}name(){return"debug_put-logpoint"}description(){return`
|
|
297
|
-
Puts a logpoint at the specified location.
|
|
298
|
-
When the logpoint is hit, the logExpression is evaluated and the result
|
|
299
|
-
is captured in the snapshot's logResult field.
|
|
300
|
-
|
|
301
|
-
Logpoints are lightweight - they only capture the log expression result,
|
|
302
|
-
NOT call stack or watch expressions. Use tracepoints for full debug context.
|
|
303
|
-
|
|
304
|
-
urlPattern matches script URLs (e.g., "app.js"). Auto-escaped, do not add backslashes.
|
|
305
|
-
|
|
306
|
-
logExpression examples:
|
|
307
|
-
- Simple value: "user.name"
|
|
308
|
-
- Template: "\`User: \${user.name}, Age: \${user.age}\`"
|
|
309
|
-
- Object: "{ user, timestamp: Date.now() }"
|
|
310
|
-
|
|
311
|
-
Returns resolvedLocations: 0 means pattern didn't match any loaded scripts.
|
|
312
|
-
`}inputSchema(){return{urlPattern:De.string().describe('Script URL pattern (e.g., "app.js"). Auto-escaped, do not add backslashes.'),lineNumber:De.number().int().positive().describe("Line number (1-based)"),logExpression:De.string().describe("Expression to evaluate and log when hit"),columnNumber:De.number().int().nonnegative().describe("Column number (1-based). Optional.").optional(),condition:De.string().describe("Conditional expression - logpoint only hits if this evaluates to true").optional(),hitCondition:De.string().describe('Hit count condition, e.g., "== 5" (5th hit), ">= 10" (10th and after), "% 10 == 0" (every 10th)').optional()}}outputSchema(){return{id:De.string().describe("Debug point ID"),urlPattern:De.string().describe("URL pattern"),lineNumber:De.number().describe("Line number"),logExpression:De.string().describe("Log expression"),columnNumber:De.number().optional().describe("Column number"),condition:De.string().optional().describe("Condition expression"),hitCondition:De.string().optional().describe("Hit count condition"),resolvedLocations:De.number().describe("Number of locations where logpoint was resolved")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=await _n(e.browserContext,{kind:"logpoint",urlPattern:t.urlPattern,lineNumber:t.lineNumber,logExpression:t.logExpression,columnNumber:t.columnNumber,condition:t.condition,hitCondition:t.hitCondition});return{id:n.id,urlPattern:n.urlPattern,lineNumber:n.lineNumber,logExpression:n.logExpression||t.logExpression,columnNumber:n.columnNumber,condition:n.condition,hitCondition:n.hitCondition,resolvedLocations:n.resolvedLocations}}};import{z as dr}from"zod";var Wn=class{static{i(this,"RemoveLogpoint")}name(){return"debug_remove-logpoint"}description(){return"Removes a logpoint by its ID."}inputSchema(){return{id:dr.string().describe("Logpoint ID to remove")}}outputSchema(){return{success:dr.boolean().describe("Whether the logpoint was removed"),message:dr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{success:!1,message:"No active logpoints"};let n=Mn(e.browserContext,t.id);if(!n||n.kind!=="logpoint")return{success:!1,message:`Logpoint ${t.id} not found`};let r=await ft(e.browserContext,t.id);return{success:r,message:r?`Logpoint ${t.id} removed`:"Failed to remove logpoint"}}};import{z as Ae}from"zod";var Un=class{static{i(this,"ListLogpoints")}name(){return"debug_list-logpoints"}description(){return"Lists all active logpoints."}inputSchema(){return{}}outputSchema(){return{logpoints:Ae.array(Ae.object({id:Ae.string(),urlPattern:Ae.string(),lineNumber:Ae.number(),logExpression:Ae.string(),columnNumber:Ae.number().optional(),condition:Ae.string().optional(),hitCondition:Ae.string().optional(),enabled:Ae.boolean(),resolvedLocations:Ae.number(),hitCount:Ae.number(),lastHitAt:Ae.number().optional()})).describe("List of logpoints"),total:Ae.number().describe("Total count")}}async handle(e,t){if(!R(e.browserContext))return{logpoints:[],total:0};let r=we(e.browserContext).filter(s=>s.kind==="logpoint").map(s=>({id:s.id,urlPattern:s.urlPattern,lineNumber:s.lineNumber,logExpression:s.logExpression||"",columnNumber:s.columnNumber,condition:s.condition,hitCondition:s.hitCondition,enabled:s.enabled,resolvedLocations:s.resolvedLocations,hitCount:s.hitCount,lastHitAt:s.lastHitAt}));return{logpoints:r,total:r.length}}};import{z as Li}from"zod";var qn=class{static{i(this,"ClearLogpoints")}name(){return"debug_clear-logpoints"}description(){return"Removes all logpoints."}inputSchema(){return{}}outputSchema(){return{clearedCount:Li.number().describe("Number of logpoints cleared"),message:Li.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No logpoints to clear"};let n=we(e.browserContext).filter(s=>s.kind==="logpoint"),r=0;for(let s of n)await ft(e.browserContext,s.id)&&r++;return{clearedCount:r,message:`Cleared ${r} logpoint(s)`}}};import{z as Vn}from"zod";var zn=class{static{i(this,"PutExceptionpoint")}name(){return"debug_put-exceptionpoint"}description(){return`
|
|
313
|
-
Sets the exception tracepoint state:
|
|
314
|
-
- "none": Don't capture on exceptions
|
|
315
|
-
- "uncaught": Capture only on uncaught exceptions
|
|
316
|
-
- "all": Capture on all exceptions (caught and uncaught)
|
|
317
|
-
|
|
318
|
-
When an exception occurs, a snapshot is captured with exception details.
|
|
319
|
-
`}inputSchema(){return{state:Vn.enum(["none","uncaught","all"]).describe("Exception tracepoint state")}}outputSchema(){return{previousState:Vn.string().describe("Previous state"),currentState:Vn.string().describe("Current state"),message:Vn.string().describe("Status message")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=Jt(e.browserContext);await Ci(e.browserContext,t.state);let r=Jt(e.browserContext);return{previousState:n,currentState:r,message:`Exception tracepoint set to: ${r}`}}};import{z as Zt}from"zod";import{z as S}from"zod";var br=S.object({source:S.string().describe("Original source file path"),line:S.number().describe("1-based line number"),column:S.number().optional().describe("1-based column number"),name:S.string().optional().describe("Original identifier name")}),Za=S.object({name:S.string().describe("Variable name"),value:S.any().describe("Variable value"),type:S.string().describe("Variable type")}),eu=S.object({type:S.string().describe("Scope type (global, local, closure, etc.)"),name:S.string().optional().describe("Scope name"),variables:S.array(Za).describe("Variables in this scope")}),fr=S.object({functionName:S.string().describe("Function name"),url:S.string().describe("Script URL"),lineNumber:S.number().describe("1-based line number"),columnNumber:S.number().optional().describe("1-based column number"),scriptId:S.string().describe("V8 script ID"),scopes:S.array(eu).describe("Variable scopes"),originalLocation:br.optional().describe("Original source location")}),tu=S.object({functionName:S.string().describe("Function name"),url:S.string().describe("Script URL"),lineNumber:S.number().describe("1-based line number"),columnNumber:S.number().optional().describe("1-based column number"),originalLocation:br.optional().describe("Original source location")}),nu=S.object({description:S.string().optional().describe("Async boundary (Promise.then, setTimeout, etc.)"),callFrames:S.array(tu).describe("Frames in this segment")}),gr=S.object({segments:S.array(nu).describe("Chain of async segments")}),Qt=S.object({id:S.string().describe("Snapshot ID"),probeId:S.string().describe("Probe ID that triggered this snapshot"),timestamp:S.number().describe("Unix timestamp (ms)"),sequenceNumber:S.number().describe("Monotonic sequence number for ordering"),url:S.string().describe("Script URL where snapshot was taken"),lineNumber:S.number().describe("1-based line number"),columnNumber:S.number().optional().describe("1-based column number"),originalLocation:br.optional().describe("Original source location"),captureTimeMs:S.number().describe("Time taken to capture snapshot (ms)")}),Bi=Qt.extend({callStack:S.array(fr).describe("Call stack with local variables"),asyncStackTrace:gr.optional().describe("Async stack trace"),watchResults:S.record(S.any()).optional().describe("Watch expression results")}),Fi=Qt.extend({logResult:S.any().optional().describe("Result of log expression evaluation")}),ou=S.object({type:S.enum(["exception","promiseRejection"]).describe("Exception type"),message:S.string().describe("Exception message"),name:S.string().optional().describe("Exception name/class"),stack:S.string().optional().describe("Stack trace string")}),Hi=Qt.extend({exception:ou.optional().describe("Exception information"),callStack:S.array(fr).describe("Call stack at exception"),asyncStackTrace:gr.optional().describe("Async stack trace"),watchResults:S.record(S.any()).optional().describe("Watch expression results")}),ru=S.object({type:S.enum(["subtree-modified","attribute-modified","node-removed"]).describe("DOM mutation type"),selector:S.string().describe("CSS selector of watched element"),targetNode:S.string().optional().describe("Outer HTML snippet of target"),attributeName:S.string().optional().describe("Changed attribute name"),oldValue:S.string().optional().describe("Previous value"),newValue:S.string().optional().describe("New value")}),Wi=Qt.extend({domChange:ru.optional().describe("DOM change information"),callStack:S.array(fr).describe("Call stack when DOM changed"),asyncStackTrace:gr.optional().describe("Async stack trace"),watchResults:S.record(S.any()).optional().describe("Watch expression results")}),iu=S.object({url:S.string().describe("Request URL"),method:S.string().describe("HTTP method"),requestHeaders:S.record(S.string()).optional().describe("Request headers"),requestBody:S.string().optional().describe("Request body"),status:S.number().optional().describe("Response status code"),statusText:S.string().optional().describe("Response status text"),responseHeaders:S.record(S.string()).optional().describe("Response headers"),responseBody:S.string().optional().describe("Response body"),resourceType:S.string().optional().describe("Resource type (xhr, fetch, etc.)"),timing:S.enum(["request","response"]).describe("When snapshot was taken"),duration:S.number().optional().describe("Request duration (ms)"),error:S.string().optional().describe("Error message if failed")}),Ui=Qt.extend({networkRequest:iu.optional().describe("Network request/response info")});var $n=class{static{i(this,"GetTracepointSnapshots")}name(){return"debug_get-tracepoint-snapshots"}description(){return`
|
|
320
|
-
Retrieves snapshots captured by tracepoints.
|
|
321
|
-
|
|
322
|
-
Each snapshot contains:
|
|
323
|
-
- Call stack with local variables
|
|
324
|
-
- Async stack trace (if available)
|
|
325
|
-
- Original source location (if source maps loaded)
|
|
326
|
-
- Watch expression results
|
|
327
|
-
`}inputSchema(){return{probeId:Zt.string().describe("Filter by specific tracepoint ID").optional(),fromSequence:Zt.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:Zt.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:Zt.array(Bi).describe("Array of tracepoint snapshots"),total:Zt.number().describe("Total number of matching snapshots")}}async handle(e,t){if(!R(e.browserContext))return{snapshots:[],total:0};let n;if(t.probeId)n=nt(e.browserContext,t.probeId);else{let s=new Set(we(e.browserContext).filter(a=>a.kind==="tracepoint").map(a=>a.id));n=gt(e.browserContext).filter(a=>s.has(a.probeId))}t.fromSequence!==void 0&&(n=n.filter(s=>s.sequenceNumber>t.fromSequence));let r=n.length;return t.limit&&n.length>t.limit&&(n=n.slice(0,t.limit)),{snapshots:n,total:r}}};import{z as hr}from"zod";var Gn=class{static{i(this,"ClearTracepointSnapshots")}name(){return"debug_clear-tracepoint-snapshots"}description(){return"Clears snapshots captured by tracepoints. Optionally filter by specific tracepoint ID."}inputSchema(){return{probeId:hr.string().describe("Clear only snapshots for this tracepoint ID").optional()}}outputSchema(){return{clearedCount:hr.number().describe("Number of snapshots cleared"),message:hr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No snapshots to clear"};let n=0;if(t.probeId)n=ke(e.browserContext,t.probeId);else{let r=we(e.browserContext).filter(s=>s.kind==="tracepoint").map(s=>s.id);for(let s of r)n+=ke(e.browserContext,s)}return{clearedCount:n,message:`Cleared ${n} tracepoint snapshot(s)`}}};import{z as en}from"zod";var jn=class{static{i(this,"GetLogpointSnapshots")}name(){return"debug_get-logpoint-snapshots"}description(){return`
|
|
328
|
-
Retrieves snapshots captured by logpoints.
|
|
329
|
-
|
|
330
|
-
Each snapshot contains:
|
|
331
|
-
- Log expression result
|
|
332
|
-
- Original source location (if source maps loaded)
|
|
333
|
-
- Timestamp and sequence number
|
|
334
|
-
|
|
335
|
-
Note: Logpoints do NOT capture call stack or watch expressions.
|
|
336
|
-
Use tracepoints if you need full debug context.
|
|
337
|
-
`}inputSchema(){return{probeId:en.string().describe("Filter by specific logpoint ID").optional(),fromSequence:en.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:en.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:en.array(Fi).describe("Array of logpoint snapshots"),total:en.number().describe("Total number of matching snapshots")}}async handle(e,t){if(!R(e.browserContext))return{snapshots:[],total:0};let n;if(t.probeId)n=nt(e.browserContext,t.probeId);else{let a=new Set(we(e.browserContext).filter(u=>u.kind==="logpoint").map(u=>u.id));n=gt(e.browserContext).filter(u=>a.has(u.probeId))}t.fromSequence!==void 0&&(n=n.filter(a=>a.sequenceNumber>t.fromSequence));let r=n.length;return t.limit&&n.length>t.limit&&(n=n.slice(0,t.limit)),{snapshots:n.map(a=>({id:a.id,probeId:a.probeId,timestamp:a.timestamp,sequenceNumber:a.sequenceNumber,url:a.url,lineNumber:a.lineNumber,columnNumber:a.columnNumber,originalLocation:a.originalLocation,captureTimeMs:a.captureTimeMs,logResult:a.logResult})),total:r}}};import{z as yr}from"zod";var Kn=class{static{i(this,"ClearLogpointSnapshots")}name(){return"debug_clear-logpoint-snapshots"}description(){return"Clears snapshots captured by logpoints. Optionally filter by specific logpoint ID."}inputSchema(){return{probeId:yr.string().describe("Clear only snapshots for this logpoint ID").optional()}}outputSchema(){return{clearedCount:yr.number().describe("Number of snapshots cleared"),message:yr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No snapshots to clear"};let n=0;if(t.probeId)n=ke(e.browserContext,t.probeId);else{let r=we(e.browserContext).filter(s=>s.kind==="logpoint").map(s=>s.id);for(let s of r)n+=ke(e.browserContext,s)}return{clearedCount:n,message:`Cleared ${n} logpoint snapshot(s)`}}};import{z as Xn}from"zod";var Yn=class{static{i(this,"GetExceptionpointSnapshots")}name(){return"debug_get-exceptionpoint-snapshots"}description(){return`
|
|
338
|
-
Retrieves snapshots captured by exceptionpoints.
|
|
339
|
-
|
|
340
|
-
Each snapshot contains:
|
|
341
|
-
- Exception info (type, message, stack)
|
|
342
|
-
- Call stack with local variables at exception point
|
|
343
|
-
- Async stack trace (if available)
|
|
344
|
-
- Original source location (if source maps loaded)
|
|
345
|
-
- Watch expression results
|
|
346
|
-
`}inputSchema(){return{fromSequence:Xn.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:Xn.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:Xn.array(Hi).describe("Array of exceptionpoint snapshots"),total:Xn.number().describe("Total number of matching snapshots")}}async handle(e,t){if(!R(e.browserContext))return{snapshots:[],total:0};let n=nt(e.browserContext,"__exception__");t.fromSequence!==void 0&&(n=n.filter(s=>s.sequenceNumber>t.fromSequence));let r=n.length;return t.limit&&n.length>t.limit&&(n=n.slice(0,t.limit)),{snapshots:n,total:r}}};import{z as qi}from"zod";var Jn=class{static{i(this,"ClearExceptionpointSnapshots")}name(){return"debug_clear-exceptionpoint-snapshots"}description(){return"Clears all snapshots captured by exceptionpoints."}inputSchema(){return{}}outputSchema(){return{clearedCount:qi.number().describe("Number of snapshots cleared"),message:qi.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No snapshots to clear"};let n=ke(e.browserContext,"__exception__");return{clearedCount:n,message:`Cleared ${n} exceptionpoint snapshot(s)`}}};import{z as tn}from"zod";var Qn=class{static{i(this,"GetDompointSnapshots")}name(){return"debug_get-dompoint-snapshots"}description(){return`
|
|
347
|
-
Retrieves snapshots captured by dompoints.
|
|
348
|
-
|
|
349
|
-
Each snapshot contains:
|
|
350
|
-
- DOM change info (mutation type, target, old/new values)
|
|
351
|
-
- Call stack with local variables (if triggered during JS execution)
|
|
352
|
-
- Async stack trace (if available)
|
|
353
|
-
- Original source location (if source maps loaded)
|
|
354
|
-
- Watch expression results
|
|
355
|
-
`}inputSchema(){return{probeId:tn.string().describe("Filter by specific dompoint ID").optional(),fromSequence:tn.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:tn.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:tn.array(Wi).describe("Array of dompoint snapshots"),total:tn.number().describe("Total number of matching snapshots")}}async handle(e,t){if(!R(e.browserContext))return{snapshots:[],total:0};let n;if(t.probeId)n=nt(e.browserContext,t.probeId);else{let s=new Set(ht(e.browserContext).map(a=>a.id));n=gt(e.browserContext).filter(a=>s.has(a.probeId))}t.fromSequence!==void 0&&(n=n.filter(s=>s.sequenceNumber>t.fromSequence));let r=n.length;return t.limit&&n.length>t.limit&&(n=n.slice(0,t.limit)),{snapshots:n,total:r}}};import{z as Sr}from"zod";var Zn=class{static{i(this,"ClearDompointSnapshots")}name(){return"debug_clear-dompoint-snapshots"}description(){return"Clears snapshots captured by dompoints. If probeId is specified, only clears snapshots for that dompoint."}inputSchema(){return{probeId:Sr.string().describe("Optional dompoint ID to clear snapshots for").optional()}}outputSchema(){return{clearedCount:Sr.number().describe("Number of snapshots cleared"),message:Sr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No snapshots to clear"};let n=0;if(t.probeId)n=ke(e.browserContext,t.probeId);else{let r=ht(e.browserContext);for(let s of r)n+=ke(e.browserContext,s.id)}return{clearedCount:n,message:`Cleared ${n} dompoint snapshot(s)`}}};import{z as nn}from"zod";var eo=class{static{i(this,"GetNetpointSnapshots")}name(){return"debug_get-netpoint-snapshots"}description(){return`
|
|
356
|
-
Retrieves snapshots captured by netpoints.
|
|
357
|
-
|
|
358
|
-
Each snapshot contains:
|
|
359
|
-
- Network request info (URL, method, headers, body)
|
|
360
|
-
- Response info (status, headers, body) when available
|
|
361
|
-
- Timing information
|
|
362
|
-
`}inputSchema(){return{probeId:nn.string().describe("Filter by specific netpoint ID").optional(),fromSequence:nn.number().int().nonnegative().describe("Return snapshots with sequence number > fromSequence (for polling)").optional(),limit:nn.number().int().positive().describe("Maximum number of snapshots to return").optional()}}outputSchema(){return{snapshots:nn.array(Ui).describe("Array of netpoint snapshots"),total:nn.number().describe("Total number of matching snapshots")}}async handle(e,t){if(!R(e.browserContext))return{snapshots:[],total:0};let n;if(t.probeId)n=nt(e.browserContext,t.probeId);else{let s=new Set(yt(e.browserContext).map(a=>a.id));n=gt(e.browserContext).filter(a=>s.has(a.probeId))}t.fromSequence!==void 0&&(n=n.filter(s=>s.sequenceNumber>t.fromSequence));let r=n.length;return t.limit&&n.length>t.limit&&(n=n.slice(0,t.limit)),{snapshots:n,total:r}}};import{z as wr}from"zod";var to=class{static{i(this,"ClearNetpointSnapshots")}name(){return"debug_clear-netpoint-snapshots"}description(){return"Clears snapshots captured by netpoints. If probeId is specified, only clears snapshots for that netpoint."}inputSchema(){return{probeId:wr.string().describe("Optional netpoint ID to clear snapshots for").optional()}}outputSchema(){return{clearedCount:wr.number().describe("Number of snapshots cleared"),message:wr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No snapshots to clear"};let n=0;if(t.probeId)n=ke(e.browserContext,t.probeId);else{let r=yt(e.browserContext);for(let s of r)n+=ke(e.browserContext,s.id)}return{clearedCount:n,message:`Cleared ${n} netpoint snapshot(s)`}}};import{z as no}from"zod";var oo=class{static{i(this,"AddWatch")}name(){return"debug_add-watch"}description(){return`
|
|
363
|
-
Adds a watch expression to be evaluated at every breakpoint hit.
|
|
364
|
-
Watch expression results are included in the snapshot's watchResults field.
|
|
365
|
-
|
|
366
|
-
Examples:
|
|
367
|
-
- "user.name"
|
|
368
|
-
- "this.state"
|
|
369
|
-
- "items.length"
|
|
370
|
-
- "JSON.stringify(config)"
|
|
371
|
-
|
|
372
|
-
Watch expressions are evaluated in the context of the paused frame.
|
|
373
|
-
`}inputSchema(){return{expression:no.string().describe("JavaScript expression to watch")}}outputSchema(){return{id:no.string().describe("Watch expression ID"),expression:no.string().describe("The watch expression"),message:no.string().describe("Status message")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=Oi(e.browserContext,t.expression);return{id:n.id,expression:n.expression,message:`Watch expression added: ${t.expression}`}}};import{z as Tr}from"zod";var ro=class{static{i(this,"RemoveWatch")}name(){return"debug_remove-watch"}description(){return`
|
|
374
|
-
Removes a watch expression by its ID.
|
|
375
|
-
Use debug_list-watches to get the IDs of active watch expressions.
|
|
376
|
-
`}inputSchema(){return{id:Tr.string().describe("Watch expression ID to remove")}}outputSchema(){return{success:Tr.boolean().describe("Whether the watch was removed"),message:Tr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{success:!1,message:"Debugging is not active"};let n=Pi(e.browserContext,t.id);return{success:n,message:n?`Watch expression ${t.id} removed`:`Watch expression ${t.id} not found`}}};import{z as Bt}from"zod";var io=class{static{i(this,"ListWatches")}name(){return"debug_list-watches"}description(){return`
|
|
377
|
-
Lists all active watch expressions.
|
|
378
|
-
`}inputSchema(){return{}}outputSchema(){return{watches:Bt.array(Bt.object({id:Bt.string(),expression:Bt.string(),createdAt:Bt.number()})).describe("List of watch expressions"),total:Bt.number().describe("Total count")}}async handle(e,t){if(!R(e.browserContext))return{watches:[],total:0};let r=kn(e.browserContext).map(s=>({id:s.id,expression:s.expression,createdAt:s.createdAt}));return{watches:r,total:r.length}}};import{z as Vi}from"zod";var so=class{static{i(this,"ClearWatches")}name(){return"debug_clear-watches"}description(){return`
|
|
379
|
-
Removes all watch expressions.
|
|
380
|
-
`}inputSchema(){return{}}outputSchema(){return{clearedCount:Vi.number().describe("Number of watch expressions cleared"),message:Vi.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No watch expressions to clear"};let n=Ri(e.browserContext);return{clearedCount:n,message:`Cleared ${n} watch expression(s)`}}};import{z as ut}from"zod";var ao=class{static{i(this,"PutDompoint")}name(){return"debug_put-dompoint"}description(){return`
|
|
381
|
-
Puts a DOM tracepoint that triggers when the element changes.
|
|
382
|
-
|
|
383
|
-
Types:
|
|
384
|
-
- "subtree-modified": Triggers when children are added/removed
|
|
385
|
-
- "attribute-modified": Triggers when attributes change
|
|
386
|
-
- "node-removed": Triggers when the element is removed
|
|
387
|
-
|
|
388
|
-
When triggered, a snapshot is captured with DOM change info.
|
|
389
|
-
|
|
390
|
-
Example:
|
|
391
|
-
- selector: "#myButton", type: "attribute-modified", attributeName: "disabled"
|
|
392
|
-
- selector: ".list-container", type: "subtree-modified"
|
|
393
|
-
`}inputSchema(){return{selector:ut.string().describe("CSS selector for the target element"),type:ut.enum(["subtree-modified","attribute-modified","node-removed"]).describe("Type of DOM change to monitor"),attributeName:ut.string().describe("For attribute-modified: specific attribute to monitor").optional()}}outputSchema(){return{id:ut.string().describe("Dompoint ID"),selector:ut.string().describe("CSS selector"),type:ut.string().describe("Dompoint type"),attributeName:ut.string().optional().describe("Monitored attribute"),nodeId:ut.number().optional().describe("CDP node ID"),message:ut.string().describe("Status message")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=await Ni(e.browserContext,e.page,{selector:t.selector,type:t.type,attributeName:t.attributeName});return{id:n.id,selector:n.selector,type:n.type,attributeName:n.attributeName,nodeId:n.nodeId,message:`Dompoint set on "${t.selector}" for ${t.type}`}}};import{z as xr}from"zod";var uo=class{static{i(this,"RemoveDompoint")}name(){return"debug_remove-dompoint"}description(){return"Removes a dompoint by its ID."}inputSchema(){return{id:xr.string().describe("Dompoint ID to remove")}}outputSchema(){return{success:xr.boolean().describe("Whether the dompoint was removed"),message:xr.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{success:!1,message:"No active dompoints"};let n=await mr(e.browserContext,t.id,e.page);return{success:n,message:n?`Dompoint ${t.id} removed`:`Dompoint ${t.id} not found`}}};import{z as ot}from"zod";var lo=class{static{i(this,"ListDompoints")}name(){return"debug_list-dompoints"}description(){return"Lists all active dompoints."}inputSchema(){return{}}outputSchema(){return{dompoints:ot.array(ot.object({id:ot.string(),selector:ot.string(),type:ot.string(),attributeName:ot.string().optional(),enabled:ot.boolean(),hitCount:ot.number(),lastHitAt:ot.number().optional()})).describe("List of dompoints"),total:ot.number().describe("Total count")}}async handle(e,t){if(!R(e.browserContext))return{dompoints:[],total:0};let r=ht(e.browserContext).map(s=>({id:s.id,selector:s.selector,type:s.type,attributeName:s.attributeName,enabled:s.enabled,hitCount:s.hitCount,lastHitAt:s.lastHitAt}));return{dompoints:r,total:r.length}}};import{z as zi}from"zod";var co=class{static{i(this,"ClearDompoints")}name(){return"debug_clear-dompoints"}description(){return"Removes all dompoints."}inputSchema(){return{}}outputSchema(){return{clearedCount:zi.number().describe("Number of dompoints cleared"),message:zi.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No dompoints to clear"};let n=await _i(e.browserContext,e.page);return{clearedCount:n,message:`Cleared ${n} dompoint(s)`}}};import{z as rt}from"zod";var mo=class{static{i(this,"PutNetpoint")}name(){return"debug_put-netpoint"}description(){return`
|
|
394
|
-
Puts a network tracepoint that triggers on matching HTTP requests.
|
|
395
|
-
|
|
396
|
-
When triggered, a snapshot is captured with request/response details including:
|
|
397
|
-
- URL, method, headers
|
|
398
|
-
- Request body (for request timing)
|
|
399
|
-
- Response status, headers, body (for response timing)
|
|
400
|
-
|
|
401
|
-
Parameters:
|
|
402
|
-
- urlPattern: Glob pattern to match URLs (e.g., "**/api/**")
|
|
403
|
-
- method: Optional HTTP method filter (GET, POST, etc.)
|
|
404
|
-
- timing: "request" (before sent) or "response" (after received)
|
|
405
|
-
- onError: Only trigger on error responses (4xx, 5xx)
|
|
406
|
-
|
|
407
|
-
Examples:
|
|
408
|
-
- urlPattern: "**/api/users/**", timing: "response"
|
|
409
|
-
- urlPattern: "**/graphql", method: "POST", timing: "request"
|
|
410
|
-
`}inputSchema(){return{urlPattern:rt.string().describe("Glob pattern to match request URLs"),method:rt.string().describe("HTTP method filter (GET, POST, PUT, DELETE, etc.)").optional(),timing:rt.enum(["request","response"]).describe('When to trigger: "request" or "response". Default: "request"').optional().default("request"),onError:rt.boolean().describe("Only trigger on error responses (4xx, 5xx)").optional()}}outputSchema(){return{id:rt.string().describe("Netpoint ID"),urlPattern:rt.string().describe("URL pattern"),method:rt.string().optional().describe("HTTP method filter"),timing:rt.string().describe("Trigger timing"),onError:rt.boolean().optional().describe("Error-only filter"),message:rt.string().describe("Status message")}}async handle(e,t){R(e.browserContext)||await He(e.browserContext,e.page);let n=await Mi(e.browserContext,e.page,{urlPattern:t.urlPattern,method:t.method,timing:t.timing||"request",onError:t.onError});return{id:n.id,urlPattern:n.urlPattern,method:n.method,timing:n.timing,onError:n.onError,message:`Netpoint set for ${t.urlPattern} on ${t.timing||"request"}`}}};import{z as Ir}from"zod";var po=class{static{i(this,"RemoveNetpoint")}name(){return"debug_remove-netpoint"}description(){return"Removes a netpoint by its ID."}inputSchema(){return{id:Ir.string().describe("Netpoint ID to remove")}}outputSchema(){return{success:Ir.boolean().describe("Whether the netpoint was removed"),message:Ir.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{success:!1,message:"No active netpoints"};let n=ki(e.browserContext,t.id);return{success:n,message:n?`Netpoint ${t.id} removed`:`Netpoint ${t.id} not found`}}};import{z as Ge}from"zod";var bo=class{static{i(this,"ListNetpoints")}name(){return"debug_list-netpoints"}description(){return"Lists all active netpoints."}inputSchema(){return{}}outputSchema(){return{netpoints:Ge.array(Ge.object({id:Ge.string(),urlPattern:Ge.string(),method:Ge.string().optional(),timing:Ge.string(),onError:Ge.boolean().optional(),enabled:Ge.boolean(),hitCount:Ge.number(),lastHitAt:Ge.number().optional()})).describe("List of netpoints"),total:Ge.number().describe("Total count")}}async handle(e,t){if(!R(e.browserContext))return{netpoints:[],total:0};let r=yt(e.browserContext).map(s=>({id:s.id,urlPattern:s.urlPattern,method:s.method,timing:s.timing,onError:s.onError,enabled:s.enabled,hitCount:s.hitCount,lastHitAt:s.lastHitAt}));return{netpoints:r,total:r.length}}};import{z as $i}from"zod";var fo=class{static{i(this,"ClearNetpoints")}name(){return"debug_clear-netpoints"}description(){return"Removes all netpoints."}inputSchema(){return{}}outputSchema(){return{clearedCount:$i.number().describe("Number of netpoints cleared"),message:$i.string().describe("Status message")}}async handle(e,t){if(!R(e.browserContext))return{clearedCount:0,message:"No netpoints to clear"};let n=Di(e.browserContext);return{clearedCount:n,message:`Cleared ${n} netpoint(s)`}}};var Gi=[new Dn,new An,new Ln,new Bn,new Fn,new Hn,new Wn,new Un,new qn,new zn,new $n,new Gn,new jn,new Kn,new Yn,new Jn,new Qn,new Zn,new eo,new to,new oo,new ro,new io,new so,new ao,new uo,new lo,new co,new mo,new po,new bo,new fo];import Cr from"sharp";import su from"ssim.js";var au=!1,uu=10;function lu(o){return Number.isFinite(o)?Math.max(0,Math.min(1,o)):0}i(lu,"_clamp01");async function ji(o,e,t,n){let r;if(n==="semantic"){r=Cr(o).resize(e,t,{fit:"cover",position:"centre"});let u=Math.max(1,Math.floor(e/2)),l=Math.max(1,Math.floor(t/2));r=r.resize(u,l,{fit:"cover",position:"centre"}).grayscale(au).blur(uu)}else r=Cr(o).resize(e,t,{fit:"cover",position:"centre"});let s=await r.ensureAlpha().raw().toBuffer({resolveWithObject:!0});return{data:new Uint8ClampedArray(s.data.buffer,s.data.byteOffset,s.data.byteLength),width:s.info.width,height:s.info.height}}i(ji,"_loadNormalized");function cu(o,e){let t=su({data:o.data,width:o.width,height:o.height},{data:e.data,width:e.width,height:e.height}),n=Number(t.mssim??t.ssim??0);return lu(n)}i(cu,"_computeSsim");async function Ki(o,e,t){let n=t?.mode??"semantic",r=t?.canvasWidth,s=t?.canvasHeight;if(typeof r!="number"||!Number.isFinite(r)||r<=0||typeof s!="number"||!Number.isFinite(s)||s<=0){let m=await Cr(e.image).metadata();if(r=m.width??0,s=m.height??0,r<=0||s<=0)throw new Error("Failed to read Figma image dimensions.")}let a=await ji(e.image,r,s,n),u=await ji(o.image,r,s,n);return{score:cu(a,u)}}i(Ki,"compare");function Xi(o,e){let t=Math.min(o.length,e.length),n=0;for(let r=0;r<t;r++)n+=o[r]*e[r];return n}i(Xi,"dot");function vr(o){let e=0;for(let t=0;t<o.length;t++){let n=o[t];e+=n*n}return Math.sqrt(e)}i(vr,"norm");function Yi(o){let e=vr(o);if(e===0)return o.slice();let t=new Array(o.length);for(let n=0;n<o.length;n++)t[n]=o[n]/e;return t}i(Yi,"l2Normalize");function go(o,e,t){if(t){let r=Yi(o),s=Yi(e);return Xi(r,s)}let n=vr(o)*vr(e);return n===0?0:Xi(o,e)/n}i(go,"cosineSimilarity");import{BedrockRuntimeClient as Ji,InvokeModelCommand as mu}from"@aws-sdk/client-bedrock-runtime";import{fromIni as pu}from"@aws-sdk/credential-providers";import du from"sharp";var bu=1024,fu=90,gu=1024;async function hu(o,e,t){let n=du(o),r=t?.maxDim||bu;if(n=n.resize({width:r,height:r,fit:"inside",withoutEnlargement:!0}),e==="png")return await n.png().toBuffer();let s=t?.jpegQuality||fu;return await n.jpeg({quality:s}).toBuffer()}i(hu,"_prepareImage");var yu=new Set(["amazon.titan-embed-image-v1"]),Su="amazon.titan-embed-image-v1",Ft;function wu(){return an&&!!Dt}i(wu,"_isAwsBedrockActive");function Tu(){if(Ft)return Ft;let o=Dt;if(!o)return;let e=sn;return e?(Ft=new Ji({region:o,credentials:pu({profile:e})}),Ft):(Ft=new Ji({region:o}),Ft)}i(Tu,"_getOrCreateBedrockClient");async function xu(o,e,t,n){let s={inputImage:(await hu(o.image,o.type,t)).toString("base64"),embeddingConfig:{outputEmbeddingLength:gu}},a=new mu({modelId:n,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(s),"utf-8")}),u=await e.send(a),l=u?.body instanceof Uint8Array?u.body:new Uint8Array(u?.body??[]),m=Buffer.from(l).toString("utf-8"),c;try{c=m?JSON.parse(m):{}}catch{throw new Error(`Amazon Bedrock Titan returned non-JSON response for embeddings: ${m.slice(0,300)}`)}let p=c?.embedding??c?.embeddings?.[0]??c?.outputEmbedding??c?.vector;if(!Array.isArray(p)||p.length===0||typeof p[0]!="number")throw new Error(`Unexpected Amazon Bedrock Titan image embedding response format: ${m.slice(0,500)}`);return p}i(xu,"_embedImageWithAmazonBedrockTitan");async function Iu(o,e,t){let n=e?.modelId??zr??Su;if(!yu.has(n))throw new Error(`Unsupported Amazon Bedrock image embedding model id: ${n}`);return await xu(o,t,e,n)}i(Iu,"_embedImageWithAmazonBedrock");async function Qi(o,e){if(wu()){let t=Tu();return t?Iu(o,e,t):void 0}}i(Qi,"_embedImage");async function Zi(o,e,t){let n=typeof t?.normalize=="boolean"?t.normalize:!0,r=await Qi(e,t);if(!r)return;let s=await Qi(o,t);return s?{score:go(r,s,n)}:void 0}i(Zi,"compare");import{BedrockRuntimeClient as es,InvokeModelCommand as Cu}from"@aws-sdk/client-bedrock-runtime";import{fromIni as vu}from"@aws-sdk/credential-providers";import Eu from"sharp";var Ou=`
|
|
411
|
-
You are analyzing a UI screenshot to compare it against another UI.
|
|
412
|
-
|
|
413
|
-
Your goal is to produce a STRUCTURAL LAYOUT FINGERPRINT that remains stable
|
|
414
|
-
even when real data, text values, or content change.
|
|
415
|
-
|
|
416
|
-
Write a concise but highly informative description using the rules below.
|
|
417
|
-
|
|
418
|
-
GENERAL RULES
|
|
419
|
-
- Describe WHAT EXISTS and WHERE IT IS, not how it looks visually.
|
|
420
|
-
- Prefer explicit structure and hierarchy over natural language.
|
|
421
|
-
- Be consistent and deterministic in wording.
|
|
422
|
-
- Do NOT describe colors, fonts, themes, or exact text.
|
|
423
|
-
- Do NOT include user data, names, numbers, timestamps, or labels.
|
|
424
|
-
|
|
425
|
-
LAYOUT STRUCTURE
|
|
426
|
-
Describe the UI from top to bottom:
|
|
427
|
-
|
|
428
|
-
1) PAGE REGIONS
|
|
429
|
-
- Identify major regions in order:
|
|
430
|
-
- top header / app bar
|
|
431
|
-
- left or right sidebar
|
|
432
|
-
- main content area
|
|
433
|
-
- footer (if present)
|
|
434
|
-
|
|
435
|
-
2) REGION DETAILS
|
|
436
|
-
For EACH region, describe:
|
|
437
|
-
- Position (top / left / right / center / full-width)
|
|
438
|
-
- Layout type (row, column, grid, split, stacked)
|
|
439
|
-
- Whether it is fixed or scrollable
|
|
440
|
-
- Primary purpose (navigation, content, controls, metadata)
|
|
441
|
-
|
|
442
|
-
3) COMPONENT INVENTORY
|
|
443
|
-
List the components that exist, grouped by region:
|
|
444
|
-
- navigation menus
|
|
445
|
-
- tabs
|
|
446
|
-
- tables (rows/columns, header present or not)
|
|
447
|
-
- lists (vertical/horizontal, item density: sparse/medium/dense)
|
|
448
|
-
- cards (count: single / few / many)
|
|
449
|
-
- forms (inline / multi-section)
|
|
450
|
-
- modals, drawers, overlays (present or not)
|
|
451
|
-
|
|
452
|
-
4) HIERARCHY & RELATIONSHIPS
|
|
453
|
-
Explicitly mention:
|
|
454
|
-
- parent \u2192 child relationships
|
|
455
|
-
- repeated patterns (e.g. "repeating card list", "uniform table rows")
|
|
456
|
-
- alignment relationships (sidebar + main content, header spanning all columns)
|
|
457
|
-
|
|
458
|
-
5) ABSENCE IS SIGNAL
|
|
459
|
-
If something is NOT present, state it explicitly when relevant:
|
|
460
|
-
- no sidebar
|
|
461
|
-
- no table
|
|
462
|
-
- no modal
|
|
463
|
-
- no pagination
|
|
464
|
-
|
|
465
|
-
FORMAT
|
|
466
|
-
- Use short bullet-style sentences.
|
|
467
|
-
- Use consistent phrasing across similar structures.
|
|
468
|
-
- Avoid synonyms (always say \u201Csidebar\u201D, not sometimes \u201Cside panel\u201D).
|
|
469
|
-
- Keep the output under ~30 lines.
|
|
470
|
-
|
|
471
|
-
Return plain text only. No markdown.
|
|
472
|
-
`;function Pu(o){return o?.prompt??Ou.trim()}i(Pu,"_resolvePrompt");function Ru(o){return typeof o?.maxDim=="number"&&o.maxDim>0?Math.floor(o.maxDim):1024}i(Ru,"_resolveMaxDim");function Nu(o){return o?.imageFormat==="jpeg"?"jpeg":"png"}i(Nu,"_resolveImageFormat");function _u(o){let e=o?.jpegQuality;return typeof e=="number"&&e>=50&&e<=100?Math.floor(e):90}i(_u,"_resolveJpegQuality");async function Mu(o,e){let t=Ru(e),n=Nu(e),r=_u(e),s=Eu(o).resize({width:t,height:t,fit:"inside",withoutEnlargement:!0}),a,u;return n==="png"?(a=await s.png().toBuffer(),u="image/png"):(a=await s.jpeg({quality:r}).toBuffer(),u="image/jpeg"),{bytes:a,mimeType:u}}i(Mu,"_preprocessImage");var ku=new Set(["amazon.titan-embed-text-v2:0"]),Du="amazon.titan-embed-text-v2:0",Au=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"]),Lu="anthropic.claude-3-sonnet-20240229-v1:0",Ht;function os(){return an&&!!Dt}i(os,"_isAwsBedrockActive");function rs(){if(Ht)return Ht;let o=Dt;if(!o)return;let e=sn;return e?(Ht=new es({region:o,credentials:vu({profile:e})}),Ht):(Ht=new es({region:o}),Ht)}i(rs,"_getOrCreateBedrockClient");async function is(o,e,t){let n=new Cu({modelId:e,contentType:"application/json",accept:"application/json",body:Buffer.from(JSON.stringify(t))}),r=await o.send(n),s=Buffer.from(r.body).toString("utf-8");return JSON.parse(s)}i(is,"_invokeBedrock");async function Bu(o,e,t,n){let{bytes:r,mimeType:s}=await Mu(o.image,e),u={anthropic_version:"bedrock-2023-05-31",max_tokens:1e4,temperature:0,messages:[{role:"user",content:[{type:"text",text:Pu(e)},{type:"image",source:{type:"base64",media_type:s,data:r.toString("base64")}}]}]},l=await is(t,n,u),m=l?.content?.[0]?.text??l?.output_text??l?.completion;if(!m||!m.trim())throw new Error("Amazon Bedrock Claude returned empty description.");return m.trim()}i(Bu,"_describeUIWithAmazonBedrockClaude");async function Fu(o,e,t){let s=(await is(e,t,{inputText:o}))?.embedding;if(!Array.isArray(s)||typeof s[0]!="number")throw new Error("Unexpected embedding response for Amazon Bedrock Titan text embedding.");return s}i(Fu,"_embedTextWithAmazonBedrockTitan");async function Hu(o,e,t){let n=e?.visionModelId??Gr??Lu;if(!Au.has(n))throw new Error(`Unsupported Amazon Bedrock vision model id: ${n}`);return await Bu(o,e,t,n)}i(Hu,"_describeUIWithAmazonBedrock");async function Wu(o,e,t){let n=e?.textEmbedModelId??$r??Du;if(!ku.has(n))throw new Error(`Unsupported Amazon Bedrock text embedding model id: ${n}`);return await Fu(o,t,n)}i(Wu,"_embedTextWithAmazonBedrock");async function ts(o,e){if(os()){let t=rs();return t?Hu(o,e,t):void 0}}i(ts,"_describeUI");async function ns(o,e){if(os()){let t=rs();return t?Wu(o,e,t):void 0}}i(ns,"_embedTextVector");async function ss(o,e,t){let n=typeof t?.normalize=="boolean"?t.normalize:!0,r=await ts(e,t);if(!r)return;let s=await ts(o,t);if(!s)return;let a=await ns(r,t);if(!a)return;let u=await ns(s,t);return u?{score:go(a,u,n)}:void 0}i(ss,"compare");var Uu=.25,qu=.5,Vu=.25;function ho(o){return Number.isFinite(o)?Math.max(0,Math.min(1,o)):0}i(ho,"_clamp01");function Er(o,e){return typeof o=="number"&&Number.isFinite(o)&&o>0?o:e}i(Er,"_weightOrDefault");async function as(o,e,t){let n=[],r=Er(t?.weights?.mssim,Uu),s=Er(t?.weights?.vectorEmbedding,qu),a=Er(t?.weights?.textEmbedding,Vu),u=await Ki(o,e,t?.mssim),l=ho(u.score);n.push(`mssim=${l.toFixed(5)}`);let m;try{let M=await Zi(o,e,t?.imageEmbedding);M&&typeof M.score=="number"?(m=ho(M.score),n.push(`image-embedding=${m.toFixed(5)}`)):n.push("image-embedding=skipped (inactive)")}catch(M){n.push(`image-embedding=skipped (${M instanceof Error?M.message:String(M)})`)}let c;try{let M=await ss(o,e,t?.textEmbedding);M&&typeof M.score=="number"?(c=ho(M.score),n.push(`text-embedding=${c.toFixed(5)}`)):n.push("text-embedding=skipped (inactive)")}catch(M){n.push(`text-embedding=skipped (${M instanceof Error?M.message:String(M)})`)}let p=[{name:"mssim",score:l,weight:r}];typeof m=="number"&&p.push({name:"image-embedding",score:m,weight:s}),typeof c=="number"&&p.push({name:"text-embedding",score:c,weight:a});let b=p.reduce((M,y)=>M+y.weight,0),h=b>0?p.reduce((M,y)=>M+y.score*(y.weight/b),0):0,C=ho(h);return n.push(`combined=${C.toFixed(5)} (signals=${p.map(M=>M.name).join(", ")})`),{score:C,notes:n}}i(as,"compareWithNotes");import zu from"node:crypto";function $u(){let o=jr;if(!o)throw new Error("No Figma access token configured");return o}i($u,"_requireFigmaToken");function Gu(o){return o==="jpg"?{mimeType:"image/jpeg",type:"jpeg"}:{mimeType:"image/png",type:"png"}}i(Gu,"_mimeTypeFor");function ju(o){let e=zu.createHash("sha256");return e.update(o.fileKey),e.update("|"),e.update(o.nodeId),e.update("|"),e.update(o.format??"png"),e.update("|"),e.update(String(o.scale??2)),e.digest("hex").slice(0,24)}i(ju,"_buildCacheKey");async function Ku(o,e){let t=await fetch(o,{method:"GET",headers:{"X-Figma-Token":e,Accept:"application/json"}}),n=await t.text(),r;try{r=n?JSON.parse(n):{}}catch{throw new Error(`Figma API returned non-JSON response (status=${t.status}). Body: ${n.slice(0,500)}`)}if(!t.ok){let s=typeof r?.err=="string"?r.err:`Figma API error (status=${t.status})`;throw new Error(s)}return r}i(Ku,"_fetchJson");async function Xu(o){let e=await fetch(o,{method:"GET"});if(!e.ok){let n=await e.text().catch(()=>"");throw new Error(`Failed to download Figma rendered image (status=${e.status}): ${n.slice(0,300)}`)}let t=await e.arrayBuffer();return Buffer.from(t)}i(Xu,"_fetchBinary");async function us(o){let e=$u(),t=o.format??"png",n=typeof o.scale=="number"&&o.scale>0?o.scale:2,{mimeType:r,type:s}=Gu(t),a=Kr,u=o.fileKey,l=o.nodeId,m=`${a}/images/${encodeURIComponent(u)}?ids=${encodeURIComponent(l)}&format=${encodeURIComponent(t)}&scale=${encodeURIComponent(String(n))}`,c=await Ku(m,e),p=c.images?.[l];if(!p){let M=typeof c.err=="string"&&c.err.trim()?c.err:"Figma did not return an image URL for the given nodeId.";throw new Error(M)}let b=await Xu(p),h=ju(o),C={image:b,mimeType:r,type:s,cacheKey:h};return o.includeId===!0&&(C.nodeId=l,C.fileKey=u),C}i(us,"getFigmaDesignScreenshot");import{z as me}from"zod";var ls="png",Yu=!0,cs="semantic",yo=class{static{i(this,"ComparePageWithDesign")}name(){return"figma_compare-page-with-design"}description(){return`
|
|
473
|
-
Compares the CURRENT PAGE UI against a Figma design snapshot and returns a combined similarity score.
|
|
474
|
-
|
|
475
|
-
What this tool does:
|
|
476
|
-
1) Fetches a raster snapshot from Figma (frame/node screenshot)
|
|
477
|
-
2) Takes a screenshot of the live browser page (full page or a specific selector)
|
|
478
|
-
3) Computes multiple similarity signals and combines them into one score:
|
|
479
|
-
- MSSIM (structural similarity; always available)
|
|
480
|
-
- Image embedding similarity (optional; may be skipped if provider is not configured)
|
|
481
|
-
- Vision\u2192text\u2192text embedding similarity (optional; may be skipped if provider is not configured)
|
|
482
|
-
|
|
483
|
-
How to use it effectively:
|
|
484
|
-
- Prefer 'semantic' MSSIM mode when comparing Figma sample data vs real data (less sensitive to text/value differences).
|
|
485
|
-
- Use 'raw' MSSIM mode only when you expect near pixel-identical output.
|
|
486
|
-
- If you suspect layout/structure mismatch, run with fullPage=true first, then retry with a selector for the problematic region.
|
|
487
|
-
- Notes explain which signals were used or skipped; skipped signals usually mean missing cloud configuration (e.g. AWS_REGION, inference profile, etc).
|
|
488
|
-
|
|
489
|
-
This tool is designed for UI regression checks, design parity checks, and "does this page still match the intended layout?" validation.
|
|
490
|
-
`.trim()}inputSchema(){return{figmaFileKey:me.string().min(1).describe("Figma file key (the part after /file/ in Figma URL)."),figmaNodeId:me.string().min(1).describe('Figma node id to render (frame/component node id like "12:34").'),selector:me.string().optional().describe("Optional CSS selector to compare only a specific region of the page."),fullPage:me.boolean().optional().default(Yu).describe("If true, captures the full scrollable page. Ignored when selector is provided."),figmaScale:me.number().int().positive().optional().describe("Optional scale factor for Figma raster export (e.g. 1, 2, 3)."),figmaFormat:me.enum(["png","jpg"]).optional().describe("Optional raster format for Figma snapshot."),weights:me.object({mssim:me.number().positive().optional().describe("Weight for MSSIM signal."),imageEmbedding:me.number().positive().optional().describe("Weight for image embedding signal."),textEmbedding:me.number().positive().optional().describe("Weight for vision\u2192text\u2192text embedding signal.")}).optional().describe("Optional weights to combine signals. Only active signals participate."),mssimMode:me.enum(["raw","semantic"]).optional().default(cs).describe("MSSIM mode. semantic is more robust for real-data vs design-data comparisons."),maxDim:me.number().int().positive().optional().describe("Optional preprocessing max dimension forwarded to compare pipeline."),jpegQuality:me.number().int().min(50).max(100).optional().describe("Optional JPEG quality forwarded to compare pipeline (used only when JPEG encoding is selected internally).")}}outputSchema(){return{score:me.number().describe("Combined similarity score in the range [0..1]. Higher means more similar."),notes:me.array(me.string()).describe("Human-readable notes explaining which signals were used and their individual scores."),meta:me.object({pageUrl:me.string().describe("URL of the page that was compared."),pageTitle:me.string().describe("Title of the page that was compared."),figmaFileKey:me.string().describe("Figma file key used for the design snapshot."),figmaNodeId:me.string().describe("Figma node id used for the design snapshot."),selector:me.string().nullable().describe("Selector used for page screenshot, if any. Null means full page."),fullPage:me.boolean().describe("Whether the page screenshot was full-page."),pageImageType:me.enum(["png","jpeg"]).describe("Image type of the captured page screenshot."),figmaImageType:me.enum(["png","jpeg"]).describe("Image type of the captured Figma snapshot.")}).describe("Metadata about what was compared.")}}async handle(e,t){let n=String(e.page.url()),r=String(await e.page.title()),s=t.figmaFormat??"png",a=typeof t.figmaScale=="number"?t.figmaScale:void 0,u=await us({fileKey:t.figmaFileKey,nodeId:t.figmaNodeId,format:s,scale:a}),l;if(typeof t.selector=="string"&&t.selector.trim()){let b=t.selector.trim(),h=e.page.locator(b);if(await h.count()===0)throw new Error(`Element not found for selector: ${b}`);l=await h.first().screenshot({type:ls})}else{let b=t.fullPage!==!1;l=await e.page.screenshot({type:ls,fullPage:b})}let m={image:l,type:"png",name:"page"},c={image:u.image,type:u.type==="jpeg"?"jpeg":"png",name:"figma"},p=await as(m,c,{weights:t.weights?{mssim:t.weights.mssim,vectorEmbedding:t.weights.imageEmbedding,textEmbedding:t.weights.textEmbedding}:void 0,mssim:{mode:t.mssimMode??cs},imageEmbedding:{maxDim:t.maxDim,jpegQuality:typeof t.jpegQuality=="number"?t.jpegQuality:void 0},textEmbedding:{maxDim:t.maxDim,jpegQuality:typeof t.jpegQuality=="number"?t.jpegQuality:void 0}});return{score:p.score,notes:p.notes,meta:{pageUrl:n,pageTitle:r,figmaFileKey:t.figmaFileKey,figmaNodeId:t.figmaNodeId,selector:typeof t.selector=="string"&&t.selector.trim()?t.selector.trim():null,fullPage:typeof t.selector=="string"&&t.selector.trim()?!1:t.fullPage!==!1,pageImageType:"png",figmaImageType:u.type}}}};var ms=[new yo];import{z as Ju}from"zod";var So=class{static{i(this,"Click")}name(){return"interaction_click"}description(){return"Clicks an element on the page."}inputSchema(){return{selector:Ju.string().describe("CSS selector for the element to click.")}}outputSchema(){return{}}async handle(e,t){return await(await e.page.waitForSelector(t.selector)).click(),{}}};import{z as ps}from"zod";var wo=class{static{i(this,"Drag")}name(){return"interaction_drag"}description(){return"Drags an element to a target location."}inputSchema(){return{sourceSelector:ps.string().describe("CSS selector for the element to drag."),targetSelector:ps.string().describe("CSS selector for the target location.")}}outputSchema(){return{}}async handle(e,t){let n=await e.page.waitForSelector(t.sourceSelector),r=await e.page.waitForSelector(t.targetSelector),s=await n.boundingBox(),a=await r.boundingBox();if(!s||!a)throw new Error("Could not get element positions for drag operation");return await e.page.mouse.move(s.x+s.width/2,s.y+s.height/2),await e.page.mouse.down(),await e.page.mouse.move(a.x+a.width/2,a.y+a.height/2),await e.page.mouse.up(),{}}};import{z as ds}from"zod";var To=class{static{i(this,"Fill")}name(){return"interaction_fill"}description(){return"Fills out an input field."}inputSchema(){return{selector:ds.string().describe("CSS selector for the input field."),value:ds.string().describe("Value to fill.")}}outputSchema(){return{}}async handle(e,t){return await(await e.page.waitForSelector(t.selector)).fill(t.value),{}}};import{z as Qu}from"zod";var xo=class{static{i(this,"Hover")}name(){return"interaction_hover"}description(){return"Hovers an element on the page."}inputSchema(){return{selector:Qu.string().describe("CSS selector for the element to hover.")}}outputSchema(){return{}}async handle(e,t){return await(await e.page.waitForSelector(t.selector)).hover(),{}}};import{z as on}from"zod";var bs=50,fs=10,Io=class{static{i(this,"PressKey")}name(){return"interaction_press-key"}description(){return`
|
|
491
|
-
Presses a keyboard key with optional "hold" and auto-repeat behavior.
|
|
492
|
-
|
|
493
|
-
Key facts:
|
|
494
|
-
- keyboard.press(key, { delay }) does NOT trigger OS-style auto-repeat.
|
|
495
|
-
- Some UI behaviors (especially scrolling) require repeated keydown events.
|
|
496
|
-
- Use repeat=true + holdMs to approximate real keyboard holding.
|
|
497
|
-
|
|
498
|
-
Execution logic:
|
|
499
|
-
- If selector is provided, the element is focused first.
|
|
500
|
-
- If holdMs is omitted or repeat=false:
|
|
501
|
-
\u2192 a single keyboard.press() is executed.
|
|
502
|
-
- If holdMs is provided AND repeat=true:
|
|
503
|
-
\u2192 keyboard.press() is called repeatedly until holdMs elapses.
|
|
504
|
-
`.trim()}inputSchema(){return{key:on.string().describe('Keyboard key to press (e.g. "Enter", "ArrowDown", "a").'),selector:on.string().describe("Optional CSS selector to focus before sending the key.").optional(),holdMs:on.number().int().min(0).describe("Optional duration in milliseconds to hold the key. With repeat=true, this is the total repeat duration.").optional(),repeat:on.boolean().optional().default(!1).describe("If true, simulates key auto-repeat by pressing the key repeatedly (useful for scrolling)."),repeatIntervalMs:on.number().int().min(fs).optional().default(bs).describe("Interval between repeated key presses in ms (only when repeat=true).")}}outputSchema(){return{}}async handle(e,t){t.selector&&await(await e.page.waitForSelector(t.selector)).focus();let n=t.holdMs??0,r=t.repeat===!0;if(n<=0||r===!1)return await e.page.keyboard.press(t.key,n>0?{delay:n}:void 0),{};let s=typeof t.repeatIntervalMs=="number"&&Number.isFinite(t.repeatIntervalMs)&&t.repeatIntervalMs>=fs?Math.floor(t.repeatIntervalMs):bs,a=Date.now();for(;Date.now()-a<n;)await e.page.keyboard.press(t.key),await e.page.waitForTimeout(s);return{}}};import{z as je}from"zod";var Zu=200,el=200,Co=class{static{i(this,"ResizeViewport")}name(){return"interaction_resize-viewport"}description(){return`
|
|
505
|
-
Resizes the PAGE VIEWPORT using Playwright viewport emulation (page.setViewportSize).
|
|
506
|
-
|
|
507
|
-
This affects:
|
|
508
|
-
- window.innerWidth / window.innerHeight
|
|
509
|
-
- CSS media queries (responsive layouts)
|
|
510
|
-
- Layout, rendering and screenshots
|
|
511
|
-
|
|
512
|
-
Notes:
|
|
513
|
-
- This does NOT resize the OS-level browser window.
|
|
514
|
-
- Runtime switching to viewport=null (binding to real window size) is not supported by Playwright.
|
|
515
|
-
If you need real window-driven responsive behavior, start the BrowserContext with viewport: null
|
|
516
|
-
and use the window resize tool instead.
|
|
517
|
-
`.trim()}inputSchema(){return{width:je.number().int().min(Zu).describe("Target viewport width in CSS pixels."),height:je.number().int().min(el).describe("Target viewport height in CSS pixels.")}}outputSchema(){return{requested:je.object({width:je.number().int().describe("Requested viewport width (CSS pixels)."),height:je.number().int().describe("Requested viewport height (CSS pixels).")}).describe("Requested viewport configuration."),viewport:je.object({innerWidth:je.number().int().describe("window.innerWidth after resize (CSS pixels)."),innerHeight:je.number().int().describe("window.innerHeight after resize (CSS pixels)."),outerWidth:je.number().int().describe("window.outerWidth after resize (CSS pixels)."),outerHeight:je.number().int().describe("window.outerHeight after resize (CSS pixels)."),devicePixelRatio:je.number().describe("window.devicePixelRatio after resize.")}).describe("Viewport metrics observed inside the page after resizing.")}}async handle(e,t){await e.page.setViewportSize({width:t.width,height:t.height});let n=await e.page.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio}));return{requested:{width:t.width,height:t.height},viewport:{innerWidth:Number(n.innerWidth),innerHeight:Number(n.innerHeight),outerWidth:Number(n.outerWidth),outerHeight:Number(n.outerHeight),devicePixelRatio:Number(n.devicePixelRatio)}}}};import{z as se}from"zod";var tl=200,nl=200,vo=class{static{i(this,"ResizeWindow")}name(){return"interaction_resize-window"}description(){return`
|
|
518
|
-
Resizes the REAL BROWSER WINDOW (OS-level window) for the current page using Chrome DevTools Protocol (CDP).
|
|
519
|
-
|
|
520
|
-
This tool works best on Chromium-based browsers (Chromium/Chrome/Edge).
|
|
521
|
-
It is especially useful in headful sessions when you run with viewport emulation disabled (viewport: null),
|
|
522
|
-
so the page layout follows the OS window size.
|
|
523
|
-
|
|
524
|
-
Important:
|
|
525
|
-
- If Playwright viewport emulation is enabled (viewport is NOT null), resizing the OS window may not change page layout.
|
|
526
|
-
- On non-Chromium browsers (Firefox/WebKit), CDP is not available and this tool will fail.
|
|
527
|
-
`.trim()}inputSchema(){return{width:se.number().int().min(tl).optional().describe('Target window width in pixels (required when state="normal").'),height:se.number().int().min(nl).optional().describe('Target window height in pixels (required when state="normal").'),state:se.enum(["normal","maximized","minimized","fullscreen"]).optional().default("normal").describe('Target window state. If not "normal", width/height may be ignored by the browser.')}}outputSchema(){return{requested:se.object({width:se.number().int().nullable().describe("Requested window width (pixels). Null if not provided."),height:se.number().int().nullable().describe("Requested window height (pixels). Null if not provided."),state:se.enum(["normal","maximized","minimized","fullscreen"]).describe("Requested window state.")}).describe("Requested window change parameters."),before:se.object({windowId:se.number().int().describe("CDP window id for the current target."),state:se.string().nullable().describe("Window state before resizing."),left:se.number().int().nullable().describe("Window left position before resizing."),top:se.number().int().nullable().describe("Window top position before resizing."),width:se.number().int().nullable().describe("Window width before resizing."),height:se.number().int().nullable().describe("Window height before resizing.")}).describe("Window bounds before resizing."),after:se.object({windowId:se.number().int().describe("CDP window id for the current target."),state:se.string().nullable().describe("Window state after resizing."),left:se.number().int().nullable().describe("Window left position after resizing."),top:se.number().int().nullable().describe("Window top position after resizing."),width:se.number().int().nullable().describe("Window width after resizing."),height:se.number().int().nullable().describe("Window height after resizing.")}).describe("Window bounds after resizing."),viewport:se.object({innerWidth:se.number().int().describe("window.innerWidth after resizing (CSS pixels)."),innerHeight:se.number().int().describe("window.innerHeight after resizing (CSS pixels)."),outerWidth:se.number().int().describe("window.outerWidth after resizing (CSS pixels)."),outerHeight:se.number().int().describe("window.outerHeight after resizing (CSS pixels)."),devicePixelRatio:se.number().describe("window.devicePixelRatio after resizing.")}).describe("Page viewport metrics after resizing (helps verify responsive behavior).")}}async handle(e,t){let n=t.state??"normal",r=t.width,s=t.height;if(n==="normal"&&(typeof r!="number"||typeof s!="number"))throw new Error('state="normal" requires both width and height.');let a=e.page,u=await a.context().newCDPSession(a);try{let l=await u.send("Browser.getWindowForTarget",{}),m=Number(l.windowId),c=l.bounds??{},p={windowId:m,state:typeof c.windowState=="string"?c.windowState:null,left:typeof c.left=="number"?c.left:null,top:typeof c.top=="number"?c.top:null,width:typeof c.width=="number"?c.width:null,height:typeof c.height=="number"?c.height:null},b={};n!=="normal"?b.windowState=n:(b.windowState="normal",b.width=r,b.height=s),await u.send("Browser.setWindowBounds",{windowId:m,bounds:b});let C=(await u.send("Browser.getWindowForTarget",{})).bounds??{},M={windowId:m,state:typeof C.windowState=="string"?C.windowState:null,left:typeof C.left=="number"?C.left:null,top:typeof C.top=="number"?C.top:null,width:typeof C.width=="number"?C.width:null,height:typeof C.height=="number"?C.height:null},y=await a.evaluate(()=>({innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight,devicePixelRatio:window.devicePixelRatio})),T={innerWidth:Number(y.innerWidth),innerHeight:Number(y.innerHeight),outerWidth:Number(y.outerWidth),outerHeight:Number(y.outerHeight),devicePixelRatio:Number(y.devicePixelRatio)};return{requested:{width:typeof r=="number"?r:null,height:typeof s=="number"?s:null,state:n},before:p,after:M,viewport:T}}catch(l){let m=String(l?.message??l);throw new Error(`Failed to resize real browser window via CDP. This tool works best on Chromium-based browsers. Original error: ${m}`)}finally{await u.detach().catch(()=>{})}}};import{z as gs}from"zod";var Eo=class{static{i(this,"Select")}name(){return"interaction_select"}description(){return"Select an element on the page with the given value"}inputSchema(){return{selector:gs.string().describe("CSS selector for element to select."),value:gs.string().describe("Value to select.")}}outputSchema(){return{}}async handle(e,t){return await(await e.page.waitForSelector(t.selector)).selectOption(t.value),{}}};import{z as ee}from"zod";var hs="auto",ys="by",Oo=class{static{i(this,"Scroll")}name(){return"interaction_scroll"}description(){return`
|
|
528
|
-
Scrolls the page viewport or a specific scrollable element.
|
|
529
|
-
|
|
530
|
-
Modes:
|
|
531
|
-
- 'by': Scrolls by a relative delta (dx/dy) from the current scroll position.
|
|
532
|
-
- 'to': Scrolls to an absolute scroll position (x/y).
|
|
533
|
-
- 'top': Scrolls to the very top.
|
|
534
|
-
- 'bottom': Scrolls to the very bottom.
|
|
535
|
-
- 'left': Scrolls to the far left.
|
|
536
|
-
- 'right': Scrolls to the far right.
|
|
537
|
-
|
|
538
|
-
Use this tool to:
|
|
539
|
-
- Reveal content below the fold
|
|
540
|
-
- Jump to the top/bottom without knowing exact positions
|
|
541
|
-
- Bring elements into view before clicking
|
|
542
|
-
- Inspect lazy-loaded content that appears on scroll
|
|
543
|
-
`.trim()}inputSchema(){return{mode:ee.enum(["by","to","top","bottom","left","right"]).optional().default(ys).describe('Scroll mode. "by" uses dx/dy, "to" uses x/y, and top/bottom/left/right jump to edges.'),selector:ee.string().optional().describe("Optional CSS selector for a scrollable container. If omitted, scrolls the document viewport."),dx:ee.number().optional().describe('Horizontal scroll delta in pixels (used when mode="by"). Default: 0.'),dy:ee.number().optional().describe('Vertical scroll delta in pixels (used when mode="by"). Default: 0.'),x:ee.number().optional().describe('Absolute horizontal scroll position in pixels (used when mode="to").'),y:ee.number().optional().describe('Absolute vertical scroll position in pixels (used when mode="to").'),behavior:ee.enum(["auto","smooth"]).optional().default(hs).describe('Native scroll behavior. Use "auto" for deterministic automation.')}}outputSchema(){return{mode:ee.enum(["by","to","top","bottom","left","right"]).describe("The scroll mode used."),selector:ee.string().nullable().describe("The selector of the scroll container if provided; otherwise null (document viewport)."),behavior:ee.enum(["auto","smooth"]).describe("The scroll behavior used."),before:ee.object({x:ee.number().describe("ScrollLeft before scrolling."),y:ee.number().describe("ScrollTop before scrolling."),scrollWidth:ee.number().describe("Total scrollable width before scrolling."),scrollHeight:ee.number().describe("Total scrollable height before scrolling."),clientWidth:ee.number().describe("Viewport/container client width before scrolling."),clientHeight:ee.number().describe("Viewport/container client height before scrolling.")}).describe("Scroll metrics before the scroll action."),after:ee.object({x:ee.number().describe("ScrollLeft after scrolling."),y:ee.number().describe("ScrollTop after scrolling."),scrollWidth:ee.number().describe("Total scrollable width after scrolling."),scrollHeight:ee.number().describe("Total scrollable height after scrolling."),clientWidth:ee.number().describe("Viewport/container client width after scrolling."),clientHeight:ee.number().describe("Viewport/container client height after scrolling.")}).describe("Scroll metrics after the scroll action."),canScrollX:ee.boolean().describe("Whether horizontal scrolling is possible (scrollWidth > clientWidth)."),canScrollY:ee.boolean().describe("Whether vertical scrolling is possible (scrollHeight > clientHeight)."),maxScrollX:ee.number().describe("Maximum horizontal scrollLeft (scrollWidth - clientWidth)."),maxScrollY:ee.number().describe("Maximum vertical scrollTop (scrollHeight - clientHeight)."),isAtLeft:ee.boolean().describe("Whether the scroll position is at the far left."),isAtRight:ee.boolean().describe("Whether the scroll position is at the far right."),isAtTop:ee.boolean().describe("Whether the scroll position is at the very top."),isAtBottom:ee.boolean().describe("Whether the scroll position is at the very bottom.")}}async handle(e,t){let n=t.mode??ys,r=t.selector,s=t.behavior??hs,a=t.dx??0,u=t.dy??0,l=t.x,m=t.y;if(n==="to"&&typeof l!="number"&&typeof m!="number")throw new Error('mode="to" requires at least one of x or y.');if(n==="by"&&a===0&&u===0)throw new Error('mode="by" requires dx and/or dy to be non-zero.');let c=await e.page.evaluate(({modeEval:p,selectorEval:b,dxEval:h,dyEval:C,xEval:M,yEval:y,behaviorEval:T})=>{let W=i(()=>{if(b){let le=document.querySelector(b);if(!le)throw new Error(`Element with selector "${b}" not found`);return le}let w=document.scrollingElement||document.documentElement||document.body;if(!w)throw new Error("No scrolling element available.");return w},"getTarget"),ie=i(w=>({x:w.scrollLeft,y:w.scrollTop,scrollWidth:w.scrollWidth,scrollHeight:w.scrollHeight,clientWidth:w.clientWidth,clientHeight:w.clientHeight}),"readMetrics"),Y=i((w,le,G)=>w<le?le:w>G?G:w,"clamp"),k=i(w=>{let le=Math.max(0,w.scrollWidth-w.clientWidth),G=Math.max(0,w.scrollHeight-w.clientHeight);if(p==="by"){let H=Y(w.scrollLeft+h,0,le),X=Y(w.scrollTop+C,0,G);w.scrollTo({left:H,top:X,behavior:T});return}if(p==="to"){let H=typeof M=="number"?Y(M,0,le):w.scrollLeft,X=typeof y=="number"?Y(y,0,G):w.scrollTop;w.scrollTo({left:H,top:X,behavior:T});return}if(p==="top"){w.scrollTo({top:0,left:w.scrollLeft,behavior:T});return}if(p==="bottom"){w.scrollTo({top:G,left:w.scrollLeft,behavior:T});return}if(p==="left"){w.scrollTo({left:0,top:w.scrollTop,behavior:T});return}if(p==="right"){w.scrollTo({left:le,top:w.scrollTop,behavior:T});return}},"doScroll"),B=W(),F=ie(B);k(B);let q=ie(B),z=Math.max(0,q.scrollWidth-q.clientWidth),Z=Math.max(0,q.scrollHeight-q.clientHeight),j=q.scrollWidth>q.clientWidth,U=q.scrollHeight>q.clientHeight,K=1,ue=q.x<=K,Te=q.x>=z-K,pe=q.y<=K,J=q.y>=Z-K;return{before:F,after:q,canScrollX:j,canScrollY:U,maxScrollX:z,maxScrollY:Z,isAtLeft:ue,isAtRight:Te,isAtTop:pe,isAtBottom:J}},{modeEval:n,selectorEval:r,dxEval:a,dyEval:u,xEval:l,yEval:m,behaviorEval:s});return{mode:n,selector:r??null,behavior:s,before:c.before,after:c.after,canScrollX:c.canScrollX,canScrollY:c.canScrollY,maxScrollX:c.maxScrollX,maxScrollY:c.maxScrollY,isAtLeft:c.isAtLeft,isAtRight:c.isAtRight,isAtTop:c.isAtTop,isAtBottom:c.isAtBottom}}};var Ss=[new So,new wo,new To,new xo,new Io,new Co,new vo,new Eo,new Oo];import{z as Wt}from"zod";var ol=0,rl="load",Po=class{static{i(this,"GoBack")}name(){return"navigation_go-back"}description(){return`
|
|
544
|
-
Navigates to the previous page in history.
|
|
545
|
-
In case of multiple redirects, the navigation will resolve with the response of the last redirect.
|
|
546
|
-
If cannot go back, returns empty response.
|
|
547
|
-
`}inputSchema(){return{timeout:Wt.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(ol),waitUntil:Wt.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(rl)}}outputSchema(){return{url:Wt.string().describe("Contains the URL of the navigated page.").optional(),status:Wt.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:Wt.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:Wt.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(e,t){let n=await e.page.goBack({timeout:t.timeout,waitUntil:t.waitUntil});return{url:n?.url(),status:n?.status(),statusText:n?.statusText(),ok:n?.ok()}}};import{z as Ut}from"zod";var il=0,sl="load",Ro=class{static{i(this,"GoForward")}name(){return"navigation_go-forward"}description(){return`
|
|
548
|
-
Navigates to the next page in history.
|
|
549
|
-
In case of multiple redirects, the navigation will resolve with the response of the last redirect.
|
|
550
|
-
If cannot go back, returns empty response.
|
|
551
|
-
`}inputSchema(){return{timeout:Ut.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(il),waitUntil:Ut.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(sl)}}outputSchema(){return{url:Ut.string().describe("Contains the URL of the navigated page.").optional(),status:Ut.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:Ut.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:Ut.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(e,t){let n=await e.page.goForward({timeout:t.timeout,waitUntil:t.waitUntil});return{url:n?.url(),status:n?.status(),statusText:n?.statusText(),ok:n?.ok()}}};import{z as Rt}from"zod";var al=0,ul="load",No=class{static{i(this,"GoTo")}name(){return"navigation_go-to"}description(){return`
|
|
552
|
-
Navigates to the given URL.
|
|
553
|
-
**NOTE**: The tool either throws an error or returns a main resource response.
|
|
554
|
-
The only exceptions are navigation to \`about:blank\` or navigation to the same URL with a different hash,
|
|
555
|
-
which would succeed and return empty response.
|
|
556
|
-
`}inputSchema(){return{url:Rt.string().describe("URL to navigate page to. The url should include scheme, e.g. `http://`, `https://`."),timeout:Rt.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(al),waitUntil:Rt.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(ul)}}outputSchema(){return{url:Rt.string().describe("Contains the URL of the navigated page.").optional(),status:Rt.number().int().positive().describe("Contains the status code of the navigated page (e.g., 200 for a success).").optional(),statusText:Rt.string().describe('Contains the status text of the navigated page (e.g. usually an "OK" for a success).').optional(),ok:Rt.boolean().describe("Contains a boolean stating whether the navigated page was successful (status in the range 200-299) or not.").optional()}}async handle(e,t){let n=await e.page.goto(t.url,{timeout:t.timeout,waitUntil:t.waitUntil});return{url:n?.url(),status:n?.status(),statusText:n?.statusText(),ok:n?.ok()}}};import{z as qt}from"zod";var ll=0,cl="load",_o=class{static{i(this,"Reload")}name(){return"navigation_reload"}description(){return`
|
|
557
|
-
Reloads the current page.
|
|
558
|
-
In case of multiple redirects, the navigation resolves with the response of the last redirect.
|
|
559
|
-
If the reload does not produce a response, returns empty response.
|
|
560
|
-
`}inputSchema(){return{timeout:qt.number().int().nonnegative().describe("Maximum operation time in milliseconds. Defaults to `0` - no timeout.").optional().default(ll),waitUntil:qt.enum(["load","domcontentloaded","networkidle","commit"]).describe("\nWhen to consider operation succeeded, defaults to `load`. Events can be either:\n- `domcontentloaded`: Consider operation to be finished when the `DOMContentLoaded` event is fired.\n- `load`: Consider operation to be finished when the `load` event is fired.\n- `networkidle`: **DISCOURAGED** consider operation to be finished when there are no network connections for\n at least `500` ms. Don't use this method for testing, rely on web assertions to assess readiness instead.\n- `commit`: Consider operation to be finished when network response is received and the document started loading.").optional().default(cl)}}outputSchema(){return{url:qt.string().describe("Contains the URL of the reloaded page.").optional(),status:qt.number().int().positive().describe("Contains the status code of the reloaded page (e.g., 200 for a success).").optional(),statusText:qt.string().describe('Contains the status text of the reloaded page (e.g. usually an "OK" for a success).').optional(),ok:qt.boolean().describe("Contains a boolean stating whether the reloaded page was successful (status in the range 200-299) or not.").optional()}}async handle(e,t){let n=await e.page.reload({timeout:t.timeout,waitUntil:t.waitUntil});return{url:n?.url(),status:n?.status(),statusText:n?.statusText(),ok:n?.ok()}}};var ws=[new Po,new Ro,new No,new _o];import{z as Ee}from"zod";var Mo=class{static{i(this,"GetConsoleMessages")}name(){return"o11y_get-console-messages"}description(){return"Retrieves console messages/logs from the browser with filtering options."}inputSchema(){return{type:Ee.enum(ve(Lt)).transform(dt(Lt)).describe(`
|
|
561
|
-
Type of console messages to retrieve.
|
|
562
|
-
When specified, console messages with equal or higher levels are retrieved.
|
|
563
|
-
Valid values are (in ascending order according to their levels): ${ve(Lt)}.`).optional(),search:Ee.string().describe("Text to search for in console messages.").optional(),timestamp:Ee.number().int().nonnegative().describe(`
|
|
564
|
-
Start time filter as a Unix epoch timestamp in milliseconds.
|
|
565
|
-
If provided, only console messages recorded at or after this timestamp will be returned.`).optional(),sequenceNumber:Ee.number().int().nonnegative().describe(`
|
|
566
|
-
Sequence number for incremental retrieval.
|
|
567
|
-
If provided, only console messages with a sequence number greater than this value will be returned.
|
|
568
|
-
This allows clients to fetch console messages incrementally by passing the last received sequence number on subsequent requests.`).optional(),limit:Ee.object({count:Ee.number().int().nonnegative().default(0).describe(`
|
|
569
|
-
Count of the maximum number of console messages to return.
|
|
570
|
-
If the result exceeds this limit, it will be truncated.
|
|
571
|
-
"0" means no count limit.`),from:Ee.enum(["start","end"]).default("end").describe(`
|
|
572
|
-
Controls which side is kept when truncation is applied.
|
|
573
|
-
"start" keeps the first N items (trims from the end).
|
|
574
|
-
"end" keeps the last N items (trims from the start).`)}).describe("Maximum number of console messages to return.").optional()}}outputSchema(){return{messages:Ee.array(Ee.object({type:Ee.string().describe("Type of the console message."),text:Ee.string().describe("Text of the console message."),location:Ee.object({url:Ee.string().describe("URL of the resource."),lineNumber:Ee.number().nonnegative().describe("0-based line number in the resource."),columnNumber:Ee.number().nonnegative().describe("0-based column number in the resource.")}).describe("Location of the console message in the resource.").optional(),timestamp:Ee.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the console message."),sequenceNumber:Ee.number().int().nonnegative().describe(`
|
|
575
|
-
A monotonically increasing sequence number assigned to each console message.
|
|
576
|
-
It reflects the order in which messages were captured and can be used by clients
|
|
577
|
-
to retrieve messages incrementally by requesting only those with a higher sequence
|
|
578
|
-
number than the last one received.`)}).describe("Console message item.")).describe("Retrieved console messages.")}}async handle(e,t){let n=t.type?fn[t.type]?.code:void 0,r=e.getConsoleMessages().filter(u=>{let l=!0;return n!==void 0&&(l=u.level.code>=n),l&&t.timestamp&&(l=u.timestamp>=t.timestamp),l&&t.sequenceNumber&&(l=u.sequenceNumber>t.sequenceNumber),l&&t.search&&(l=u.text.includes(t.search)),l});return{messages:(t.limit?.count?t.limit.from==="start"?r.slice(0,t.limit.count):r.slice(-t.limit.count):r).map(u=>({type:u.type,text:u.text,location:u.location?{url:u.location.url,lineNumber:u.location.lineNumber,columnNumber:u.location.columnNumber}:void 0,timestamp:u.timestamp,sequenceNumber:u.sequenceNumber}))}}};import{z as te}from"zod";var ko=class{static{i(this,"GetHttpRequests")}name(){return"o11y_get-http-requests"}description(){return"Retrieves HTTP requests from the browser with filtering options."}inputSchema(){return{resourceType:te.enum(ve(Ot)).transform(dt(Ot)).describe(`
|
|
579
|
-
Resource type of the HTTP requests to retrieve.
|
|
580
|
-
Valid values are: ${ve(Ot)}.`).optional(),status:te.object({min:te.number().int().positive().describe("Minimum status code of the HTTP requests to retrieve.").optional(),max:te.number().int().positive().describe("Maximum status code of the HTTP requests to retrieve.").optional()}).describe("Status code of the HTTP requests to retrieve.").optional(),ok:te.boolean().describe(`
|
|
581
|
-
Whether to retrieve successful or failed HTTP requests.
|
|
582
|
-
An HTTP request is considered successful only if its status code is 2XX.
|
|
583
|
-
Otherwise (non-2XX status code or no response at all because of timeout, network failure, etc ...) it is considered as failed.
|
|
584
|
-
When this flag is not set, all (successful and failed HTTP requests) are retrieved.`).optional(),timestamp:te.number().int().nonnegative().describe(`
|
|
585
|
-
Start time filter as a Unix epoch timestamp in milliseconds.
|
|
586
|
-
If provided, only HTTP requests recorded at or after this timestamp will be returned.
|
|
587
|
-
`).optional(),sequenceNumber:te.number().int().nonnegative().describe(`
|
|
588
|
-
Sequence number for incremental retrieval.
|
|
589
|
-
If provided, only HTTP requests with a sequence number greater than this value will be returned.
|
|
590
|
-
This allows clients to fetch HTTP requests incrementally by passing the last received sequence number on subsequent requests.
|
|
591
|
-
`).optional(),limit:te.object({count:te.number().int().nonnegative().default(0).describe(`
|
|
592
|
-
Count of the maximum number of HTTP requests to return.
|
|
593
|
-
If the result exceeds this limit, it will be truncated.
|
|
594
|
-
"0" means no count limit.`),from:te.enum(["start","end"]).default("end").describe(`
|
|
595
|
-
Controls which side is kept when truncation is applied.
|
|
596
|
-
"start" keeps the first N items (trims from the end).
|
|
597
|
-
"end" keeps the last N items (trims from the start).`)}).describe("Maximum number of HTTP requests to return.").optional()}}outputSchema(){return{requests:te.array(te.object({url:te.string().describe("HTTP request url."),method:te.enum(ve(gn)).describe(`HTTP request method. Valid values are: ${ve(gn)}`),headers:te.record(te.string(),te.string()).describe("HTTP request headers as key-value pairs."),body:te.string().describe("HTTP request body if available.").optional(),resourceType:te.enum(ve(Ot)).describe(`
|
|
598
|
-
HTTP request resource type as it was perceived by the rendering engine.
|
|
599
|
-
Valid values are: ${ve(Ot)}`),failure:te.string().describe("Error message of the HTTP request if failed.").optional(),duration:te.number().describe('HTTP request duration in milliseconds. "-1" if not available (no response).').optional(),response:te.object({status:te.number().int().positive().describe("HTTP response status code."),statusText:te.string().describe("HTTP response status text."),headers:te.record(te.string(),te.string()).describe("HTTP response headers as key-value pairs."),body:te.string().describe("HTTP response body if available.").optional()}).describe("HTTP response.").optional(),ok:te.boolean().describe(`
|
|
600
|
-
Flag to represent whether the HTTP request successful or failed.
|
|
601
|
-
An HTTP request is considered successful only if its status code is 2XX.
|
|
602
|
-
Otherwise (non-2XX status code or no response at all because of timeout, network failure, etc ...) it is considered as failed.`).optional(),timestamp:te.number().int().nonnegative().describe("Unix epoch timestamp (in milliseconds) of the HTTP request."),sequenceNumber:te.number().int().nonnegative().describe(`
|
|
603
|
-
A monotonically increasing sequence number assigned to each HTTP request.
|
|
604
|
-
It reflects the order in which requests were captured and can be used by clients
|
|
605
|
-
to retrieve requests incrementally by requesting only those with a higher sequence
|
|
606
|
-
number than the last one received.`)}).describe("HTTP request item.")).describe("Retrieved HTTP requests.")}}async handle(e,t){let n=e.getHttpRequests().filter(a=>{let u=!0;return u&&t.resourceType&&(u=a.resourceType===t.resourceType),u&&t.status&&(u&&t.status.min&&(u=a.response?a.response.status>=t.status.min:!1),u&&t.status.max&&(u=a.response?a.response.status<=t.status.max:!1)),u&&t.ok!==void 0&&(u=a.ok),u&&t.timestamp&&(u=a.timestamp>=t.timestamp),u&&t.sequenceNumber&&(u=a.sequenceNumber>t.sequenceNumber),u});return{requests:(t.limit?.count?t.limit.from==="start"?n.slice(0,t.limit.count):n.slice(-t.limit.count):n).map(a=>({url:a.url,method:a.method,headers:a.headers,body:a.body,resourceType:a.resourceType,failure:a.failure,duration:a.duration,response:a.response?{status:a.response.status,statusText:a.response.statusText,headers:a.response.headers,body:a.response.body}:void 0,ok:a.ok,timestamp:a.timestamp,sequenceNumber:a.sequenceNumber}))}}};import{z as ml}from"zod";var Do=class{static{i(this,"GetTraceId")}name(){return"o11y_get-trace-id"}description(){return"Gets the OpenTelemetry compatible trace id of the current session."}inputSchema(){return{}}outputSchema(){return{traceId:ml.string().describe("The OpenTelemetry compatible trace id of the current session if available.").optional()}}async handle(e,t){return{traceId:await e.getTraceId()}}};import{z as V}from"zod";var Ts=0,pl=3e4;function Ao(o,e,t){return typeof o!="number"||!Number.isFinite(o)?{rating:"not_available",value:null,unit:"ms",thresholds:{good:e,poor:t}}:o<=e?{rating:"good",value:o,unit:"ms",thresholds:{good:e,poor:t}}:o>t?{rating:"poor",value:o,unit:"ms",thresholds:{good:e,poor:t}}:{rating:"needs_improvement",value:o,unit:"ms",thresholds:{good:e,poor:t}}}i(Ao,"rateMs");function dl(o,e,t){return typeof o!="number"||!Number.isFinite(o)?{rating:"not_available",value:null,unit:"score",thresholds:{good:e,poor:t}}:o<=e?{rating:"good",value:o,unit:"score",thresholds:{good:e,poor:t}}:o>t?{rating:"poor",value:o,unit:"score",thresholds:{good:e,poor:t}}:{rating:"needs_improvement",value:o,unit:"score",thresholds:{good:e,poor:t}}}i(dl,"rateScore");function Or(o){return o==="needs_improvement"?"needs improvement":o==="not_available"?"not available":o}i(Or,"formatRating");function bl(o){let e=o.ratings.lcp.rating,t=o.ratings.inp.rating,n=o.ratings.cls.rating,r=o.ratings.ttfb.rating,s=o.ratings.fcp.rating,a=e==="good"&&t==="good"&&n==="good",u=[];a?u.push("Core Web Vitals look good (LCP, INP and CLS are all within recommended thresholds)."):u.push("Core Web Vitals need attention. Focus on the worst-rated metric first (LCP, INP, or CLS).");let l=[],m=[],c=[],p=[],b=[],h=[];return o.lcpSelectorHint&&h.push(`LCP element hint (best-effort): ${o.lcpSelectorHint}`),e==="poor"||e==="needs_improvement"?(l.push("Optimize the LCP element (often the hero image, headline, or main content above the fold)."),l.push("Reduce render-blocking resources (critical CSS, JS). Consider inlining critical CSS and deferring non-critical JS."),l.push('Preload the LCP resource (e.g., <link rel="preload"> for the hero image/font) and ensure it is discoverable without heavy JS.'),l.push("Improve server response and caching. A slow TTFB often delays LCP."),l.push("Avoid client-only rendering for above-the-fold content when possible; stream/SSR critical content.")):e==="good"?l.push("LCP is within the recommended threshold. Keep the above-the-fold path lean."):l.push("LCP is not available in this browser/session. Consider using Chromium or a page-load scenario that produces LCP entries."),t==="poor"||t==="needs_improvement"?(m.push("Break up long main-thread tasks. Aim to keep tasks under ~50ms (split work, yield to the event loop)."),m.push("Reduce expensive work in input handlers (click, pointer, key events). Move non-urgent work to idle time."),m.push("Avoid synchronous layout thrash during interactions (batch DOM reads/writes, reduce forced reflow)."),m.push("Defer heavy third-party scripts and reduce JavaScript bundle size to improve responsiveness.")):t==="good"?m.push("INP is within the recommended threshold. Keep interaction handlers lightweight."):m.push("INP is not available in this browser/session. It requires Event Timing support and user interactions."),n==="poor"||n==="needs_improvement"?(c.push("Reserve space for images/iframes/ads (set width/height or aspect-ratio) to prevent layout jumps."),c.push("Avoid inserting content above existing content unless it is in response to a user interaction."),c.push("Use stable font loading (font-display: swap/optional) and consider preloading critical fonts to reduce text shifts."),c.push("Be careful with late-loading banners/toasts; render them in reserved containers.")):n==="good"?c.push("CLS is within the recommended threshold. Keep layout stable during load and async updates."):c.push("CLS is not available in this browser/session. Consider Chromium or a scenario with visible layout changes."),r==="poor"||r==="needs_improvement"?(p.push("Improve backend latency: reduce server processing time, optimize DB queries, and eliminate unnecessary middleware."),p.push("Enable CDN/edge caching where possible. Use caching headers and avoid dynamic responses for static content."),p.push("Reduce cold-start and TLS overhead (keep-alive, warm pools, edge runtimes).")):r==="good"?p.push("TTFB is good. Backend/network latency is unlikely to be the primary bottleneck."):p.push("TTFB is not available in this browser/session."),s==="poor"||s==="needs_improvement"?(b.push("Reduce render-blocking CSS/JS and prioritize critical content for first paint."),b.push("Optimize above-the-fold resources and avoid large synchronous scripts during initial load."),b.push("Consider code-splitting and preloading critical assets to improve first paint.")):s==="good"?b.push("FCP is good. The page provides early visual feedback."):b.push("FCP is not available in this browser/session."),h.push("For reliable debugging, capture metrics after navigation and after user actions that trigger loading or layout changes."),h.push("If values look unstable, try adding waitMs (e.g., 1000-3000) and re-measure after the UI settles."),{coreWebVitalsPassed:a,summary:u,lcp:l,inp:m,cls:c,ttfb:p,fcp:b,general:h}}i(bl,"buildRecommendations");var Lo=class{static{i(this,"GetWebVitals")}name(){return"o11y_get-web-vitals"}description(){return`
|
|
607
|
-
Collects Web Vitals-style performance metrics and provides recommendations based on Google's thresholds.
|
|
608
|
-
|
|
609
|
-
Core Web Vitals:
|
|
610
|
-
- LCP (ms): Largest Contentful Paint (good <= 2500, poor > 4000)
|
|
611
|
-
- INP (ms): Interaction to Next Paint (good <= 200, poor > 500)
|
|
612
|
-
- CLS (score): Cumulative Layout Shift (good <= 0.1, poor > 0.25)
|
|
613
|
-
|
|
614
|
-
Supporting diagnostics:
|
|
615
|
-
- TTFB (ms): Time to First Byte (good <= 800, poor > 1800)
|
|
616
|
-
- FCP (ms): First Contentful Paint (good <= 1800, poor > 3000)
|
|
617
|
-
|
|
618
|
-
Guidance:
|
|
619
|
-
- Call after navigation and after user actions.
|
|
620
|
-
- If you need more stable LCP/CLS/INP, pass waitMs (e.g. 1000-3000).
|
|
621
|
-
- Some metrics may be unavailable depending on browser support and whether interactions occurred.
|
|
622
|
-
`.trim()}inputSchema(){return{waitMs:V.number().int().min(0).max(pl).optional().default(Ts).describe("Optional wait duration in milliseconds before reading metrics (default: 0)."),includeDebug:V.boolean().optional().default(!1).describe("If true, returns additional debug details such as entry counts and LCP element hint.")}}outputSchema(){let e=V.object({rating:V.enum(["good","needs_improvement","poor","not_available"]).describe("Rating based on Google thresholds."),value:V.number().nullable().describe("Metric value (null if unavailable)."),unit:V.enum(["ms","score"]).describe("Unit of the metric."),thresholds:V.object({good:V.number().describe('Upper bound for the "good" rating.'),poor:V.number().describe('Lower bound for the "poor" rating.')}).describe("Thresholds used for rating.")});return{url:V.string().describe("Current page URL."),title:V.string().describe("Current page title."),timestampMs:V.number().int().describe("Unix epoch timestamp (ms) when the metrics were captured."),metrics:V.object({lcpMs:V.number().nullable().describe("Largest Contentful Paint in milliseconds."),inpMs:V.number().nullable().describe("Interaction to Next Paint in milliseconds (best-effort approximation)."),cls:V.number().nullable().describe("Cumulative Layout Shift score."),ttfbMs:V.number().nullable().describe("Time to First Byte in milliseconds."),fcpMs:V.number().nullable().describe("First Contentful Paint in milliseconds.")}).describe("Raw metric values (null if unavailable)."),ratings:V.object({lcp:e.describe("LCP rating."),inp:e.describe("INP rating."),cls:e.describe("CLS rating."),ttfb:e.describe("TTFB rating."),fcp:e.describe("FCP rating.")}).describe("Ratings computed from Google thresholds."),recommendations:V.object({coreWebVitalsPassed:V.boolean().describe('True if all Core Web Vitals are rated "good".'),summary:V.array(V.string()).describe("High-level summary and prioritization guidance."),lcp:V.array(V.string()).describe("Recommendations for improving LCP."),inp:V.array(V.string()).describe("Recommendations for improving INP."),cls:V.array(V.string()).describe("Recommendations for improving CLS."),ttfb:V.array(V.string()).describe("Recommendations for improving TTFB."),fcp:V.array(V.string()).describe("Recommendations for improving FCP."),general:V.array(V.string()).describe("General measurement and debugging notes.")}).describe("Recommendations based on the measured values and their ratings."),notes:V.array(V.string()).describe("Notes about metric availability, browser limitations, and interpretation."),debug:V.object({waitMs:V.number().int().describe("Actual wait duration used before reading metrics."),entries:V.object({navigation:V.number().int().describe("Count of navigation entries."),paint:V.number().int().describe("Count of paint entries."),lcp:V.number().int().describe("Count of largest-contentful-paint entries."),layoutShift:V.number().int().describe("Count of layout-shift entries."),eventTiming:V.number().int().describe("Count of event timing entries.")}).describe("Counts of PerformanceEntry types used to compute metrics."),lastLcpSelectorHint:V.string().nullable().describe("Best-effort selector hint for the last LCP element (if available)."),lastLcpTagName:V.string().nullable().describe("Tag name of the last LCP element (if available).")}).optional().describe("Optional debug details.")}}async handle(e,t){let n=t.waitMs??Ts,r=t.includeDebug===!0,s=String(e.page.url()),a=String(await e.page.title()),u=Date.now(),l=await e.page.evaluate(async({waitMsEval:ie,includeDebugEval:Y})=>{let k=[],B=i(H=>new Promise(X=>{setTimeout(()=>X(),H)}),"sleep");ie>0&&await B(ie);let F=null,q=performance.getEntriesByType("navigation")??[];if(q.length>0){let H=q[0];typeof H.responseStart=="number"&&H.responseStart>=0&&(F=H.responseStart)}else k.push("TTFB: navigation entries not available (older browser or restricted timing).");let z=null,Z=performance.getEntriesByType("paint")??[],j=Z.find(H=>H.name==="first-contentful-paint");j&&typeof j.startTime=="number"?z=j.startTime:k.push("FCP: paint entries not available (browser may not support Paint Timing).");let U=null,K=null,ue=performance.getEntriesByType("largest-contentful-paint")??[];if(ue.length>0){let H=ue[ue.length-1];typeof H.startTime=="number"&&(U=H.startTime),H.element&&H.element instanceof Element&&(K=H.element)}else k.push("LCP: largest-contentful-paint entries not available (requires LCP support).");let Te=i(H=>{if(!H)return null;let X=H.getAttribute("data-testid")||H.getAttribute("data-test-id")||H.getAttribute("data-test");if(X&&X.trim())return'[data-testid="'+X.replace(/"/g,'\\"')+'"]';let Oe=H.getAttribute("data-selector");if(Oe&&Oe.trim())return'[data-selector="'+Oe.replace(/"/g,'\\"')+'"]';if(H.id)try{return"#"+CSS.escape(H.id)}catch{return"#"+String(H.id)}return H.tagName?H.tagName.toLowerCase():null},"selectorHintFor"),pe=null,J=performance.getEntriesByType("layout-shift")??[];if(J.length>0){let H=0;for(let X of J)X&&X.hadRecentInput===!0||typeof X.value=="number"&&(H+=X.value);pe=H}else k.push("CLS: layout-shift entries not available (requires Layout Instability API support).");let w=null,le=performance.getEntriesByType("event")??[];if(le.length>0){let H=0;for(let X of le){let Oe=typeof X.interactionId=="number"?X.interactionId:0,xe=typeof X.duration=="number"?X.duration:0;Oe>0&&xe>H&&(H=xe)}H>0?w=H:k.push("INP: event timing entries exist but no interactionId-based events were found.")}else k.push("INP: event timing entries not available (requires Event Timing API support).");k.length===0?k.push("All requested metrics were available."):k.push("Some metrics may be null due to browser support limitations.");let G={metrics:{ttfbMs:F,fcpMs:z,lcpMs:U,cls:pe,inpMs:w},notes:k,lcp:{selectorHint:Te(K),tagName:K?String(K.tagName).toLowerCase():null},debug:{waitMs:ie,entries:{navigation:q.length,paint:Z.length,lcp:ue.length,layoutShift:J.length,eventTiming:le.length}}};return Y?(G.debug.lastLcpSelectorHint=G.lcp.selectorHint,G.debug.lastLcpTagName=G.lcp.tagName):delete G.debug,G},{waitMsEval:n,includeDebugEval:r}),m=typeof l?.metrics?.lcpMs=="number"?l.metrics.lcpMs:null,c=typeof l?.metrics?.inpMs=="number"?l.metrics.inpMs:null,p=typeof l?.metrics?.cls=="number"?l.metrics.cls:null,b=typeof l?.metrics?.ttfbMs=="number"?l.metrics.ttfbMs:null,h=typeof l?.metrics?.fcpMs=="number"?l.metrics.fcpMs:null,C={lcp:Ao(m,2500,4e3),inp:Ao(c,200,500),cls:dl(p,.1,.25),ttfb:Ao(b,800,1800),fcp:Ao(h,1800,3e3)},M=typeof l?.lcp?.selectorHint=="string"?l.lcp.selectorHint:null,y=bl({ratings:C,lcpSelectorHint:M}),T=Array.isArray(l?.notes)?l.notes:[];T.push(`Ratings: LCP=${Or(C.lcp.rating)}, INP=${Or(C.inp.rating)}, CLS=${Or(C.cls.rating)}.`);let W={url:s,title:a,timestampMs:u,metrics:{lcpMs:m,inpMs:c,cls:p,ttfbMs:b,fcpMs:h},ratings:C,recommendations:y,notes:T};return r&&l?.debug&&(W.debug=l.debug),W}};import{z as fl}from"zod";var Bo=class{static{i(this,"NewTraceId")}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:fl.string().describe("The generated new OpenTelemetry compatible trace id.")}}async handle(e,t){let n=yn();return await e.setTraceId(n),{traceId:n}}};import{z as gl}from"zod";var Fo=class{static{i(this,"SetTraceId")}name(){return"o11y_set-trace-id"}description(){return"Sets the OpenTelemetry compatible trace id of the current session."}inputSchema(){return{traceId:gl.string().describe("The OpenTelemetry compatible trace id to be set.")}}outputSchema(){return{}}async handle(e,t){return await e.setTraceId(t.traceId),{}}};var xs=[new Mo,new ko,new Do,new Lo,new Bo,new Fo];import{z as L}from"zod";var Is=30,Cs=2e3,vs=!0,hl=25,yl=5e4,Ho=class{static{i(this,"GetComponentForElement")}name(){return"react_get-component-for-element"}description(){return`
|
|
623
|
-
Finds the React component(s) associated with a DOM element using React Fiber (best-effort).
|
|
624
|
-
|
|
625
|
-
How it works:
|
|
626
|
-
- Resolve a DOM element by CSS selector OR by (x,y) using elementFromPoint()
|
|
627
|
-
- Attempt to locate React Fiber pointers on that element (e.g. __reactFiber$*)
|
|
628
|
-
- If not present, walk up ancestor elements to find the nearest React-owned host node
|
|
629
|
-
|
|
630
|
-
Key correctness fix:
|
|
631
|
-
- If fiber is found on an ANCESTOR element, we scan that fiber subtree to locate the EXACT host fiber
|
|
632
|
-
where fiber.stateNode === target DOM element, then build the component stack from that host fiber.
|
|
633
|
-
This reduces unrelated components appearing in the returned stack.
|
|
634
|
-
|
|
635
|
-
What to expect (important for AI debugging):
|
|
636
|
-
- React Fiber is not a public API; results are best-effort and can differ by build (dev/prod).
|
|
637
|
-
- Component names may come from displayName, wrappers, third-party libraries, or minified production builds.
|
|
638
|
-
- wrappersDetected/wrapperFrames help interpret memo/forwardRef/context boundaries that can otherwise look confusing.
|
|
639
|
-
- If hostMapping.strategy is "ancestor-fallback", the stack may include unrelated frames; try a more specific selector
|
|
640
|
-
or target a deeper DOM node to improve mapping accuracy.
|
|
641
|
-
`.trim()}inputSchema(){return{selector:L.string().optional().describe("CSS selector for the target element. If provided, takes precedence over x/y."),x:L.number().int().optional().describe("Viewport X coordinate in CSS pixels. Used when selector is not provided."),y:L.number().int().optional().describe("Viewport Y coordinate in CSS pixels. Used when selector is not provided."),maxStackDepth:L.number().int().positive().optional().default(Is).describe("Maximum number of component frames to return in the component stack."),includePropsPreview:L.boolean().optional().default(vs).describe("If true, includes a best-effort, truncated props preview for the nearest component."),maxPropsPreviewChars:L.number().int().positive().optional().default(Cs).describe("Maximum characters for props preview (after safe stringification).")}}outputSchema(){return{target:L.object({selector:L.string().nullable(),point:L.object({x:L.number().int().nullable(),y:L.number().int().nullable()}),found:L.boolean(),domHint:L.string().nullable(),elementPath:L.string().nullable()}),react:L.object({detected:L.boolean(),detectionReason:L.string(),fiberKey:L.string().nullable(),hostMapping:L.object({fiberOnTargetElement:L.boolean().describe("Whether the initial fiber pointer was found directly on the target element."),anchorDomHint:L.string().nullable().describe("DOM hint of the element where fiber pointer was found (target or ancestor)."),targetDomHint:L.string().nullable().describe("DOM hint of the actual target element."),hostFiberMatchedTarget:L.boolean().describe("Whether we found the exact host fiber for target element (fiber.stateNode === targetEl) via subtree scan."),subtreeScanNodesScanned:L.number().int().describe("Number of fiber nodes scanned during subtree search."),subtreeScanMaxNodes:L.number().int().describe("Maximum fiber nodes allowed to scan (safety cap)."),strategy:L.enum(["direct-on-target","ancestor-subtree-scan","ancestor-fallback"]).describe("Mapping strategy used to produce the final stack.")}),nearestComponent:L.object({name:L.string().nullable(),displayName:L.string().nullable(),kind:L.enum(["function","class","unknown"]),debugSource:L.object({fileName:L.string().optional(),lineNumber:L.number().int().optional(),columnNumber:L.number().int().optional()}).optional(),propsPreview:L.string().optional()}).nullable(),componentStack:L.array(L.object({name:L.string().nullable(),displayName:L.string().nullable(),kind:L.enum(["function","class","unknown"]),debugSource:L.object({fileName:L.string().optional(),lineNumber:L.number().int().optional(),columnNumber:L.number().int().optional()}).optional()})),componentStackText:L.string(),wrappersDetected:L.array(L.string()),wrapperFrames:L.array(L.object({wrapper:L.string(),frameIndex:L.number().int(),frameLabel:L.string()})),notes:L.array(L.string())})}}async handle(e,t){let n=typeof t.selector=="string"&&t.selector.trim()?t.selector.trim():void 0,r=typeof t.x=="number"&&Number.isFinite(t.x)?Math.floor(t.x):void 0,s=typeof t.y=="number"&&Number.isFinite(t.y)?Math.floor(t.y):void 0;if(!n&&(typeof r!="number"||typeof s!="number"))throw new Error("Provide either selector, or both x and y for elementFromPoint.");let a=typeof t.maxStackDepth=="number"&&t.maxStackDepth>0?Math.floor(t.maxStackDepth):Is,u=t.includePropsPreview===void 0?vs:t.includePropsPreview===!0,l=typeof t.maxPropsPreviewChars=="number"&&Number.isFinite(t.maxPropsPreviewChars)&&t.maxPropsPreviewChars>0?Math.floor(t.maxPropsPreviewChars):Cs;return await e.page.evaluate(({selectorEval:c,xEval:p,yEval:b,maxStackDepthEval:h,includePropsPreviewEval:C,maxPropsPreviewCharsEval:M,maxElementPathDepthEval:y,maxFiberNodesToScanEval:T})=>{let W=[];function ie(g){if(!g)return null;let E=g.tagName.toLowerCase(),v=g.id&&g.id.trim()?"#"+g.id.trim():"",N=typeof g.className=="string"?g.className:"",D=N?"."+N.trim().split(/\s+/).slice(0,4).join("."):"";return E+v+D}i(ie,"domHint");function Y(g,E){if(!g)return null;let v=[],N=g,D=0;for(;N&&D<E;){let A=N.tagName?N.tagName.toLowerCase():"unknown",Q=N.id&&N.id.trim()?"#"+N.id.trim():"",re=typeof N.className=="string"?N.className:"",ce=re?re.trim().split(/\s+/).filter(Boolean).slice(0,3):[],Ce=ce.length>0?"."+ce.join("."):"",Ve="";try{let xt=N.parentElement;if(xt){let $t=Array.from(xt.children).filter(It=>It.tagName===N.tagName);if($t.length>1){let It=$t.indexOf(N)+1;It>0&&(Ve=`:nth-of-type(${It})`)}}}catch{}v.push(`${A}${Q}${Ce}${Ve}`),N=N.parentElement,D++}return v.reverse(),v.join(" > ")}i(Y,"buildElementPath");function k(g){if(!g)return null;let E=Object.getOwnPropertyNames(g);for(let v of E)if(v.startsWith("__reactFiber$")||v.startsWith("__reactInternalInstance$"))return v;return null}i(k,"findFiberKeyOn");function B(g){if(!g)return null;let E=k(g);if(E){let N=g[E];if(N)return{fiber:N,fiberKey:E,onTarget:!0,anchorEl:g}}let v=g;for(;v;){let N=k(v);if(N){let D=v[N];if(D)return{fiber:D,fiberKey:N,onTarget:!1,anchorEl:v}}v=v.parentElement}return null}i(B,"findFiberForElement");function F(g,E,v){if(!g)return{hostFiber:null,scanned:0,found:!1};if(g.stateNode===E)return{hostFiber:g,scanned:1,found:!0};let N=[],D=new Set;N.push(g),D.add(g);let A=0;for(;N.length>0;){let Q=N.shift();if(A++,Q&&Q.stateNode===E)return{hostFiber:Q,scanned:A,found:!0};if(A>=v)return{hostFiber:null,scanned:A,found:!1};let re=Q?Q.child:null;re&&!D.has(re)&&(D.add(re),N.push(re));let ce=Q?Q.sibling:null;ce&&!D.has(ce)&&(D.add(ce),N.push(ce))}return{hostFiber:null,scanned:A,found:!1}}i(F,"findHostFiberForDomElement");function q(g){if(!g)return!1;let E=g.prototype;return!!(E&&E.isReactComponent)}i(q,"isClassComponentType");function z(g){if(!g)return null;if(typeof g=="function"){let E=g.displayName;return typeof E=="string"&&E.trim()?E.trim():typeof g.name=="string"&&g.name.trim()?g.name.trim():"Anonymous"}if(typeof g=="object"){let E=g.displayName;if(typeof E=="string"&&E.trim())return E.trim();let v=g.render;if(typeof v=="function"){let N=v.displayName;return typeof N=="string"&&N.trim()?N.trim():typeof v.name=="string"&&v.name.trim()?v.name.trim():"Anonymous"}}return null}i(z,"typeDisplayName");function Z(g){if(!g)return null;if(typeof g=="function")return typeof g.name=="string"&&g.name.trim()?g.name.trim():null;if(typeof g=="object"){let E=g.render;return typeof E=="function"&&typeof E.name=="string"&&E.name.trim()?E.name.trim():null}return null}i(Z,"typeName");function j(g){if(!g)return g;if(typeof g=="object"){let E=g.render;if(typeof E=="function")return E}return g}i(j,"unwrapType");function U(g){return g?typeof j(g.type??g.elementType)=="function":!1}i(U,"isMeaningfulComponentFiber");function K(g){if(!g)return"unknown";let E=j(g.type??g.elementType);return typeof E=="function"?q(E)?"class":"function":"unknown"}i(K,"inferKind");function ue(g){let E=g?g._debugSource:void 0;if(!E||typeof E!="object")return;let v={};return typeof E.fileName=="string"&&(v.fileName=E.fileName),typeof E.lineNumber=="number"&&(v.lineNumber=E.lineNumber),typeof E.columnNumber=="number"&&(v.columnNumber=E.columnNumber),Object.keys(v).length>0?v:void 0}i(ue,"getDebugSource");function Te(g,E){let v=new WeakSet;function N(A,Q){if(A===null)return null;let re=typeof A;if(re==="string")return A.length>500?A.slice(0,500)+"\u2026":A;if(re==="number"||re==="boolean")return A;if(re==="bigint")return String(A);if(re!=="undefined"){if(re==="function")return"[function]";if(re==="symbol")return String(A);if(re==="object"){if(A instanceof Element)return"[Element "+(A.tagName?A.tagName.toLowerCase():"")+"]";if(A instanceof Window)return"[Window]";if(A instanceof Document)return"[Document]";if(A instanceof Date)return A.toISOString();if(v.has(A))return"[circular]";if(v.add(A),Array.isArray(A))return Q<=0?"[array len="+A.length+"]":A.slice(0,20).map(Ve=>N(Ve,Q-1));if(Q<=0)return"[object]";let ce={},Ce=Object.keys(A).slice(0,40);for(let Ve of Ce)try{ce[Ve]=N(A[Ve],Q-1)}catch{ce[Ve]="[unreadable]"}return ce}return String(A)}}i(N,"helper");let D="";try{D=JSON.stringify(N(g,2))}catch{try{D=String(g)}catch{D="[unserializable]"}}return D.length>E?D.slice(0,E)+"\u2026(truncated)":D}i(Te,"safeStringify");function pe(g){return g.map(v=>{let N=v?.displayName,D=v?.name;return typeof N=="string"&&N.trim()?N.trim():typeof D=="string"&&D.trim()?D.trim():"Anonymous"}).filter(v=>!!v).reverse().join(" > ")}i(pe,"stackTextFromFrames");function J(g){let E=j(g.type??g.elementType),v=Z(E),N=z(E),D=K(g),A=ue(g),Q={name:v,displayName:N,kind:D};return A&&(Q.debugSource=A),Q}i(J,"frameFromFiber");function w(g){let E=g.displayName?g.displayName:"",v=g.name?g.name:"",N=g.debugSource,D=N&&typeof N=="object"?`${String(N.fileName??"")}:${String(N.lineNumber??"")}:${String(N.columnNumber??"")}`:"";return`${E}|${v}|${g.kind}|${D}`}i(w,"makeFrameKey");function le(g){let E=[],v=null;for(let N of g){let D=w(N);v&&D===v||(E.push(N),v=D)}return E}i(le,"collapseConsecutiveDuplicates");function G(g){let E=new Set;function v(D){let A=D.trim().toLowerCase();A&&E.add(A)}i(v,"canonicalAdd");for(let D of g){let A=typeof D.displayName=="string"&&D.displayName.trim()?D.displayName.trim():typeof D.name=="string"&&D.name.trim()?D.name.trim():"";if(!A)continue;let Q=A.toLowerCase();/^memo(\(|$)/i.test(A)&&v("memo"),Q.includes("forwardref")&&v("forwardref"),Q.includes(".provider")&&v("context-provider"),Q.includes(".consumer")&&v("context-consumer"),Q.includes("suspense")&&v("suspense"),Q.includes("fragment")&&v("fragment"),Q.includes("strictmode")&&v("strictmode"),Q.includes("profiler")&&v("profiler")}let N=Array.from(E);return N.sort((D,A)=>D<A?-1:D>A?1:0),N}i(G,"detectWrappers");function H(g,E){let v=[],N=new Set(E.map(D=>D.toLowerCase()));for(let D=0;D<g.length;D++){let A=g[D],Q=A.displayName&&A.displayName.trim()?A.displayName.trim():A.name&&A.name.trim()?A.name.trim():"Anonymous",re=Q.toLowerCase();for(let ce of N){let Ce=!1;ce==="memo"?Ce=/^memo(\(|$)/i.test(Q):ce==="forwardref"?Ce=re.includes("forwardref"):ce==="context-provider"?Ce=re.includes(".provider"):ce==="context-consumer"?Ce=re.includes(".consumer"):ce==="suspense"?Ce=re.includes("suspense"):ce==="fragment"?Ce=re.includes("fragment"):ce==="strictmode"?Ce=re.includes("strictmode"):ce==="profiler"&&(Ce=re.includes("profiler")),Ce&&v.push({wrapper:ce,frameIndex:D,frameLabel:Q})}}return v}i(H,"findWrapperFrames");let X=null;if(c&&c.trim())try{X=document.querySelector(c)}catch{X=null}else typeof p=="number"&&typeof b=="number"&&(X=document.elementFromPoint(p,b));let Oe=!!X,xe=ie(X),qe=Y(X,y);if(!Oe)return{target:{selector:c??null,point:{x:typeof p=="number"?p:null,y:typeof b=="number"?b: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:T,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes:["No DOM element could be resolved; cannot inspect React."]}};let Ie=B(X);if(!Ie)return W.push("No React fiber pointer found on the element or its ancestors."),W.push("This can happen if the page is not React, uses a different renderer, or runs with hardened/minified internals."),{target:{selector:c??null,point:{x:typeof p=="number"?p:null,y:typeof b=="number"?b:null},found:!0,domHint:xe,elementPath:qe},react:{detected:!1,detectionReason:"React fiber not found on element/ancestors.",fiberKey:null,hostMapping:{fiberOnTargetElement:!1,anchorDomHint:null,targetDomHint:xe,hostFiberMatchedTarget:!1,subtreeScanNodesScanned:0,subtreeScanMaxNodes:T,strategy:"ancestor-fallback"},nearestComponent:null,componentStack:[],componentStackText:"",wrappersDetected:[],wrapperFrames:[],notes:W}};let ne=Ie.fiber,Pe=Ie.onTarget,Ne=0,lt=ie(Ie.anchorEl),Ye="direct-on-target";if(!Ie.onTarget){Ye="ancestor-fallback";let g=F(Ie.fiber,X,T);Ne=g.scanned,g.found&&g.hostFiber?(ne=g.hostFiber,Pe=!0,Ye="ancestor-subtree-scan",W.push(`Mapped target DOM element to host fiber by scanning fiber subtree (scanned=${Ne}).`)):(Pe=!1,W.push(`Could not find exact host fiber for the target element in the ancestor fiber subtree (scanned=${Ne}). Falling back to ancestor fiber stack; some frames may be unrelated.`))}let Je={fiberOnTargetElement:Ie.onTarget,anchorDomHint:lt,targetDomHint:xe,hostFiberMatchedTarget:Pe,subtreeScanNodesScanned:Ne,subtreeScanMaxNodes:T,strategy:Ye},it=null,St=ne;for(;St;){if(U(St)){it=St;break}St=St.return??null}it||W.push("Fiber was found, but no meaningful function/class component was detected in the return chain.");let Nt=[],zt=new Set,st=it;for(;st&&Nt.length<h;){if(zt.has(st)){W.push("Detected a cycle in fiber.return chain; stopping stack traversal.");break}zt.add(st),U(st)&&Nt.push(J(st)),st=st.return??null}let _t=Nt.length>0?le(Nt):[],Zo=pe(_t),Mt=G(_t),er=H(_t,Mt);Mt.length>=3&&W.push(`Wrapper-heavy stack detected (${Mt.join(", ")}). Interpreting nearestComponent may require skipping wrappers.`);let wt=null;if(it&&(wt=J(it),C))try{let g=it.memoizedProps;g!==void 0?wt.propsPreview=Te(g,M):W.push("memoizedProps not available on nearest component fiber.")}catch{W.push("Failed to read memoizedProps for nearest component (best-effort).")}return W.push("React Fiber inspection uses non-public internals; fields are best-effort."),W.push("Component names may come from displayName, wrappers, third-party libraries, or minified production builds."),Ye==="ancestor-fallback"&&W.push("Host mapping fallback was used; consider selecting a deeper/more specific DOM element for a more accurate component stack."),{target:{selector:c??null,point:{x:typeof p=="number"?p:null,y:typeof b=="number"?b:null},found:!0,domHint:xe,elementPath:qe},react:{detected:!0,detectionReason:Ye==="direct-on-target"?"React fiber found on the target element.":Ye==="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:Ie.fiberKey,hostMapping:Je,nearestComponent:wt,componentStack:_t,componentStackText:Zo,wrappersDetected:Mt,wrapperFrames:er,notes:W}}},{selectorEval:n??null,xEval:typeof r=="number"?r:null,yEval:typeof s=="number"?s:null,maxStackDepthEval:a,includePropsPreviewEval:u,maxPropsPreviewCharsEval:l,maxElementPathDepthEval:hl,maxFiberNodesToScanEval:yl})}};import{z as P}from"zod";var Es="contains",Os=200,Ps=!0,Rs=!0,Ns=80,_s=5,Sl=50,wl=20,Tl=25e4,Wo=class{static{i(this,"GetElementForComponent")}name(){return"react_get-element-for-component"}description(){return`
|
|
642
|
-
Maps a React COMPONENT INSTANCE to the DOM elements it renders (its "DOM footprint") by traversing the React Fiber graph.
|
|
643
|
-
|
|
644
|
-
Selection strategy:
|
|
645
|
-
- Best: provide an anchor (anchorSelector OR anchorX/anchorY) to target the correct instance near that UI.
|
|
646
|
-
- Optionally provide a query (componentName and/or fileNameHint/lineNumber) to search the Fiber graph.
|
|
647
|
-
- If both anchor + query are provided, we rank candidates and pick the best match near the anchor.
|
|
648
|
-
|
|
649
|
-
Important behavior:
|
|
650
|
-
- The React DevTools hook is helpful but NOT strictly required.
|
|
651
|
-
- If hook exists, roots are discovered reliably via getFiberRoots().
|
|
652
|
-
- If hook doesn't exist, we fall back to scanning the DOM for __reactFiber$ pointers (best-effort).
|
|
653
|
-
- Query search returns multiple candidates (up to maxMatches) and ranks them.
|
|
654
|
-
- Debug source information is best-effort and may be missing even in dev builds depending on bundler/build flags.
|
|
655
|
-
|
|
656
|
-
Operational note for MCP users:
|
|
657
|
-
- If you are using a persistent/headful browser and want more reliable root discovery and component search,
|
|
658
|
-
install the "React Developer Tools" Chrome extension in that browser profile (manual step by the user).
|
|
659
|
-
`.trim()}inputSchema(){return{anchorSelector:P.string().optional().describe("DOM CSS selector used as an anchor to pick a concrete component instance near that element."),anchorX:P.number().int().nonnegative().optional().describe("Anchor X coordinate (viewport pixels)."),anchorY:P.number().int().nonnegative().optional().describe("Anchor Y coordinate (viewport pixels)."),componentName:P.string().optional().describe("React component name/displayName to search in the Fiber graph."),matchStrategy:P.enum(["exact","contains"]).optional().default(Es).describe("How to match componentName against Fiber type/displayName."),fileNameHint:P.string().optional().describe('Best-effort debug source file hint (usually endsWith match), e.g., "UserCard.tsx".'),lineNumber:P.number().int().positive().optional().describe("Optional debug source line number hint (dev builds only)."),maxElements:P.number().int().positive().optional().default(Os).describe("Maximum number of DOM elements to return from the selected component subtree."),onlyVisible:P.boolean().optional().default(Ps).describe("If true, only visually visible elements are returned."),onlyInViewport:P.boolean().optional().default(Rs).describe("If true, only elements intersecting the viewport are returned."),textPreviewMaxLength:P.number().int().positive().optional().default(Ns).describe("Max length for per-element text preview (innerText/textContent/aria-label)."),maxMatches:P.number().int().positive().optional().default(_s).describe("Max number of matching component candidates to return/rank.")}}outputSchema(){return{reactDetected:P.boolean().describe("True if __REACT_DEVTOOLS_GLOBAL_HOOK__ looks available."),fiberDetected:P.boolean().describe("True if DOM appears to contain React Fiber pointers (__reactFiber$...)."),rootDiscovery:P.enum(["devtools-hook","dom-fiber-scan","none"]).describe("How roots were discovered."),component:P.object({name:P.string().nullable(),displayName:P.string().nullable(),debugSource:P.object({fileName:P.string().nullable(),lineNumber:P.number().int().nullable(),columnNumber:P.number().int().nullable()}).nullable(),componentStack:P.array(P.string()),selection:P.enum(["anchor","query","query+anchor","unknown"]),scoring:P.object({score:P.number(),nameMatched:P.boolean(),debugSourceMatched:P.boolean(),anchorRelated:P.boolean(),proximityPx:P.number().nullable().optional()}).optional()}).nullable(),candidates:P.array(P.object({name:P.string().nullable(),displayName:P.string().nullable(),debugSource:P.object({fileName:P.string().nullable(),lineNumber:P.number().int().nullable(),columnNumber:P.number().int().nullable()}).nullable(),componentStack:P.array(P.string()),selection:P.enum(["anchor","query","query+anchor","unknown"]),scoring:P.object({score:P.number(),nameMatched:P.boolean(),debugSourceMatched:P.boolean(),anchorRelated:P.boolean(),proximityPx:P.number().nullable().optional()}).optional(),domFootprintCount:P.number().int()})).describe("Ranked candidate matches (best-first)."),elements:P.array(P.object({tagName:P.string(),id:P.string().nullable(),className:P.string().nullable(),selectorHint:P.string().nullable(),textPreview:P.string().nullable(),boundingBox:P.object({x:P.number(),y:P.number(),width:P.number(),height:P.number()}).nullable(),isVisible:P.boolean(),isInViewport:P.boolean()})),notes:P.array(P.string())}}async handle(e,t){let n=t.anchorSelector,r=t.anchorX,s=t.anchorY,a=t.componentName,u=t.matchStrategy??Es,l=t.fileNameHint,m=t.lineNumber,c=t.maxElements??Os,p=t.onlyVisible??Ps,b=t.onlyInViewport??Rs,h=t.textPreviewMaxLength??Ns,C=t.maxMatches??_s,M=typeof n=="string"&&n.trim().length>0,y=typeof r=="number"&&typeof s=="number",T=typeof a=="string"&&a.trim().length>0||typeof l=="string"&&l.trim().length>0||typeof m=="number";if(!M&&!y&&!T)throw new Error("Provide at least one targeting method: anchorSelector, (anchorX+anchorY), or componentName/debugSource hints.");return await e.page.evaluate(({anchorSelectorEval:ie,anchorXEval:Y,anchorYEval:k,componentNameEval:B,matchStrategyEval:F,fileNameHintEval:q,lineNumberEval:z,maxElementsEval:Z,onlyVisibleEval:j,onlyInViewportEval:U,textPreviewMaxLengthEval:K,maxMatchesEval:ue,maxRootsScan:Te,maxFibersVisited:pe,stackLimit:J})=>{let w=[];function le(f){return typeof Element<"u"&&f instanceof Element}i(le,"isElement");function G(f){if(typeof f!="string")return null;let d=f.trim();return d||null}i(G,"normalizeStr");function H(f){let d=f,I=Object.keys(d);for(let O of I)if(O.startsWith("__reactFiber$")){let _=d[O];if(_)return _}for(let O of I)if(O.startsWith("__reactInternalInstance$")){let _=d[O];if(_)return _}return null}i(H,"getFiberFromDomElement");function X(){let d=globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;return!d||typeof d.getFiberRoots!="function"?null:d}i(X,"getDevtoolsHook");function Oe(f){let d=[],I=f.renderers;return!I||typeof I.forEach!="function"?d:(I.forEach((O,_)=>{try{let $=f.getFiberRoots(_);$&&typeof $.forEach=="function"&&$.forEach(oe=>{oe&&d.push(oe)})}catch{}}),d.slice(0,Te))}i(Oe,"getAllRootsFromHook");function xe(f){let d=[],I=Array.from(document.querySelectorAll("*")),O=I.length>5e3?Math.ceil(I.length/5e3):1;for(let _=0;_<I.length;_+=O){let $=I[_],oe=H($);if(oe&&(d.push(oe),d.length>=f))break}return d}i(xe,"getSeedFibersFromDomScan");function qe(f){if(typeof f!="function")return null;let d=f,I=G(d.displayName);if(I)return I;let O=G(d.name);return O||null}i(qe,"getFunctionDisplayName");function Ie(f){if(!f)return null;let d=f.type??f.elementType??null;if(!d)return null;if(typeof d=="function")return qe(d);if(typeof d=="string")return d;let I=G(d.displayName);return I||G(d.name)}i(Ie,"getFiberTypeName");function ne(f){if(!f)return null;let d=f.type??f.elementType??null;if(!d)return null;let I=G(d.displayName);return I||Ie(f)}i(ne,"getDisplayName");function Pe(f){let d=new Set,I=[],O=i(_=>{_&&(d.has(_)||(d.add(_),I.push(_)))},"push");O(f),O(f?.alternate);for(let _=0;_<8;_++){let $=I[_];if(!$)break;O($?._debugOwner),O($?._debugOwner?.alternate)}for(let _ of I){let $=_?._debugSource??_?._debugOwner?._debugSource??_?.type?._debugSource??_?.elementType?._debugSource??null;if(!$)continue;let oe=G($.fileName)??null,ge=typeof $.lineNumber=="number"?$.lineNumber:null,Le=typeof $.columnNumber=="number"?$.columnNumber:null;if(oe||ge!==null||Le!==null)return{fileName:oe,lineNumber:ge,columnNumber:Le}}return null}i(Pe,"getDebugSource");function Ne(f){let d=f?.stateNode;return le(d)}i(Ne,"isHostFiber");function lt(f,d){let I=[],O=f??null;for(let $=0;$<d&&O;$++){let oe=ne(O),ge=Ne(O);oe&&!ge&&I.push(oe),O=O.return??null}if(I.length===0){O=f??null;for(let $=0;$<d&&O;$++){let oe=ne(O);oe&&I.push(oe),O=O.return??null}}let _=[];for(let $ of I)(_.length===0||_[_.length-1]!==$)&&_.push($);return _}i(lt,"buildComponentStack");function Ye(f){let d=f??null,I=new Set;for(;d;){let O=d.alternate??d;if(I.has(O))break;if(I.add(O),!Ne(d))return d;d=d.return??null}return null}i(Ye,"firstMeaningfulComponentAbove");function Je(f,d,I){if(!f)return!1;let O=f.trim(),_=d.trim();return!O||!_?!1:I==="exact"?O===_:O.toLowerCase().includes(_.toLowerCase())}i(Je,"matchesName");function it(f,d,I){let O=Pe(f);if(!O)return!1;if(d){let _=d.trim();if(_){let $=(O.fileName??"").trim();if(!$)return!1;let oe=$.toLowerCase(),ge=_.toLowerCase();if(!(oe.endsWith(ge)||oe.includes(ge)))return!1}}return!(typeof I=="number"&&(O.lineNumber===null||Math.abs(O.lineNumber-I)>3))}i(it,"matchesDebugSource");function St(f){if(!f)return null;let d=f.current;return d?d.child??d:f}i(St,"toStartFiber");function Nt(f,d,I){if(f&&f.trim()){let O=document.querySelector(f);return O||(w.push(`anchorSelector did not match any element: ${f}`),null)}if(typeof d=="number"&&typeof I=="number"){let O=document.elementFromPoint(d,I);return O||(w.push(`anchorPoint did not hit any element: (${d}, ${I})`),null)}return null}i(Nt,"pickAnchorElement");function zt(f){let d=G(f.getAttribute("data-testid"))??G(f.getAttribute("data-test-id"))??G(f.getAttribute("data-test"))??null;if(d)return`[data-testid='${d.replace(/'/g,"\\'")}']`;let I=G(f.getAttribute("data-selector"))??null;if(I)return`[data-selector='${I.replace(/'/g,"\\'")}']`;if(f.id)try{return`#${CSS.escape(f.id)}`}catch{return`#${f.id}`}return f.tagName.toLowerCase()}i(zt,"selectorHintFor");function st(f,d){let I=G(f.getAttribute("aria-label"))??null;if(I)return I.slice(0,d);let O=String(f.innerText??f.textContent??"").trim();return O?O.slice(0,d):null}i(st,"textPreviewFor");function _t(f){let d=f.getBoundingClientRect(),I=getComputedStyle(f),O=I.display!=="none"&&I.visibility!=="hidden"&&parseFloat(I.opacity||"1")>0&&d.width>0&&d.height>0,_=window.innerWidth,$=window.innerHeight,oe=d.right>0&&d.bottom>0&&d.left<_&&d.top<$,ge=Number.isFinite(d.x)&&Number.isFinite(d.y)&&Number.isFinite(d.width)&&Number.isFinite(d.height)?{x:d.x,y:d.y,width:d.width,height:d.height}:null,Le=d.left+d.width/2,he=d.top+d.height/2;return{boundingBox:ge,isVisible:O,isInViewport:oe,centerX:Le,centerY:he}}i(_t,"computeRuntime");function Zo(f,d,I,O,_){let $=[],oe=new Set,ge=[];f&&ge.push(f);let Le=new Set;for(;ge.length>0&&!($.length>=d);){let he=ge.pop();if(!he)continue;let Qe=he.alternate??he;if(Le.has(Qe))continue;if(Le.add(Qe),Ne(he)){let de=he.stateNode;if(!oe.has(de)){oe.add(de);let _e=_t(de);if(!(I&&!_e.isVisible)){if(!(O&&!_e.isInViewport)){let Ze=de.tagName.toLowerCase(),vt=de.id?String(de.id):null,ze=de.className,et=typeof ze=="string"&&ze.trim()?ze.trim():null;$.push({tagName:Ze,id:vt,className:et,selectorHint:zt(de),textPreview:st(de,_),boundingBox:_e.boundingBox,isVisible:_e.isVisible,isInViewport:_e.isInViewport})}}}}let ct=he.child??null,Ct=he.sibling??null;ct&&ge.push(ct),Ct&&ge.push(Ct)}return $}i(Zo,"collectDomElementsFromFiberSubtree");function Mt(f,d,I){let O=d.componentName?d.componentName.trim():void 0,_=!!O,$=d.fileNameHint?d.fileNameHint.trim():void 0,oe=!!$,ge=typeof d.lineNumber=="number";if(!(_||oe||ge))return[];let he=[];for(let de of f){let _e=St(de);_e&&he.push(_e)}let Qe=new Set,ct=[],Ct=0;for(;he.length>0&&!(ct.length>=I);){let de=he.pop();if(!de)continue;let _e=de.alternate??de;if(Qe.has(_e))continue;if(Qe.add(_e),Ct++,Ct>pe){w.push(`Fiber traversal safety cap reached (${pe}). Results may be incomplete.`);break}let Ze=ne(de),vt=Ie(de),ze=_?Je(Ze,O,d.matchStrategy)||Je(vt,O,d.matchStrategy):!0,et=oe||ge?it(de,$,d.lineNumber):!0;ze&&et&&ct.push(de);let Et=de.child??null,Me=de.sibling??null;Et&&he.push(Et),Me&&he.push(Me)}return ct}i(Mt,"findFibersByQuery");function er(f,d,I,O,_,$,oe,ge,Le){let he=_.componentName?_.componentName.trim():void 0,Qe=!!he,ct=ne(f),Ct=Ie(f),de=Qe?Je(ct,he,_.matchStrategy)||Je(Ct,he,_.matchStrategy):!1,_e=G(_.fileNameHint)||typeof _.lineNumber=="number"?it(f,G(_.fileNameHint)??void 0,typeof _.lineNumber=="number"?_.lineNumber:void 0):!1,Ze=Zo(f,$,oe,ge,Le),vt=!1,ze=null;if(d){let Et=zt(d);if(vt=Ze.some(Me=>!Me.selectorHint||!Et?!1:Me.selectorHint===Et)||Ze.some(Me=>{if(!Me.boundingBox)return!1;let Fe=d.getBoundingClientRect(),mt=Me.boundingBox;return Fe.left<mt.x+mt.width&&Fe.left+Fe.width>mt.x&&Fe.top<mt.y+mt.height&&Fe.top+Fe.height>mt.y}),typeof I=="number"&&typeof O=="number"&&Ze.length>0){let Me=null;for(let Fe of Ze){if(!Fe.boundingBox)continue;let mt=Fe.boundingBox.x+Fe.boundingBox.width/2,Nr=Fe.boundingBox.y+Fe.boundingBox.height/2,_r=mt-I,Mr=Nr-O,nr=Math.sqrt(_r*_r+Mr*Mr);(Me===null||nr<Me)&&(Me=nr)}ze=Me}}let et=0;if(et+=de?3:0,et+=_e?3:0,et+=vt?2:0,et+=Math.min(1.5,Ze.length/25),typeof ze=="number"&&Number.isFinite(ze)){let Et=Math.max(0,Math.min(1,1-ze/1e3));et+=1.5*Et}return{score:et,nameMatched:de,debugSourceMatched:_e,anchorRelated:vt,proximityPx:ze,dom:Ze}}i(er,"scoreCandidate");let wt=X(),Tt=!!wt;Tt?w.push("React DevTools hook detected. Root discovery will use getFiberRoots() (more reliable)."):(w.push("React DevTools hook was not detected (__REACT_DEVTOOLS_GLOBAL_HOOK__)."),w.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 g=(()=>{let f=document.body;return f?!!(H(f)||xe(1).length>0):!1})();g?w.push("React Fiber pointers detected on DOM nodes. Anchor-based mapping may still work without the DevTools hook (best-effort)."):w.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 E=Nt(ie,Y,k),v=null;if(E){let f=H(E);if(f){let d=f,I=f.alternate??null,O=d;I&&(I.child||I.sibling)&&!(d.child||d.sibling)&&(O=I);let _=Ye(O);_?v=_:w.push("Anchor fiber found but no meaningful component fiber was found above it.")}else w.push("Anchor element found but React fiber was not found on it.")}let N=[],D="none";if(wt){let f=Oe(wt);f.length>0&&(N=f,D="devtools-hook")}if(N.length===0&&g){let f=xe(10);f.length>0&&(N=f,D="dom-fiber-scan",w.push("Using DOM fiber scan as fallback root discovery (best-effort)."))}let A=!!G(B)||!!G(q)||typeof z=="number",Q={componentName:G(B)??void 0,matchStrategy:F,fileNameHint:G(q)??void 0,lineNumber:typeof z=="number"?z:void 0},re=[];A&&(N.length>0?re=Mt(N,Q,Math.max(1,Math.floor(ue))):w.push("Query was provided but no roots could be discovered. Provide an anchorSelector/anchorX+anchorY so we can map via DOM fiber pointers."));let ce=[];v&&ce.push(v);for(let f of re)ce.push(f);let Ce=[],Ve=new Set;for(let f of ce){let d=f?.alternate??f;f&&!Ve.has(d)&&(Ve.add(d),Ce.push(f))}if(Ce.length===0)return Tt||w.push("No candidates found. Without the DevTools hook, component search may be unreliable unless you provide a strong anchor."),{reactDetected:Tt,fiberDetected:g,rootDiscovery:D,component:null,candidates:[],elements:[],notes:[...w,"Failed to select a target component fiber. Provide a better anchor, or ensure React is present and render has happened."]};let xt=[];for(let f of Ce){let d=er(f,E,Y,k,Q,Z,j,U,K),I="unknown",O=v?(v.alternate??v)===(f.alternate??f):!1,_=re.some(Qe=>(Qe.alternate??Qe)===(f.alternate??f));O&&_?I="query+anchor":O?I="anchor":_?I="query":I="unknown";let $=Ie(f),oe=ne(f),ge=Pe(f),Le=lt(f,J),he={name:$,displayName:oe,debugSource:ge,componentStack:Le,selection:I,scoring:{score:d.score,nameMatched:d.nameMatched,debugSourceMatched:d.debugSourceMatched,anchorRelated:d.anchorRelated,proximityPx:d.proximityPx}};xt.push({fiber:f,meta:he,dom:d.dom,domFootprintCount:d.dom.length})}xt.sort((f,d)=>(d.meta.scoring?.score??0)-(f.meta.scoring?.score??0));let $t=xt[0],It=xt.slice(0,Math.max(1,Math.floor(ue))).map(f=>({...f.meta,domFootprintCount:f.domFootprintCount})),tr=$t.dom;return A&&(Q.fileNameHint||typeof Q.lineNumber=="number")&&(It.some(d=>!!(d.debugSource?.fileName||d.debugSource?.lineNumber!==null))||(w.push("debugSource hints were provided, but no _debugSource information was found on matched fibers. This is common in production builds and some dev toolchains."),w.push('React DevTools may still display "Rendered by <file>:<line>" using sourcemaps/stack traces even when fiber._debugSource is null.'))),tr.length>=Z&&w.push(`Element list was truncated at maxElements=${Z}. Increase maxElements if needed.`),tr.length===0&&w.push("No DOM elements were returned for the selected component. It may render no host elements, or filtering (onlyVisible/onlyInViewport) removed them."),!Tt&&g?w.push("React DevTools hook was not detected, but DOM fiber pointers were found. Using DOM-fiber scanning and anchor-based mapping (best-effort)."):!Tt&&!g&&w.push("React DevTools hook was not detected and no DOM fiber pointers were found. Component-to-DOM mapping is unavailable on this page."),{reactDetected:Tt,fiberDetected:g,rootDiscovery:D,component:$t.meta,candidates:It,elements:tr,notes:[...w,"Component metadata is best-effort. Wrappers (memo/forwardRef/HOCs), Suspense/Offscreen, and portals may make names/stacks noisy."]}},{anchorSelectorEval:typeof n=="string"&&n.trim()?n.trim():void 0,anchorXEval:typeof r=="number"&&Number.isFinite(r)?Math.floor(r):void 0,anchorYEval:typeof s=="number"&&Number.isFinite(s)?Math.floor(s):void 0,componentNameEval:typeof a=="string"&&a.trim()?a.trim():void 0,matchStrategyEval:u,fileNameHintEval:typeof l=="string"&&l.trim()?l.trim():void 0,lineNumberEval:typeof m=="number"&&Number.isFinite(m)?Math.floor(m):void 0,maxElementsEval:Math.max(1,Math.floor(c)),onlyVisibleEval:!!p,onlyInViewportEval:!!b,textPreviewMaxLengthEval:Math.max(1,Math.floor(h)),maxMatchesEval:Math.max(1,Math.floor(C)),maxRootsScan:wl,maxFibersVisited:Tl,stackLimit:Sl})}};var Ms=[new Ho,new Wo];import{z as ks}from"zod";var Uo=class{static{i(this,"JsInBrowser")}name(){return"run_js-in-browser"}description(){return`
|
|
660
|
-
Runs custom JavaScript INSIDE the active browser page using Playwright's "page.evaluate()".
|
|
661
|
-
|
|
662
|
-
This code executes in the PAGE CONTEXT (real browser environment):
|
|
663
|
-
- Has access to window, document, DOM, Web APIs
|
|
664
|
-
- Can read/modify the page state
|
|
665
|
-
- Runs with the same permissions as the loaded web page
|
|
666
|
-
|
|
667
|
-
Typical use cases:
|
|
668
|
-
- Inspect or mutate DOM state
|
|
669
|
-
- Read client-side variables or framework internals
|
|
670
|
-
- Trigger browser-side logic
|
|
671
|
-
- Extract computed values directly from the page
|
|
672
|
-
|
|
673
|
-
Notes:
|
|
674
|
-
- The code runs in an isolated execution context, but within the page
|
|
675
|
-
- No direct access to Node.js APIs
|
|
676
|
-
- Return value must be serializable
|
|
677
|
-
`}inputSchema(){return{script:ks.string().describe("JavaScript code to execute.")}}outputSchema(){return{result:ks.any().describe(`
|
|
678
|
-
The result of the evaluation. This value can be of any type, including primitives, arrays, or objects.
|
|
679
|
-
It represents the direct return value of the JavaScript expression executed in the page context.
|
|
680
|
-
The structure and type of this value are not constrained and depend entirely on the evaluated code.`)}}async handle(e,t){return{result:await e.page.evaluate(t.script)}}};import Ds from"node:vm";import xl from"node:crypto";import{z as Pr}from"zod";var As=5e3,Il=3e4;function Cl(o){if(o==null||typeof o=="string"||typeof o=="number"||typeof o=="boolean")return o;try{return JSON.parse(JSON.stringify(o))}catch{return String(o)}}i(Cl,"toJsonSafe");var qo=class{static{i(this,"JsInSandbox")}name(){return"run_js-in-sandbox"}description(){return`
|
|
681
|
-
Runs custom JavaScript inside a Node.js VM sandbox.
|
|
682
|
-
|
|
683
|
-
This runs on the MCP SERVER (not in the browser).
|
|
684
|
-
|
|
685
|
-
Available bindings:
|
|
686
|
-
- page: Playwright Page (main interaction surface)
|
|
687
|
-
- console: captured logs (log/warn/error)
|
|
688
|
-
- sleep(ms): async helper
|
|
689
|
-
|
|
690
|
-
Safe built-ins:
|
|
691
|
-
- Math, JSON, Number, String, Boolean, Array, Object, Date, RegExp
|
|
692
|
-
- isFinite, isNaN, parseInt, parseFloat
|
|
693
|
-
- URL, URLSearchParams
|
|
694
|
-
- TextEncoder, TextDecoder
|
|
695
|
-
- structuredClone
|
|
696
|
-
- crypto.randomUUID()
|
|
697
|
-
- AbortController
|
|
698
|
-
- setTimeout / clearTimeout
|
|
699
|
-
|
|
700
|
-
NOT available:
|
|
701
|
-
- require, process, fs, Buffer
|
|
702
|
-
- globalThis
|
|
703
|
-
|
|
704
|
-
This is NOT a security boundary. Intended for trusted automation logic.
|
|
705
|
-
`.trim()}inputSchema(){return{code:Pr.string().describe("JavaScript code (async allowed)."),timeoutMs:Pr.number().int().min(0).max(Il).optional().default(As).describe("Max VM CPU time for synchronous execution in milliseconds.")}}outputSchema(){return{result:Pr.any().describe(`
|
|
706
|
-
Return value of the sandboxed code (best-effort JSON-safe).
|
|
707
|
-
If user returns undefined but logs exist, returns { logs }.
|
|
708
|
-
If error occurs, returns { error, logs }.`)}}async handle(e,t){let n=[],r={log:i((...m)=>{n.push({level:"log",message:m.map(c=>String(c)).join(" ")})},"log"),warn:i((...m)=>{n.push({level:"warn",message:m.map(c=>String(c)).join(" ")})},"warn"),error:i((...m)=>{n.push({level:"error",message:m.map(c=>String(c)).join(" ")})},"error")},s=i(async m=>{let c=Math.max(0,Math.floor(m));await new Promise(p=>{setTimeout(()=>p(),c)})},"sleep"),a={page:e.page,console:r,sleep:s,Math,JSON,Number,String,Boolean,Array,Object,Date,RegExp,isFinite,isNaN,parseInt,parseFloat,URL,URLSearchParams,TextEncoder,TextDecoder,structuredClone,crypto:{randomUUID:xl.randomUUID},AbortController,setTimeout,clearTimeout},u=Ds.createContext(a),l=`
|
|
709
|
-
'use strict';
|
|
710
|
-
(async () => {
|
|
711
|
-
${String(t.code??"")}
|
|
712
|
-
})()
|
|
713
|
-
`.trim();try{let c=new Ds.Script(l,{filename:"mcp-sandbox.js"}).runInContext(u,{timeout:t.timeoutMs??As}),p=await Promise.resolve(c);return p===void 0&&n.length>0?{result:{logs:n}}:{result:Cl(p)}}catch(m){return{result:{error:m instanceof Error?m.stack??m.message:String(m),logs:n}}}}};var Ls=[new Uo,new qo];import vl from"picomatch";var Bs=new WeakMap;function Vt(o){let e=Bs.get(o);if(e)return e;let t={stubs:[],installed:!1};return Bs.set(o,t),t}i(Vt,"_ensureStore");function Fs(){let o=Date.now(),e=Math.floor(Math.random()*1e6);return`${o.toString(36)}-${e.toString(36)}`}i(Fs,"_nowId");async function El(o){o<=0||await new Promise(e=>{setTimeout(()=>e(),o)})}i(El,"_sleep");function Rr(o){return typeof o!="number"||!Number.isFinite(o)||o<=0?-1:Math.floor(o)}i(Rr,"_normalizeTimes");function Hs(o,e){return o===-1?!0:e<o}i(Hs,"_isTimesRemaining");function Ws(o){if(typeof o=="number"&&Number.isFinite(o))return Math.max(0,Math.min(1,o))}i(Ws,"_normalizeChance");function Ol(o){return o===void 0?!0:o<=0?!1:o>=1?!0:Math.random()<o}i(Ol,"_shouldApplyChance");function Us(o){let e=o.trim();return e?vl(e,{dot:!0,nocase:!1}):()=>!1}i(Us,"_compileMatcher");function Pl(o,e){for(let t of o)if(t.enabled&&Hs(t.times,t.usedCount)&&t.matcher(e)&&!(t.kind==="mock_http_response"&&!Ol(t.chance)))return t}i(Pl,"_pickStub");async function Rl(o,e){let t=o.request();if(e.usedCount++,e.delayMs>0&&await El(e.delayMs),e.kind==="mock_http_response"){if(e.action==="abort"){let a=e.abortErrorCode??"failed";await o.abort(a);return}let n=typeof e.status=="number"?e.status:200,r=e.headers??{},s=typeof e.body=="string"?e.body:"";await o.fulfill({status:n,headers:r,body:s});return}else if(e.kind==="intercept_http_request"){let r={headers:{...t.headers(),...e.modifications.headers??{}}};typeof e.modifications.method=="string"&&e.modifications.method.trim()&&(r.method=e.modifications.method.trim().toUpperCase()),typeof e.modifications.body=="string"&&(r.postData=e.modifications.body),await o.continue(r);return}await o.continue()}i(Rl,"_applyStub");async function Vo(o){let e=Vt(o);e.installed||(await o.route("**/*",async t=>{let n=t.request().url(),r=Vt(o),s=Pl(r.stubs,n);if(!s){await t.continue();return}try{await Rl(t,s)}finally{Hs(s.times,s.usedCount)||(r.stubs=r.stubs.filter(a=>a.id!==s.id))}}),e.installed=!0)}i(Vo,"ensureRoutingInstalled");function qs(o,e){let t=Vt(o),n={...e,kind:"mock_http_response",id:Fs(),usedCount:0,matcher:Us(e.pattern),times:Rr(e.times),delayMs:Math.max(0,Math.floor(e.delayMs)),chance:Ws(e.chance)};return t.stubs.push(n),n}i(qs,"addMockHttpResponseStub");function Vs(o,e){let t=Vt(o),n={...e,kind:"intercept_http_request",id:Fs(),usedCount:0,matcher:Us(e.pattern),times:Rr(e.times),delayMs:Math.max(0,Math.floor(e.delayMs))};return t.stubs.push(n),n}i(Vs,"addHttpInterceptRequestStub");function zs(o,e){let t=Vt(o);if(!e){let r=t.stubs.length;return t.stubs=[],r}let n=t.stubs.length;return t.stubs=t.stubs.filter(r=>r.id!==e),n-t.stubs.length}i(zs,"clearStub");function $s(o){return[...Vt(o).stubs]}i($s,"listStubs");function zo(o){return typeof o!="number"||!Number.isFinite(o)||o<=0?0:Math.floor(o)}i(zo,"normalizeDelayMs");function $o(o){return Rr(o)}i($o,"normalizeTimesPublic");function Go(o){if(!o)return;let e={};for(let[t,n]of Object.entries(o))e[String(t)]=String(n);return e}i(Go,"normalizeHeaders");function jo(o){if(typeof o=="string")return o;if(o&&typeof o=="object")return JSON.stringify(o)}i(jo,"normalizeBody");function Gs(o){if(typeof o!="string")return;let e=o.trim();if(e)return e}i(Gs,"normalizeAbortCode");function js(o){if(typeof o!="string")return;let e=o.trim();if(e)return e.toUpperCase()}i(js,"normalizeMethod");function Ks(o){return Ws(o)}i(Ks,"normalizeChance");import{z as Xs}from"zod";var Ko=class{static{i(this,"Clear")}name(){return"stub_clear"}description(){return`
|
|
714
|
-
Clears stubs installed.
|
|
715
|
-
|
|
716
|
-
- If stubId is provided, clears only that stub.
|
|
717
|
-
- If stubId is omitted, clears all stubs for the current session/context.
|
|
718
|
-
`.trim()}inputSchema(){return{stubId:Xs.string().optional().describe("Stub id to remove. Omit to remove all stubs.")}}outputSchema(){return{clearedCount:Xs.number().int().nonnegative().describe("Number of stubs removed.")}}async handle(e,t){return{clearedCount:zs(e.browserContext,t.stubId)}}};import{z as ye}from"zod";var Xo=class{static{i(this,"InterceptHttpRequest")}name(){return"stub_intercept-http-request"}description(){return`
|
|
719
|
-
Installs a request interceptor stub that can modify outgoing requests before they are sent.
|
|
720
|
-
|
|
721
|
-
Use cases:
|
|
722
|
-
- A/B testing / feature flags (inject headers)
|
|
723
|
-
- Security testing (inject malformed headers / payload)
|
|
724
|
-
- Edge cases (special characters, large payload)
|
|
725
|
-
- Auth simulation (add API keys / tokens in headers)
|
|
726
|
-
|
|
727
|
-
Notes:
|
|
728
|
-
- pattern is a glob matched against the full request URL (picomatch).
|
|
729
|
-
- This modifies requests; it does not change responses.
|
|
730
|
-
- times limits how many times the interceptor applies (-1 means infinite).
|
|
731
|
-
`.trim()}inputSchema(){return{pattern:ye.string().describe("Glob pattern matched against the full request URL (picomatch)."),modifications:ye.object({headers:ye.record(ye.string(),ye.string()).optional().describe("Headers to merge into the outgoing request headers."),body:ye.union([ye.string(),ye.record(ye.string(),ye.any()),ye.array(ye.any())]).optional().describe("Override request body. If object/array, it will be JSON-stringified."),method:ye.string().optional().describe("Override HTTP method (e.g., POST, PUT).")}).optional().describe("Request modifications to apply."),delayMs:ye.number().int().nonnegative().optional().describe("Artificial delay in milliseconds before continuing the request."),times:ye.number().int().optional().describe("Apply only N times, then let through. Omit for infinite.")}}outputSchema(){return{stubId:ye.string().describe("Unique id of the installed stub."),kind:ye.literal("intercept_http_request").describe("Stub kind."),pattern:ye.string().describe("Glob pattern."),enabled:ye.boolean().describe("Whether the stub is enabled."),delayMs:ye.number().int().describe("Applied artificial delay in milliseconds."),times:ye.number().int().describe("Max applications (-1 means infinite).")}}async handle(e,t){await Vo(e.browserContext);let n=zo(t.delayMs),r=$o(t.times),s=Go(t.modifications?.headers),a=jo(t.modifications?.body),u=js(t.modifications?.method),l=Vs(e.browserContext,{enabled:!0,pattern:t.pattern,modifications:{headers:s,body:a,method:u},delayMs:n,times:r});return{stubId:l.id,kind:"intercept_http_request",pattern:l.pattern,enabled:l.enabled,delayMs:l.delayMs,times:l.times}}};import{z as Ke}from"zod";var Yo=class{static{i(this,"List")}name(){return"stub_list"}description(){return`
|
|
732
|
-
Lists currently installed stubs for the active browser context/session.
|
|
733
|
-
Useful to debug why certain calls are being mocked/intercepted.
|
|
734
|
-
`.trim()}inputSchema(){return{}}outputSchema(){return{stubs:Ke.array(Ke.object({id:Ke.string().describe("Stub id."),kind:Ke.string().describe("Stub kind."),enabled:Ke.boolean().describe("Whether stub is enabled."),pattern:Ke.string().describe("Glob pattern (picomatch)."),delayMs:Ke.number().int().describe("Artificial delay in ms."),times:Ke.number().int().describe("Max applications (-1 means infinite)."),usedCount:Ke.number().int().describe("How many times it has been applied."),action:Ke.string().optional().describe("For mock_response: fulfill/abort."),status:Ke.number().int().optional().describe("For mock_response: HTTP status (if set).")}))}}async handle(e,t){return{stubs:$s(e.browserContext).map(r=>{let s={id:r.id,kind:r.kind,enabled:r.enabled,pattern:r.pattern,delayMs:r.delayMs,times:r.times,usedCount:r.usedCount};return r.kind==="mock_http_response"&&(s.action=r.action,typeof r.status=="number"&&(s.status=r.status)),s})}}};import{z as ae}from"zod";var Jo=class{static{i(this,"MockHttpResponse")}name(){return"stub_mock-http-response"}description(){return`
|
|
735
|
-
Installs a response stub for matching requests using glob patterns (picomatch).
|
|
736
|
-
|
|
737
|
-
Use cases:
|
|
738
|
-
- Offline testing (return 200 with local JSON)
|
|
739
|
-
- Error scenarios (force 500/404 or abort with timedout)
|
|
740
|
-
- Edge cases (empty data / huge payload / special characters)
|
|
741
|
-
- Flaky API testing (chance < 1.0)
|
|
742
|
-
- Performance testing (delayMs)
|
|
743
|
-
|
|
744
|
-
Notes:
|
|
745
|
-
- pattern is a glob matched against the full request URL.
|
|
746
|
-
- stubs are evaluated in insertion order; first match wins.
|
|
747
|
-
- times limits how many times the stub applies (-1 means infinite).
|
|
748
|
-
`.trim()}inputSchema(){return{pattern:ae.string().describe("Glob pattern matched against the full request URL (picomatch)."),response:ae.object({action:ae.enum(["fulfill","abort"]).optional().default("fulfill").describe("Fulfill with a mocked response or abort the request."),status:ae.number().int().min(100).max(599).optional().describe("HTTP status code (used when action=fulfill)."),headers:ae.record(ae.string(),ae.string()).optional().describe("HTTP headers for the mocked response."),body:ae.union([ae.string(),ae.record(ae.string(),ae.any()),ae.array(ae.any())]).optional().describe("Response body. If object/array, it will be JSON-stringified."),abortErrorCode:ae.string().optional().describe('Playwright abort error code (used when action=abort), e.g. "timedout".')}).describe("Mock response configuration."),delayMs:ae.number().int().nonnegative().optional().describe("Artificial delay in milliseconds before applying the stub."),times:ae.number().int().optional().describe("Apply only N times, then let through. Omit for infinite."),chance:ae.number().min(0).max(1).optional().describe("Probability (0..1) to apply the stub per request (flaky testing).")}}outputSchema(){return{stubId:ae.string().describe("Unique id of the installed stub (use it to clear later)."),kind:ae.literal("mock_http_response").describe("Stub kind."),pattern:ae.string().describe("Glob pattern."),enabled:ae.boolean().describe("Whether the stub is enabled."),delayMs:ae.number().int().describe("Applied artificial delay in milliseconds."),times:ae.number().int().describe("Max applications (-1 means infinite)."),chance:ae.number().optional().describe("Apply probability (omit means always)."),action:ae.enum(["fulfill","abort"]).describe("Applied action."),status:ae.number().int().optional().describe("HTTP status (present when action=fulfill).")}}async handle(e,t){await Vo(e.browserContext);let n=t.response.action??"fulfill",r=zo(t.delayMs),s=$o(t.times),a=Ks(t.chance),u=t.response.status,l=Go(t.response.headers),m=jo(t.response.body),c=Gs(t.response.abortErrorCode),p=qs(e.browserContext,{enabled:!0,pattern:t.pattern,action:n,status:u,headers:l,body:m,abortErrorCode:c,delayMs:r,times:s,chance:a}),b={stubId:p.id,kind:"mock_http_response",pattern:p.pattern,enabled:p.enabled,delayMs:p.delayMs,times:p.times,action:p.action};return typeof p.chance=="number"&&(b.chance=p.chance),p.action==="fulfill"&&(b.status=typeof p.status=="number"?p.status:200),b}};var Ys=[new Ko,new Xo,new Yo,new Jo];import{z as Xe}from"zod";var Js=3e4,Qs=500,Zs=0,ea=50,Qo=class{static{i(this,"WaitForNetworkIdle")}name(){return"sync_wait-for-network-idle"}description(){return`
|
|
749
|
-
Waits until the page reaches a network-idle condition based on the session's tracked in-flight request count.
|
|
750
|
-
|
|
751
|
-
Definition:
|
|
752
|
-
- "Idle" means the number of in-flight requests is <= maxConnections
|
|
753
|
-
- and it stays that way continuously for at least idleTimeMs.
|
|
754
|
-
|
|
755
|
-
When to use:
|
|
756
|
-
- Before interacting with SPA pages that load data asynchronously
|
|
757
|
-
- Before taking screenshots / AX tree snapshots for more stable results
|
|
758
|
-
- After actions that trigger background fetch/XHR activity
|
|
759
|
-
|
|
760
|
-
Notes:
|
|
761
|
-
- This tool does NOT rely on window globals or page-injected counters.
|
|
762
|
-
- It uses server-side tracking, so it works reliably even with strict CSP.
|
|
763
|
-
- If the page has long-polling or never-ending requests, increase maxConnections or accept a shorter idleTimeMs.
|
|
764
|
-
`.trim()}inputSchema(){return{timeoutMs:Xe.number().int().min(0).optional().default(Js).describe("Maximum time to wait before failing (milliseconds).."),idleTimeMs:Xe.number().int().min(0).optional().default(Qs).describe("How long the network must stay idle continuously before resolving (milliseconds)."),maxConnections:Xe.number().int().min(0).optional().default(Zs).describe("Idle threshold. Network is considered idle when in-flight requests <= maxConnections."),pollIntervalMs:Xe.number().int().min(10).optional().default(ea).describe("Polling interval used to sample the in-flight request count (milliseconds).")}}outputSchema(){return{waitedMs:Xe.number().int().nonnegative().describe("Total time waited until the network became idle or the tool timed out (milliseconds)."),idleTimeMs:Xe.number().int().nonnegative().describe("Idle duration required for success (milliseconds)."),timeoutMs:Xe.number().int().nonnegative().describe("Maximum allowed wait time (milliseconds)."),maxConnections:Xe.number().int().nonnegative().describe("Idle threshold used: in-flight requests must be <= this value."),pollIntervalMs:Xe.number().int().nonnegative().describe("Polling interval used to sample the in-flight request count (milliseconds)."),finalInFlightRequests:Xe.number().int().nonnegative().describe("The last observed number of in-flight requests at the moment the tool returned."),observedIdleMs:Xe.number().int().nonnegative().describe("How long the in-flight request count stayed <= maxConnections right before returning (milliseconds).")}}async handle(e,t){let n=t.timeoutMs??Js,r=t.idleTimeMs??Qs,s=t.maxConnections??Zs,a=t.pollIntervalMs??ea,u=Date.now(),l=u+n,m=u,c=0;for(;;){let p=Date.now();c=e.numOfInFlightRequests(),c>s&&(m=p);let b=p-m;if(b>=r)return{waitedMs:p-u,idleTimeMs:r,timeoutMs:n,maxConnections:s,pollIntervalMs:a,finalInFlightRequests:c,observedIdleMs:b};if(p>=l){let h=p-u;throw new Error(`Timed out after ${h}ms waiting for network idle (idleTimeMs=${r}, maxConnections=${s}, inFlight=${c}).`)}await this.sleep(a)}}async sleep(e){await new Promise(t=>{setTimeout(()=>t(),e)})}};var ta=[new Qo];var Oy=[...bi,...wi,...Gi,...ms,...Ss,...ws,...xs,...Ms,...Ls,...Ys,...ta];export{i as a,kl as b,Dl as c,Al as d,Ll as e,kr as f,Dr as g,Ar as h,Lr as i,Br as j,Hr as k,Wr as l,Ur as m,Kt as n,rn as o,Dt as p,sn as q,an as r,zr as s,$r as t,Gr as u,jr as v,Kr as w,Bl as x,Fl as y,Hl as z,ql as A,Vl as B,Xr as C,be as D,zl as E,$l as F,ri as G,Oy as H};
|