@agimon-ai/browse-tool 0.17.2 → 0.17.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import{a as e,c as t,h as n,p as r,s as i,t as a,v as o,y as s}from"./playwright-test-DoPVFxH9.mjs";import{stripTypeScriptTypes as c}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as l,PortRegistryService as u}from"@agimon-ai/foundation-port-registry";import{ProcessRegistryService as d}from"@agimon-ai/foundation-process-registry";import{Container as f,ContainerModule as p,inject as m,injectable as h,optional as g}from"inversify";import*as _ from"node:path";import v,{basename as y,dirname as b,isAbsolute as x,join as S,relative as C,resolve as w}from"node:path";import T,{constants as ee,createWriteStream as E,existsSync as D,mkdirSync as O,readFileSync as te,writeFileSync as ne}from"node:fs";import*as re from"node:fs/promises";import{access as ie,chmod as ae,mkdir as k,readFile as A,readdir as oe,rename as se,rm as ce,stat as le,symlink as ue,unlink as de,writeFile as j}from"node:fs/promises";import M,{arch as fe,homedir as pe,platform as me,tmpdir as N}from"node:os";import{execFile as he,execSync as ge,spawn as P}from"node:child_process";import{fileURLToPath as _e}from"node:url";import ve,{chromium as ye,firefox as be,webkit as xe}from"playwright";import{Readable as Se}from"node:stream";import{pipeline as Ce}from"node:stream/promises";import{AsyncLocalStorage as we}from"node:async_hooks";import{z as F}from"zod";import{createNodeTelemetry as Te,resolveNodeTelemetryEndpoints as Ee}from"@agimon-ai/log-sink-mcp";import{SpanKind as De,SpanStatusCode as Oe,context as ke,trace as Ae}from"@opentelemetry/api";import{OTLPTraceExporter as je}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes as Me}from"@opentelemetry/resources";import{BatchSpanProcessor as Ne,NodeTracerProvider as Pe,SimpleSpanProcessor as Fe}from"@opentelemetry/sdk-trace-node";import{promisify as Ie}from"node:util";import{parse as Le}from"yaml";import{build as Re}from"esbuild";import{randomUUID as I}from"node:crypto";import{isInitializeRequest as ze}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as Be}from"@modelcontextprotocol/sdk/server/stdio.js";import{createServer as Ve}from"node:http";import{StreamableHTTPServerTransport as He}from"@modelcontextprotocol/sdk/server/streamableHttp.js";const L={ProfileService:Symbol.for(`ProfileService`),ProxyConfigService:Symbol.for(`ProxyConfigService`),PageRegistry:Symbol.for(`PageRegistry`),BrowserService:Symbol.for(`BrowserService`),ElementLocatorService:Symbol.for(`ElementLocatorService`),PageMonitorService:Symbol.for(`PageMonitorService`),PauseController:Symbol.for(`PauseController`),AutomationRunner:Symbol.for(`AutomationRunner`),SpecRunner:Symbol.for(`SpecRunner`),SpecBundlerService:Symbol.for(`SpecBundlerService`),SpecDiscoveryService:Symbol.for(`SpecDiscoveryService`),SpecMetadataService:Symbol.for(`SpecMetadataService`),SetupRunner:Symbol.for(`SetupRunner`),WebServerManager:Symbol.for(`WebServerManager`),Logger:Symbol.for(`Logger`),TelemetryService:Symbol.for(`TelemetryService`),CodeSnippetService:Symbol.for(`CodeSnippetService`),HttpServerHealthCheck:Symbol.for(`HttpServerHealthCheck`),HttpServerManager:Symbol.for(`HttpServerManager`),HttpBrowserClient:Symbol.for(`HttpBrowserClient`),RemoteToolExecutor:Symbol.for(`RemoteToolExecutor`),ExtensionTaskQueue:Symbol.for(`ExtensionTaskQueue`),ExtensionToolDelegator:Symbol.for(`ExtensionToolDelegator`),ToolExecutor:Symbol.for(`ToolExecutor`),StealthLauncher:Symbol.for(`StealthLauncher`),BrowserLockManager:Symbol.for(`BrowserLockManager`),ExtensionSessionRegistry:Symbol.for(`ExtensionSessionRegistry`),ExtensionPageProxy:Symbol.for(`ExtensionPageProxy`),ExtensionSpecRunner:Symbol.for(`ExtensionSpecRunner`),McpSessionTracker:Symbol.for(`McpSessionTracker`),ChromeForTestingService:Symbol.for(`ChromeForTestingService`),WebSocketHub:Symbol.for(`WebSocketHub`),IdleCleanupService:Symbol.for(`IdleCleanupService`),PortRegistryService:Symbol.for(`PortRegistryService`),McpPortAllocationService:Symbol.for(`McpPortAllocationService`),ProcessRegistryService:Symbol.for(`ProcessRegistryService`),Tool:Symbol.for(`Tool`)};function R(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function z(e,t){return function(n,r){t(n,r,e)}}function B(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}let Ue=class{sessions=new Map;sessionIdCounter=0;constructor(e,t,n,r,i,a){this.browserService=e,this.specRunner=t,this.specMetadataService=n,this.setupRunner=r,this.webServerManager=i,this.pageRegistry=a}async runSpec(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},browserId:a,pageId:o,hooksPath:s}=e,c=n??`spec-${++this.sessionIdCounter}`,l={id:c,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(c,l);let u=!!(a&&o);try{u&&(l.browserId=a,l.pageIds.push(o)),l.status=`running`,l.updatedAt=new Date;let e=await this.specRunner.executeSpec({specPath:t,sessionId:c,browserType:i.browserType,headless:i.headless,hooksPath:s,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&e.browser){let t=this.browserService.registerExternalBrowser(e.browser);if(l.browserId=t,e.keptPages?.length)for(let n of e.keptPages){let e=await this.pageRegistry.register({page:n.page,context:n.context,browserId:t,onClose:e=>{let t=l.pageIds.indexOf(e);t>=0&&l.pageIds.splice(t,1)}});l.pageIds.push(e)}}return l.status=`completed`,l.updatedAt=new Date,{automationId:c,status:`completed`,pageIds:r||u?[...l.pageIds]:[],browserId:r||u?l.browserId:null,specResult:e}}catch(e){return l.status=`failed`,l.error=e instanceof Error?e.message:String(e),l.updatedAt=new Date,{automationId:c,status:`failed`,pageIds:u?[...l.pageIds]:[],browserId:u?l.browserId:null}}}async runSpecEnhanced(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},testFilter:a,specArgs:o,configPath:s,runSetup:c=!1,startServer:l=!1,stopServerAfter:u=!0,browserId:d,pageId:f,hooksPath:p}=e,m=n??`spec-${++this.sessionIdCounter}`,h={id:m,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(m,h);let g,_;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?b(s):process.cwd();l&&e?.webServer&&(g=await this.webServerManager.startServer(e.webServer,n));let v=!!(d&&f);if(v&&(h.browserId=d,h.pageIds.push(f)),h.status=`running`,h.updatedAt=new Date,c&&e?.setupFile){let t=e.setupFile.startsWith(`/`)?e.setupFile:`${n}/${e.setupFile}`,r=e.setupExport??`default`,a=await this.browserService.launch(i);try{if(_=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!_.success)throw Error(_.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let y=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:m,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,..._?.state},hooksPath:p,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&y.browser){let e=this.browserService.registerExternalBrowser(y.browser);if(h.browserId=e,y.keptPages?.length)for(let t of y.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=h.pageIds.indexOf(e);t>=0&&h.pageIds.splice(t,1)}});h.pageIds.push(n)}}return h.status=`completed`,h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer(),{automationId:m,status:`completed`,pageIds:r||v?[...h.pageIds]:[],browserId:r||v?h.browserId:null,specResult:y,serverResult:g,setupResult:_}}catch(e){h.status=`failed`,h.error=e instanceof Error?e.message:String(e),h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer();let t=!!(d&&f);return{automationId:m,status:`failed`,pageIds:t?[...h.pageIds]:[],browserId:t?h.browserId:null,serverResult:g,setupResult:_}}}getSession(e){return this.sessions.get(e)}listSessions(){return Array.from(this.sessions.values())}async stop(e){let t=this.sessions.get(e);return t?(t.browserId&&await this.browserService.closeBrowser(t.browserId),t.status=`completed`,t.updatedAt=new Date,!0):!1}};Ue=B([h(),z(0,m(L.BrowserService)),z(1,m(L.SpecRunner)),z(2,m(L.SpecMetadataService)),z(3,m(L.SetupRunner)),z(4,m(L.WebServerManager)),z(5,m(L.PageRegistry)),R(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],Ue);var We=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let Ge=class{platform=M.platform();async checkLock(e){let t={isLocked:!1};return this.platform===`win32`?this.checkWindowsLock(e,t):this.checkUnixLock(e,t)}async cleanupOrphanedLocks(e){let t=await this.checkLock(e);if(!t.isLocked)return!0;if(t.isProcessAlive)return!1;let n=[v.join(e,`SingletonLock`),v.join(e,`SingletonCookie`),v.join(e,`SingletonSocket`),v.join(e,`lockfile`)];for(let e of n)try{T.existsSync(e)&&await de(e)}catch{}let r=v.join(e,`Default`,`LOCK`);try{T.existsSync(r)&&await de(r)}catch{}return!0}async ensureAvailable(e){if(!T.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new We(`Browser session conflict: Another Chrome process (PID: ${t.pid}) is using this profile.\nUser data directory: ${e}\n\nTo resolve:\n1. Close the existing browser session manually\n2. Or use a different profile name\n3. Or kill the process: kill ${t.pid}`,t.pid,e);if(!await this.cleanupOrphanedLocks(e))throw Error(`Failed to clean up orphaned browser lock in ${e}`)}}async checkUnixLock(e,t){let n=v.join(e,`SingletonLock`),r=v.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=T.readlinkSync(n).match(/-(\d+)$/);e&&(t.isLocked=!0,t.pid=Number.parseInt(e[1],10),t.isProcessAlive=this.isProcessRunning(t.pid))}catch{t.isLocked=!0,t.isProcessAlive=T.existsSync(r)}return!t.isLocked&&T.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return T.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=v.join(e,`lockfile`);if(t.lockFilePath=n,T.existsSync(n))try{let e=await A(n,`utf-8`),r=Number.parseInt(e.trim(),10);Number.isNaN(r)||(t.isLocked=!0,t.pid=r,t.isProcessAlive=this.isProcessRunning(r))}catch{t.isLocked=!0,t.isProcessAlive=!0}return t}isProcessRunning(e){try{return process.kill(e,0),!0}catch(e){return e.code===`EPERM`}}};Ge=B([h()],Ge);var Ke=`147.0.7727.50`,qe=`browse-tool/chrome-for-testing:147.0.7727.50`,Je=`/usr/local/bin/google-chrome-for-testing`,Ye=`linux/amd64`,Xe=`browse-tool/cloakbrowser:latest`,Ze=`/usr/local/bin/cloakbrowser`,Qe=`linux/amd64`;const V=18e4;var $e;const et=Ke,tt=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function nt(e,t){let n=e.split(`.`).map(e=>Number.parseInt(e,10)||0),r=t.split(`.`).map(e=>Number.parseInt(e,10)||0),i=Math.max(n.length,r.length);for(let e=0;e<i;e+=1){let t=n[e]??0,i=r[e]??0;if(t!==i)return t>i?1:-1}return 0}function rt(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:et}function it(){let e=process.env[tt]?.trim();return e&&e.length>0?e:void 0}function at(e){return nt(e,`147.0.7727.3`)>=0}function ot(){return at(rt())}let st=class{static{$e=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=S(pe(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=it();if(e)return this.validateConfiguredExecutablePath(e);let t=this.getExpectedExecutablePath(),n=await this.getInstalledVersion();try{let e=this.getRequestedVersion(),r=await this.fetchVersionInfo(e);return D(t)&&n===e?t:this.download(r)}catch(e){if(D(t))return t;throw e}}async isInstalled(){return D(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=S(this.cacheDir,`version.txt`);if(!D(e))return null;try{return(await A(e,`utf-8`)).trim()}catch{return null}}async update(){let e=S(this.cacheDir,`chrome`);return D(e)&&await ce(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){O(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=S(this.cacheDir,`chrome.zip`),r=S(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await ce(n,{force:!0}),await j(S(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!D(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return me()!==`win32`&&await ae(i,493),i}async fetchVersionInfo(e){let t=await fetch($e.API_URL);if(!t.ok)throw Error(`Failed to fetch Chrome for Testing info: ${t.statusText}`);let n=(await t.json()).versions.find(t=>t.version===e);if(!n)throw Error(`Chrome for Testing version ${e} not found in feed`);return n}getRequestedVersion(){return rt()}async validateConfiguredExecutablePath(e){try{if(!(await le(e)).isFile())throw Error(`configured path is not a file`);return me()!==`win32`&&await ie(e,ee.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${tt} was not found or is not executable: ${e}`,{cause:t})}}getDownloadUrl(e){let t=this.getPlatformKey();return e.downloads.chrome.find(e=>e.platform===t)?.url}getPlatformKey(){let e=me(),t=fe();if(e===`darwin`)return t===`arm64`?`mac-arm64`:`mac-x64`;if(e===`linux`)return`linux64`;if(e===`win32`)return t===`x64`?`win64`:`win32`;throw Error(`Unsupported platform: ${e}`)}getExpectedExecutablePath(){let e=me(),t=S(this.cacheDir,`chrome`);if(e===`darwin`)return S(t,`chrome-mac-${fe()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return S(t,`chrome-linux64`,`chrome`);if(e===`win32`)return S(t,`chrome-win${fe()===`x64`?`64`:`32`}`,`chrome.exe`);throw Error(`Unsupported platform: ${e}`)}async downloadFile(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw Error(`Failed to download: ${n.statusText}`);let r=`${t}.tmp`,i=E(r),a=n.body.getReader();await Ce(new Se({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await se(r,t)}async extractZip(e,t){let n=me();return new Promise((r,i)=>{let a;n===`win32`?a=P(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):(O(t,{recursive:!0}),a=P(`unzip`,[`-o`,`-q`,e,`-d`,t])),a.on(`close`,e=>{e===0?r():i(Error(`Failed to extract zip (exit code: ${e})`))}),a.on(`error`,e=>{i(Error(`Failed to spawn extraction process: ${e.message}`))})})}};st=$e=B([h(),R(`design:paramtypes`,[])],st);const ct=[`pnpm-workspace.yaml`,`nx.json`,`.git`],lt=qe,ut=Ye,dt=lt.split(`:`)[0]??lt,ft=Xe,pt=ft.split(`:`)[0]??ft;function mt(e=process.cwd()){let t=v.resolve(e);for(;;){for(let e of ct)if(ht(v.join(t,e)))return t;let n=v.dirname(t);if(n===t)return e;t=n}}function ht(e){return T.existsSync(e)}function gt(){return dt}function _t(e){return`${gt()}:${e}`}function vt(e){let t=v.resolve(e);for(let e=0;e<8;e+=1){let e=v.join(t,`package.json`);if(ht(e))try{if(JSON.parse(T.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=v.dirname(t);if(n===t)break;t=n}return v.resolve(e,`../..`)}function yt(){let e=v.dirname(_e(import.meta.url));return v.join(vt(e),`docker/chrome-for-testing.Dockerfile`)}function bt(){return mt(vt(v.dirname(_e(import.meta.url))))}function xt(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function St(e){return(e?.trim()||ut).toLowerCase()}function Ct(e){let t=St(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function wt(e){return e.startsWith(`${gt()}:`)}async function Tt(e,t){return await new Promise((n,r)=>{let i=``,a=P(`docker`,e,{stdio:t===`pipe`?[`ignore`,`pipe`,`pipe`]:[`ignore`,t,t]});t===`pipe`&&a.stderr?.on(`data`,e=>{i+=e.toString(`utf8`)}),a.once(`error`,r),a.once(`close`,e=>{n({exitCode:e??1,stderr:i})})})}async function Et(e){return(await Tt([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Dt(e={}){let t=e.version?.trim()||xt(e.image)||rt(),n=e.image?.trim()||_t(t),r=St(e.platform),i=Ct(r),a=e.stdio??`inherit`,o=await Tt([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,yt(),bt()],a);if(o.exitCode!==0){let e=o.stderr.trim();throw Error(e?`Failed to build Docker image ${n}: ${e}`:`Failed to build Docker image ${n}`)}return{image:n,version:t,platform:r}}async function Ot(e){return!wt(e.image)||await Et(e.image)?!1:(await Dt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function kt(e){return e.startsWith(`${pt}:`)}function At(){let e=v.dirname(_e(import.meta.url));return v.join(vt(e),`docker/cloakbrowser.Dockerfile`)}async function jt(e={}){let t=e.image?.trim()||ft,n=St(e.platform),r=e.stdio??`inherit`,i=await Tt([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,At(),bt()],r);if(i.exitCode!==0){let e=i.stderr.trim();throw Error(e?`Failed to build CloakBrowser Docker image ${t}: ${e}`:`Failed to build CloakBrowser Docker image ${t}`)}return{image:t,platform:n}}async function Mt(e){return!kt(e.image)||await Et(e.image)?!1:(await jt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Nt(){let e=new Map,t=ge(`ps -axo pid=,ppid=`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:3e3});for(let n of t.split(`
1
+ import{a as e,c as t,h as n,p as r,s as i,t as a,v as o,y as s}from"./playwright-test-DoPVFxH9.mjs";import{stripTypeScriptTypes as c}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as l,PortRegistryService as u}from"@agimon-ai/foundation-port-registry";import{ProcessRegistryService as d}from"@agimon-ai/foundation-process-registry";import{Container as f,ContainerModule as p,inject as m,injectable as h,optional as g}from"inversify";import*as _ from"node:path";import v,{basename as y,dirname as b,isAbsolute as x,join as S,relative as C,resolve as w}from"node:path";import T,{constants as ee,createWriteStream as E,existsSync as D,mkdirSync as O,readFileSync as te,writeFileSync as ne}from"node:fs";import*as re from"node:fs/promises";import{access as ie,chmod as ae,mkdir as k,readFile as A,readdir as oe,rename as se,rm as ce,stat as le,symlink as ue,unlink as de,writeFile as j}from"node:fs/promises";import M,{arch as fe,homedir as pe,platform as me,tmpdir as N}from"node:os";import{execFile as he,execSync as ge,spawn as P}from"node:child_process";import{fileURLToPath as _e}from"node:url";import ve,{chromium as ye,firefox as be,webkit as xe}from"playwright";import{Readable as Se}from"node:stream";import{pipeline as Ce}from"node:stream/promises";import{AsyncLocalStorage as we}from"node:async_hooks";import{z as F}from"zod";import{createNodeTelemetry as Te,resolveNodeTelemetryEndpoints as Ee}from"@agimon-ai/log-sink-mcp";import{SpanKind as De,SpanStatusCode as Oe,context as ke,trace as Ae}from"@opentelemetry/api";import{OTLPTraceExporter as je}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes as Me}from"@opentelemetry/resources";import{BatchSpanProcessor as Ne,NodeTracerProvider as Pe,SimpleSpanProcessor as Fe}from"@opentelemetry/sdk-trace-node";import{promisify as Ie}from"node:util";import{parse as Le}from"yaml";import{randomUUID as I}from"node:crypto";import{build as Re}from"esbuild";import{isInitializeRequest as ze}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as Be}from"@modelcontextprotocol/sdk/server/stdio.js";import{createServer as Ve}from"node:http";import{StreamableHTTPServerTransport as He}from"@modelcontextprotocol/sdk/server/streamableHttp.js";const L={ProfileService:Symbol.for(`ProfileService`),ProxyConfigService:Symbol.for(`ProxyConfigService`),PageRegistry:Symbol.for(`PageRegistry`),BrowserService:Symbol.for(`BrowserService`),ElementLocatorService:Symbol.for(`ElementLocatorService`),PageMonitorService:Symbol.for(`PageMonitorService`),PauseController:Symbol.for(`PauseController`),AutomationRunner:Symbol.for(`AutomationRunner`),SpecRunner:Symbol.for(`SpecRunner`),SpecBundlerService:Symbol.for(`SpecBundlerService`),SpecDiscoveryService:Symbol.for(`SpecDiscoveryService`),SpecMetadataService:Symbol.for(`SpecMetadataService`),SetupRunner:Symbol.for(`SetupRunner`),WebServerManager:Symbol.for(`WebServerManager`),Logger:Symbol.for(`Logger`),TelemetryService:Symbol.for(`TelemetryService`),CodeSnippetService:Symbol.for(`CodeSnippetService`),HttpServerHealthCheck:Symbol.for(`HttpServerHealthCheck`),HttpServerManager:Symbol.for(`HttpServerManager`),HttpBrowserClient:Symbol.for(`HttpBrowserClient`),RemoteToolExecutor:Symbol.for(`RemoteToolExecutor`),ExtensionTaskQueue:Symbol.for(`ExtensionTaskQueue`),ExtensionToolDelegator:Symbol.for(`ExtensionToolDelegator`),ToolExecutor:Symbol.for(`ToolExecutor`),StealthLauncher:Symbol.for(`StealthLauncher`),BrowserLockManager:Symbol.for(`BrowserLockManager`),ExtensionSessionRegistry:Symbol.for(`ExtensionSessionRegistry`),ExtensionPageProxy:Symbol.for(`ExtensionPageProxy`),ExtensionSpecRunner:Symbol.for(`ExtensionSpecRunner`),McpSessionTracker:Symbol.for(`McpSessionTracker`),ChromeForTestingService:Symbol.for(`ChromeForTestingService`),WebSocketHub:Symbol.for(`WebSocketHub`),IdleCleanupService:Symbol.for(`IdleCleanupService`),PortRegistryService:Symbol.for(`PortRegistryService`),McpPortAllocationService:Symbol.for(`McpPortAllocationService`),ProcessRegistryService:Symbol.for(`ProcessRegistryService`),Tool:Symbol.for(`Tool`)};function R(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function z(e,t){return function(n,r){t(n,r,e)}}function B(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}let Ue=class{sessions=new Map;sessionIdCounter=0;constructor(e,t,n,r,i,a){this.browserService=e,this.specRunner=t,this.specMetadataService=n,this.setupRunner=r,this.webServerManager=i,this.pageRegistry=a}async runSpec(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},browserId:a,pageId:o,hooksPath:s}=e,c=n??`spec-${++this.sessionIdCounter}`,l={id:c,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(c,l);let u=!!(a&&o);try{u&&(l.browserId=a,l.pageIds.push(o)),l.status=`running`,l.updatedAt=new Date;let e=await this.specRunner.executeSpec({specPath:t,sessionId:c,browserType:i.browserType,headless:i.headless,hooksPath:s,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&e.browser){let t=this.browserService.registerExternalBrowser(e.browser);if(l.browserId=t,e.keptPages?.length)for(let n of e.keptPages){let e=await this.pageRegistry.register({page:n.page,context:n.context,browserId:t,onClose:e=>{let t=l.pageIds.indexOf(e);t>=0&&l.pageIds.splice(t,1)}});l.pageIds.push(e)}}return l.status=`completed`,l.updatedAt=new Date,{automationId:c,status:`completed`,pageIds:r||u?[...l.pageIds]:[],browserId:r||u?l.browserId:null,specResult:e}}catch(e){return l.status=`failed`,l.error=e instanceof Error?e.message:String(e),l.updatedAt=new Date,{automationId:c,status:`failed`,pageIds:u?[...l.pageIds]:[],browserId:u?l.browserId:null}}}async runSpecEnhanced(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},testFilter:a,specArgs:o,configPath:s,runSetup:c=!1,startServer:l=!1,stopServerAfter:u=!0,browserId:d,pageId:f,hooksPath:p}=e,m=n??`spec-${++this.sessionIdCounter}`,h={id:m,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(m,h);let g,_;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?b(s):process.cwd();l&&e?.webServer&&(g=await this.webServerManager.startServer(e.webServer,n));let v=!!(d&&f);if(v&&(h.browserId=d,h.pageIds.push(f)),h.status=`running`,h.updatedAt=new Date,c&&e?.setupFile){let t=e.setupFile.startsWith(`/`)?e.setupFile:`${n}/${e.setupFile}`,r=e.setupExport??`default`,a=await this.browserService.launch(i);try{if(_=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!_.success)throw Error(_.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let y=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:m,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,..._?.state},hooksPath:p,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&y.browser){let e=this.browserService.registerExternalBrowser(y.browser);if(h.browserId=e,y.keptPages?.length)for(let t of y.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=h.pageIds.indexOf(e);t>=0&&h.pageIds.splice(t,1)}});h.pageIds.push(n)}}return h.status=`completed`,h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer(),{automationId:m,status:`completed`,pageIds:r||v?[...h.pageIds]:[],browserId:r||v?h.browserId:null,specResult:y,serverResult:g,setupResult:_}}catch(e){h.status=`failed`,h.error=e instanceof Error?e.message:String(e),h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer();let t=!!(d&&f);return{automationId:m,status:`failed`,pageIds:t?[...h.pageIds]:[],browserId:t?h.browserId:null,serverResult:g,setupResult:_}}}getSession(e){return this.sessions.get(e)}listSessions(){return Array.from(this.sessions.values())}async stop(e){let t=this.sessions.get(e);return t?(t.browserId&&await this.browserService.closeBrowser(t.browserId),t.status=`completed`,t.updatedAt=new Date,!0):!1}};Ue=B([h(),z(0,m(L.BrowserService)),z(1,m(L.SpecRunner)),z(2,m(L.SpecMetadataService)),z(3,m(L.SetupRunner)),z(4,m(L.WebServerManager)),z(5,m(L.PageRegistry)),R(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],Ue);var We=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let Ge=class{platform=M.platform();async checkLock(e){let t={isLocked:!1};return this.platform===`win32`?this.checkWindowsLock(e,t):this.checkUnixLock(e,t)}async cleanupOrphanedLocks(e){let t=await this.checkLock(e);if(!t.isLocked)return!0;if(t.isProcessAlive)return!1;let n=[v.join(e,`SingletonLock`),v.join(e,`SingletonCookie`),v.join(e,`SingletonSocket`),v.join(e,`lockfile`)];for(let e of n)try{T.existsSync(e)&&await de(e)}catch{}let r=v.join(e,`Default`,`LOCK`);try{T.existsSync(r)&&await de(r)}catch{}return!0}async ensureAvailable(e){if(!T.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new We(`Browser session conflict: Another Chrome process (PID: ${t.pid}) is using this profile.\nUser data directory: ${e}\n\nTo resolve:\n1. Close the existing browser session manually\n2. Or use a different profile name\n3. Or kill the process: kill ${t.pid}`,t.pid,e);if(!await this.cleanupOrphanedLocks(e))throw Error(`Failed to clean up orphaned browser lock in ${e}`)}}async checkUnixLock(e,t){let n=v.join(e,`SingletonLock`),r=v.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=T.readlinkSync(n).match(/-(\d+)$/);e&&(t.isLocked=!0,t.pid=Number.parseInt(e[1],10),t.isProcessAlive=this.isProcessRunning(t.pid))}catch{t.isLocked=!0,t.isProcessAlive=T.existsSync(r)}return!t.isLocked&&T.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return T.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=v.join(e,`lockfile`);if(t.lockFilePath=n,T.existsSync(n))try{let e=await A(n,`utf-8`),r=Number.parseInt(e.trim(),10);Number.isNaN(r)||(t.isLocked=!0,t.pid=r,t.isProcessAlive=this.isProcessRunning(r))}catch{t.isLocked=!0,t.isProcessAlive=!0}return t}isProcessRunning(e){try{return process.kill(e,0),!0}catch(e){return e.code===`EPERM`}}};Ge=B([h()],Ge);var Ke=`147.0.7727.50`,qe=`browse-tool/chrome-for-testing:147.0.7727.50`,Je=`/usr/local/bin/google-chrome-for-testing`,Ye=`linux/amd64`,Xe=`browse-tool/cloakbrowser:latest`,Ze=`/usr/local/bin/cloakbrowser`,Qe=`linux/amd64`;const V=18e4;var $e;const et=Ke,tt=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function nt(e,t){let n=e.split(`.`).map(e=>Number.parseInt(e,10)||0),r=t.split(`.`).map(e=>Number.parseInt(e,10)||0),i=Math.max(n.length,r.length);for(let e=0;e<i;e+=1){let t=n[e]??0,i=r[e]??0;if(t!==i)return t>i?1:-1}return 0}function rt(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:et}function it(){let e=process.env[tt]?.trim();return e&&e.length>0?e:void 0}function at(e){return nt(e,`147.0.7727.3`)>=0}function ot(){return at(rt())}let st=class{static{$e=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=S(pe(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=it();if(e)return this.validateConfiguredExecutablePath(e);let t=this.getExpectedExecutablePath(),n=await this.getInstalledVersion();try{let e=this.getRequestedVersion(),r=await this.fetchVersionInfo(e);return D(t)&&n===e?t:this.download(r)}catch(e){if(D(t))return t;throw e}}async isInstalled(){return D(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=S(this.cacheDir,`version.txt`);if(!D(e))return null;try{return(await A(e,`utf-8`)).trim()}catch{return null}}async update(){let e=S(this.cacheDir,`chrome`);return D(e)&&await ce(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){O(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=S(this.cacheDir,`chrome.zip`),r=S(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await ce(n,{force:!0}),await j(S(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!D(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return me()!==`win32`&&await ae(i,493),i}async fetchVersionInfo(e){let t=await fetch($e.API_URL);if(!t.ok)throw Error(`Failed to fetch Chrome for Testing info: ${t.statusText}`);let n=(await t.json()).versions.find(t=>t.version===e);if(!n)throw Error(`Chrome for Testing version ${e} not found in feed`);return n}getRequestedVersion(){return rt()}async validateConfiguredExecutablePath(e){try{if(!(await le(e)).isFile())throw Error(`configured path is not a file`);return me()!==`win32`&&await ie(e,ee.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${tt} was not found or is not executable: ${e}`,{cause:t})}}getDownloadUrl(e){let t=this.getPlatformKey();return e.downloads.chrome.find(e=>e.platform===t)?.url}getPlatformKey(){let e=me(),t=fe();if(e===`darwin`)return t===`arm64`?`mac-arm64`:`mac-x64`;if(e===`linux`)return`linux64`;if(e===`win32`)return t===`x64`?`win64`:`win32`;throw Error(`Unsupported platform: ${e}`)}getExpectedExecutablePath(){let e=me(),t=S(this.cacheDir,`chrome`);if(e===`darwin`)return S(t,`chrome-mac-${fe()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return S(t,`chrome-linux64`,`chrome`);if(e===`win32`)return S(t,`chrome-win${fe()===`x64`?`64`:`32`}`,`chrome.exe`);throw Error(`Unsupported platform: ${e}`)}async downloadFile(e,t){let n=await fetch(e);if(!n.ok||!n.body)throw Error(`Failed to download: ${n.statusText}`);let r=`${t}.tmp`,i=E(r),a=n.body.getReader();await Ce(new Se({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await se(r,t)}async extractZip(e,t){let n=me();return new Promise((r,i)=>{let a;n===`win32`?a=P(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):(O(t,{recursive:!0}),a=P(`unzip`,[`-o`,`-q`,e,`-d`,t])),a.on(`close`,e=>{e===0?r():i(Error(`Failed to extract zip (exit code: ${e})`))}),a.on(`error`,e=>{i(Error(`Failed to spawn extraction process: ${e.message}`))})})}};st=$e=B([h(),R(`design:paramtypes`,[])],st);const ct=[`pnpm-workspace.yaml`,`nx.json`,`.git`],lt=qe,ut=Ye,dt=lt.split(`:`)[0]??lt,ft=Xe,pt=ft.split(`:`)[0]??ft;function mt(e=process.cwd()){let t=v.resolve(e);for(;;){for(let e of ct)if(ht(v.join(t,e)))return t;let n=v.dirname(t);if(n===t)return e;t=n}}function ht(e){return T.existsSync(e)}function gt(){return dt}function _t(e){return`${gt()}:${e}`}function vt(e){let t=v.resolve(e);for(let e=0;e<8;e+=1){let e=v.join(t,`package.json`);if(ht(e))try{if(JSON.parse(T.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=v.dirname(t);if(n===t)break;t=n}return v.resolve(e,`../..`)}function yt(){let e=v.dirname(_e(import.meta.url));return v.join(vt(e),`docker/chrome-for-testing.Dockerfile`)}function bt(){return mt(vt(v.dirname(_e(import.meta.url))))}function xt(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function St(e){return(e?.trim()||ut).toLowerCase()}function Ct(e){let t=St(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function wt(e){return e.startsWith(`${gt()}:`)}async function Tt(e,t){return await new Promise((n,r)=>{let i=``,a=P(`docker`,e,{stdio:t===`pipe`?[`ignore`,`pipe`,`pipe`]:[`ignore`,t,t]});t===`pipe`&&a.stderr?.on(`data`,e=>{i+=e.toString(`utf8`)}),a.once(`error`,r),a.once(`close`,e=>{n({exitCode:e??1,stderr:i})})})}async function Et(e){return(await Tt([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Dt(e={}){let t=e.version?.trim()||xt(e.image)||rt(),n=e.image?.trim()||_t(t),r=St(e.platform),i=Ct(r),a=e.stdio??`inherit`,o=await Tt([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,yt(),bt()],a);if(o.exitCode!==0){let e=o.stderr.trim();throw Error(e?`Failed to build Docker image ${n}: ${e}`:`Failed to build Docker image ${n}`)}return{image:n,version:t,platform:r}}async function Ot(e){return!wt(e.image)||await Et(e.image)?!1:(await Dt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function kt(e){return e.startsWith(`${pt}:`)}function At(){let e=v.dirname(_e(import.meta.url));return v.join(vt(e),`docker/cloakbrowser.Dockerfile`)}async function jt(e={}){let t=e.image?.trim()||ft,n=St(e.platform),r=e.stdio??`inherit`,i=await Tt([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,At(),bt()],r);if(i.exitCode!==0){let e=i.stderr.trim();throw Error(e?`Failed to build CloakBrowser Docker image ${t}: ${e}`:`Failed to build CloakBrowser Docker image ${t}`)}return{image:t,platform:n}}async function Mt(e){return!kt(e.image)||await Et(e.image)?!1:(await jt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Nt(){let e=new Map,t=ge(`ps -axo pid=,ppid=`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:3e3});for(let n of t.split(`
2
2
  `)){let[t,r]=n.trim().split(/\s+/,2);if(!t||!r)continue;let i=Number(t),a=Number(r);if(Number.isNaN(i)||Number.isNaN(a))continue;let o=e.get(a);o?o.push(i):e.set(a,[i])}return e}function Pt(e){let t=[];try{let n;try{n=ge(`pgrep -P ${e}`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:3e3}).trim().split(`
3
3
  `).filter(Boolean).map(Number).filter(e=>!Number.isNaN(e))}catch{n=Nt().get(e)??[]}for(let e of n)t.push(...Pt(e)),t.push(e)}catch{}return t}function Ft(e){try{return process.kill(e,0),!0}catch{return!1}}async function It(e,t=3e3){if(!Ft(e))return;let n=[...Pt(e),e];for(let e of n)try{process.kill(e,`SIGTERM`)}catch{}let r=Date.now()+t;for(;Date.now()<r;){if(!n.some(Ft))return;await new Promise(e=>setTimeout(e,100))}for(let e of n)try{Ft(e)&&process.kill(e,`SIGKILL`)}catch{}}function Lt(e,t){let n=v.join(M.tmpdir(),`proxy-auth-ext-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);T.mkdirSync(n,{recursive:!0});let r={manifest_version:3,name:`Proxy Auth Helper`,version:`1.0.0`,permissions:[`webRequest`,`webRequestAuthProvider`],host_permissions:[`<all_urls>`],background:{service_worker:`background.js`}},i=[`chrome.webRequest.onAuthRequired.addListener(`,` (details) => ({ authCredentials: { username: ${JSON.stringify(e)}, password: ${JSON.stringify(t)} } }),`,` { urls: ["<all_urls>"] },`,` ["blocking"]`,`);`,``].join(`
4
4
  `);return T.writeFileSync(v.join(n,`manifest.json`),JSON.stringify(r,null,2)),T.writeFileSync(v.join(n,`background.js`),i),n}function Rt(e){let t=[`--proxy-server=${e.server}`];return e.bypass&&t.push(`--proxy-bypass-list=${e.bypass}`),t}const zt=`x-browse-owner`,Bt=new we;function Vt(e,t){return e?Bt.run(e,t):t()}function Ht(){return Bt.getStore()}var H;const Ut=new Set([`docker`,`docker-vm`,`docker-chromium`,`docker-chrome-testing`,`docker-cloakbrowser`]),Wt=qe,Gt=Je,Kt=Xe,qt=Ze,Jt=4e3,Yt=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`,Xt=[`Current Session`,`Current Tabs`,`Last Session`,`Last Tabs`,`Sessions`],Zt=new Set([`Default`,`Guest Profile`,`System Profile`]);var Qt=class extends Error{constructor(e,t){super(`Browser launch for profile "${e}" is busy: another launch held the profile for more than ${t}ms. Retry shortly, or use a different profile to run in parallel.`),this.profileName=e,this.timeoutMs=t,this.name=`BrowserLaunchBusyError`}};let $t=class{static{H=this}static MAX_ACTIVE_BROWSERS=15;static MAX_PAGES_PER_BROWSER=15;static chromeForTestingLaunchQueue=Promise.resolve();browsers=new Map;closingExtensionRecordings=new Map;profileToBrowserId=new Map;browserIdCounter=0;profileLaunchTails=new Map;static DEFAULT_PROFILE_LAUNCH_TIMEOUT_MS=12e4;constructor(e,t,n,r,i,a,o,s){this.profileService=e,this.pageRegistry=t,this.lockManager=n,this.chromeForTesting=r,this.pageMonitor=i,this.webSocketHub=a,this.extensionSessionRegistry=o,this.extensionTaskQueue=s}getBrowserByProfile(e){let t=this.profileToBrowserId.get(e);if(t){let n=this.browsers.get(t);if(n){if(this.isBrowserAlive(n))return n;this.cleanupStaleBrowser(t,e)}else this.profileToBrowserId.delete(e)}}async launch(e={}){return e.profileName?this.withProfileLaunchLock(e.profileName,()=>this.launchInternal(e)):this.launchInternal(e)}async launchInternal(e={}){let{profileName:t,browserType:n=`chromium`,headless:r=!0,onPageClose:i,baseURL:a,recordVideo:o,proxy:s,browserId:c}=e;if(t){let e=this.getBrowserByProfile(t);if(e){let{pageId:t,page:n}=await this.newPage(e.id,i);return{browserInstance:e,pageId:t,page:n}}}await this.enforceMaxBrowserLimit();let l=c||(t?`browser-${t}`:`browser-${++this.browserIdCounter}`);if(c&&this.browsers.has(c))throw Error(`Browser ID "${c}" is already in use`);let u=this.getBrowserType(n),d=t?await this.profileService.get(t):null;t&&!d&&(d=await this.profileService.create({name:t,browserType:n}));let f=s?{proxy:{server:s.server,username:s.username,password:s.password,bypass:s.bypass}}:{},p=process.platform===`linux`&&typeof process.getuid==`function`&&process.getuid()===0,m=n===`chromium`?process.env.CHROME_BIN||process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH:void 0,h=process.env.BROWSE_TOOL_DISABLE_GPU===`true`,g=n===`chromium`?[...h?[`--disable-gpu`,`--disable-software-rasterizer`,`--disable-dev-shm-usage`,`--disable-features=VizDisplayCompositor`]:[],...p?[`--no-sandbox`,`--disable-setuid-sandbox`]:[]]:[],_=n===`chromium`?{...g.length>0?{args:g}:{},...m?{executablePath:m}:{}}:{},v;if(!r&&n===`chromium`)try{v=await u.launch({headless:r,...m?{}:{channel:`chrome`},..._,...f})}catch{v=await u.launch({headless:r,..._,...f})}else v=await u.launch({headless:r,..._,...f});let y=this.buildContextOptions(d,a,o);if(d&&t){let e=await this.profileService.loadStorageState(t);e&&(y.storageState=e)}let b=await v.newContext(y),x=await b.newPage(),S=new Date,C={id:l,mode:`playwright`,browser:v,context:b,profileName:t,pageIds:new Set,currentPageId:null,createdAt:S,lastAccessedAt:S};this.browsers.set(l,C),v.on(`disconnected`,()=>{this.cleanupStaleBrowser(l,t)}),t&&this.profileToBrowserId.set(t,l);let w=await this.pageRegistry.register({page:x,browser:v,context:b,browserId:l,profileName:t,onClose:e=>{if(C.pageIds.delete(e),C.currentPageId===e){let e=Array.from(C.pageIds);C.currentPageId=e.length>0?e[0]:null}i?.(e),C.pageIds.size===0&&this.closeBrowser(l).catch(()=>{})}});return this.pageMonitor?.startMonitoring(w,x),C.pageIds.add(w),C.currentPageId=w,{browserInstance:C,pageId:w,page:x}}async launchWithExtension(e={}){return e.profileName?this.withProfileLaunchLock(e.profileName,()=>this.launchWithExtensionInternal(e)):this.launchWithExtensionInternal(e)}async launchWithExtensionInternal(e={}){let{profileName:t,command:n,commandArgs:r=[],browserId:i,mode:a=`extension`}=e;if(t){let e=this.getBrowserByProfile(t);if(e)if(!this.canReuseExtensionBrowser(e))await this.closeBrowser(e.id).catch(()=>{this.cleanupStaleBrowser(e.id,t)});else{let t=this.getTrackedExtensionPageId(e);return this.recordBrowserActivity(e.id,t),{browserInstance:e,pageId:t,process:{pid:e.pid}}}}await this.enforceMaxBrowserLimit();let o=this.resolveExtensionPath(e.extensionPath),s=v.join(o,`manifest.json`);if(!T.existsSync(s))throw Error(`Extension manifest not found at ${s}. Please build the extension first: pnpm build:extension`);let c=i||(t?`browser-${t}`:`browser-${++this.browserIdCounter}`);if(i&&this.browsers.has(i))throw Error(`Browser ID "${i}" is already in use`);t&&(await this.profileService.get(t)||await this.profileService.create({name:t,browserType:`chromium`}));let l=this.createUserDataDir(t);l&&await this.lockManager.ensureAvailable(l);let u=await this.resolveVmRuntime({command:n,launchMode:a,vmDockerBrowser:e.vmDockerBrowser,extensionHostPath:o,requestedRuntimeExtensionPath:e.extensionPath,userDataDir:l});if(u.kind===`docker`)try{this.ensureDockerWritableUserDataDir(l)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to prepare Docker user-data directory "${l}": ${t}`,{cause:e})}let d=[u.extensionPath],f=[];if(u.kind===`docker`&&f.push(...this.createDockerVolumeArgs(o,u.extensionPath,!0)),e.proxy?.username&&e.proxy?.password){let t=Lt(e.proxy.username,e.proxy.password);if(u.kind===`docker`){let e=`${u.docker.extensionPathInContainer}-proxy-auth-${Date.now()}`;d.push(e),f.push(...this.createDockerVolumeArgs(t,e,!0))}else d.push(t)}let p=d.join(`,`),m=process.env.BROWSE_TOOL_AUTO_SELECT_TAB_CAPTURE_SOURCE_TITLE?.trim(),h=process.env.BROWSE_TOOL_AUTO_SELECT_DESKTOP_CAPTURE_SOURCE?.trim(),g=process.env.BROWSE_TOOL_USE_FAKE_UI_FOR_MEDIA_STREAM===`1`,_=u.kind===`docker`||process.platform===`linux`&&typeof process.getuid==`function`&&process.getuid()===0,y=[`--user-data-dir=${u.userDataDir}`,`--load-extension=${p}`,`--disable-extensions-except=${p}`,`--enable-extensions`,`--auto-accept-this-tab-capture`,...g?[`--use-fake-ui-for-media-stream`]:[],...m?[`--auto-select-tab-capture-source-by-title=${m}`]:[],...h?[`--auto-select-desktop-capture-source=${h}`]:[],`--disable-blink-features=AutomationControlled`,`--disable-infobars`,`--no-first-run`,`--no-default-browser-check`,`--disable-background-timer-throttling`,`--disable-backgrounding-occluded-windows`,`--disable-renderer-backgrounding`,`--disable-component-update`,`--disable-features=TranslateUI`,`--disable-ipc-flooding-protection`,...u.kind===`docker`?[`--disable-gpu`]:[],`--silent-debugger-extension-api`,`--password-store=basic`,`--use-mock-keychain`,`--disable-dev-shm-usage`,`--webrtc-ip-handling-policy=disable_non_proxied_udp`,`--enforce-webrtc-ip-permission-check`,`--lang=${process.env.LANG?.split(`.`)[0]?.replace(`_`,`-`)||`en-US`}`,..._?[`--no-sandbox`,`--disable-setuid-sandbox`]:[],`--window-size=1920,1080`,...e.proxy?Rt(e.proxy):[],e.url??`about:blank`],b=e=>{try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`||t.protocol===`file:`}catch{return!1}},x=e=>{if(!b(e))return e;try{let t=new URL(e),n=t.pathname.endsWith(`/`)&&t.pathname!==`/`?t.pathname.slice(0,-1):t.pathname;return`${t.protocol}//${t.host}${n}${t.search}`}catch{return e}},S=x(e.url??`about:blank`),C=a===`vm`&&r.length>0?[...y,...r.filter(e=>b(e)?x(e)!==S:!0)]:r.length>0?r:y,w=u.kind===`docker`?{TZ:Intl.DateTimeFormat().resolvedOptions().timeZone,...e.dockerEnv}:e.dockerEnv,ee=u.kind===`docker`?[...this.buildDockerSpawnArgs(u.docker,l,f,e.dockerRunArgs,w,e.dockerEnableVnc),...C]:C,E,D=0,O;for(let n=1;n<=3;n+=1)try{let n=()=>this.startExtensionBrowser({browserId:c,launchMode:a,profileName:t,vmRuntime:u,spawnArgs:ee,recordVideo:e.recordVideo,startupDelayMs:e.startupDelayMs,launchUrl:e.url});return this.shouldSerializeChromeForTestingLaunch(u)?await this.runSerializedChromeForTestingLaunch(n):await n()}catch(e){if(E=e instanceof Error?e:Error(String(e)),!this.isRetryableExtensionLaunchError(E)||n===3){if(u.kind===`docker`&&(D>0||O)){let e=[D>0?`Docker retry cleanup attempts: ${D}.`:``,O?`Last Docker retry cleanup: ${O}.`:``].filter(Boolean).join(` `);E.message=`${E.message} ${e}`.trim()}throw E}u.kind===`docker`&&(D+=1,O=await this.prepareDockerRetryUserDataDir(l)),await this.delay(250*n)}throw E??Error(`Extension browser launch failed`)}async resolveVmRuntime(e){let{command:t,launchMode:n,vmDockerBrowser:r,extensionHostPath:i,requestedRuntimeExtensionPath:a,userDataDir:o}=e;if(n===`vm`&&this.isDockerVmAlias(t)){let e=this.resolveDockerRuntimeExtensionPath(a),t=process.env.PLAYWRIGHT_VM_DOCKER_USER_DATA_DIR||`/vm/user-data`,n=this.resolveDockerBrowserType(r),{image:i,browserBinary:o,platform:s}=this.resolveDockerImageConfig(n);return n===`chrome-for-testing`?await Ot({image:i,platform:s}):await Mt({image:i,platform:s}),{kind:`docker`,command:`docker`,extensionPath:e,userDataDir:t,docker:{image:i,browserBinary:o,platform:s,extensionPathInContainer:e,userDataDirInContainer:t}}}return{kind:`host`,command:await this.resolveLaunchCommand({command:t,launchMode:n,vmDockerBrowser:r}),extensionPath:i,userDataDir:o}}resolveDockerRuntimeExtensionPath(e){return e&&!T.existsSync(v.join(e,`manifest.json`))?e:process.env.PLAYWRIGHT_VM_DOCKER_EXTENSION_PATH||`/vm/extensions/playwright`}resolveDockerBrowserType(e){let t=process.env.PLAYWRIGHT_VM_DOCKER_BROWSER?.trim().toLowerCase();return t===`chrome-for-testing`||t===`cft`?`chrome-for-testing`:t===`cloakbrowser`||t===`cloak`?`cloakbrowser`:e||(process.env.PLAYWRIGHT_VM_DOCKER_IMAGE?`chrome-for-testing`:`cloakbrowser`)}resolveDockerImageConfig(e){return e===`chrome-for-testing`?{image:process.env.PLAYWRIGHT_VM_DOCKER_IMAGE||Wt,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||Gt,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||Ye}:{image:process.env.PLAYWRIGHT_VM_DOCKER_IMAGE||Kt,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||qt,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||Qe}}createDockerVolumeArgs(e,t,n){return[`-v`,`${v.resolve(e)}:${t}${n?`:ro`:``}`]}ensureDockerWritableUserDataDir(e){T.mkdirSync(e,{recursive:!0,mode:511});let t=[e];for(;t.length>0;){let e=t.pop();if(!e)continue;T.chmodSync(e,511);let n=T.readdirSync(e,{withFileTypes:!0});for(let r of n){let n=v.join(e,r.name);T.chmodSync(n,r.isDirectory()?511:438),r.isDirectory()&&t.push(n)}}}buildDockerSpawnArgs(e,t,n,r,i,a){let o=[`run`,`--rm`,`--init`,`--shm-size`,process.env.PLAYWRIGHT_VM_DOCKER_SHM_SIZE||`2g`];return e.platform&&o.push(`--platform`,e.platform),process.env.PLAYWRIGHT_VM_DOCKER_NETWORK&&o.push(`--network`,process.env.PLAYWRIGHT_VM_DOCKER_NETWORK),process.platform===`linux`&&o.push(`--add-host`,`host.docker.internal:host-gateway`),o.push(...n),o.push(...this.createDockerVolumeArgs(t,e.userDataDirInContainer,!1)),o.push(...this.createDockerEnvArgs(i)),o.push(...this.parseDockerRunArgs(process.env.PLAYWRIGHT_VM_DOCKER_RUN_ARGS)),o.push(...r??[]),a?(o.push(`--entrypoint`,`/bin/bash`,e.image,`-lc`,this.buildDockerBrowserLaunchScript(e.browserBinary,{enableVnc:!0}),`_`),o):(o.push(`--entrypoint`,`/bin/bash`,e.image,`-lc`,this.buildDockerBrowserLaunchScript(e.browserBinary,{enableVnc:!1}),`_`),o)}buildDockerBrowserLaunchScript(e,t){let n=[`set -euo pipefail`,`export XDG_RUNTIME_DIR="/tmp/runtime-browse"`,`mkdir -p "$XDG_RUNTIME_DIR" "$XDG_RUNTIME_DIR/pulse"`,`chmod 700 "$XDG_RUNTIME_DIR" || true`,`command -v dbus-daemon >/dev/null 2>&1 && export DBUS_SESSION_BUS_ADDRESS="$(dbus-daemon --session --fork --print-address --nopidfile)" || true`,`if command -v pulseaudio >/dev/null 2>&1; then pulseaudio --check >/dev/null 2>&1 || pulseaudio --daemonize=yes --exit-idle-time=-1 --disable-shm=true --log-target=stderr >/tmp/pulseaudio.log 2>&1; export PULSE_SERVER="unix:$XDG_RUNTIME_DIR/pulse/native"; fi`,`Xvfb :99 -screen 0 1920x1080x24 -nolisten tcp >/tmp/xvfb.log 2>&1 &`,`if command -v xdpyinfo >/dev/null 2>&1; then for i in $(seq 1 30); do xdpyinfo -display :99 >/dev/null 2>&1 && break; sleep 0.2; done; else sleep 1; fi`,`export DISPLAY=:99`,`BROWSER_BIN=${JSON.stringify(e)}`,`exec "$BROWSER_BIN" "$@"`];return t.enableVnc&&n.splice(n.length-2,0,'if [ -n "${SE_VNC_PASSWORD:-}" ]; then',' x11vnc -storepasswd "${SE_VNC_PASSWORD}" /tmp/vnc-passwd >/dev/null 2>&1',` X11VNC_AUTH="-rfbauth /tmp/vnc-passwd"`,`else`,` X11VNC_AUTH=""`,`fi`,'x11vnc -forever -shared -rfbport "${SE_VNC_PORT:-5900}" -display :99 $X11VNC_AUTH >/tmp/x11vnc.log 2>&1 &',`if command -v websockify >/dev/null 2>&1; then`,' websockify --web /opt/bin/noVNC "${SE_NO_VNC_PORT:-7900}" "localhost:${SE_VNC_PORT:-5900}" >/tmp/novnc.log 2>&1 &',`fi`),n.join(`
@@ -46,8 +46,8 @@ import{a as e,c as t,h as n,p as r,s as i,t as a,v as o,y as s}from"./playwright
46
46
  return null;
47
47
  };
48
48
  `}};let hn=class{sessions=new Map;browserIdToSessionId=new Map;sessionIdCounter=0;defaultStaleThresholdMs=6e4;register(e){let t=this.browserIdToSessionId.get(e.browserId);t&&(this.sessions.delete(t),this.browserIdToSessionId.delete(e.browserId));let n=`session-${++this.sessionIdCounter}-${Date.now()}`,r=new Date,i={id:n,browserId:e.browserId,tabId:e.tabId,currentUrl:e.url,createdAt:r,lastHeartbeatAt:r,controlMode:`manual`,handoffRequested:!1,metadata:e.metadata};return this.sessions.set(n,i),this.browserIdToSessionId.set(e.browserId,n),i}heartbeat(e){let t=this.sessions.get(e.sessionId);if(t)return t.lastHeartbeatAt=new Date,e.tabId!==void 0&&(t.tabId=e.tabId),e.url!==void 0&&(t.currentUrl=e.url),e.state&&(t.metadata={...t.metadata,...e.state}),t}requestHandoff(e){let t=this.sessions.get(e.sessionId);if(t)return t.handoffRequested=!0,e.pageState&&(t.currentUrl=e.pageState.url,t.metadata={...t.metadata,handoffReason:e.reason,handoffPageState:e.pageState}),t}acknowledgeHandoff(e){let t=this.sessions.get(e);if(!(!t||!t.handoffRequested))return t.handoffRequested=!1,t.controlMode=`ai`,t.activeSpecPath=void 0,t}getSession(e){return this.sessions.get(e)}getSessionByBrowserId(e){let t=this.browserIdToSessionId.get(e);if(t)return this.sessions.get(t)}listSessions(){return Array.from(this.sessions.values())}removeSession(e){let t=this.sessions.get(e);return t?(this.browserIdToSessionId.delete(t.browserId),this.sessions.delete(e),!0):!1}setControlMode(e,t){let n=this.sessions.get(e);if(n)return n.controlMode=t,n}setActiveSpec(e,t){let n=this.sessions.get(e);if(n)return n.activeSpecPath=t,t&&(n.controlMode=`spec`),n}cleanupStaleSessions(e=this.defaultStaleThresholdMs){let t=Date.now(),n=0;for(let[r,i]of this.sessions)t-i.lastHeartbeatAt.getTime()>e&&(this.browserIdToSessionId.delete(i.browserId),this.sessions.delete(r),n++);return n}};hn=B([h()],hn);let gn=class{constructor(e,t,n){this.bundler=e,this.pageProxy=t,this.sessionRegistry=n}async loadSpec(e){let n=await this.bundler.bundle(e),a=process,o=a.__pw_initiator__;try{return t(e),a.__pw_initiator__=void 0,await import(`${n.outputPath}?t=${Date.now()}`),i()}catch(t){throw r(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{a.__pw_initiator__=o,await n.cleanup()}}async executeSpec(e){let{specPath:t,sessionId:r,testFilter:i,specArgs:a,onHandoff:o}=e,s=Date.now(),c=[],l=0,u=0,d=!1,f=new Set,p=new Set;this.sessionRegistry.setActiveSpec(r,t);try{let e=await this.loadSpec(t),m=i?this.filterTests(e.allTests,i):e.allTests,h={page:this.pageProxy,requestHandoff:async e=>{if(!this.sessionRegistry.getSession(r))throw Error(`Session ${r} not found`);let t=await this.pageProxy.getSnapshot(),n=this.pageProxy.url();try{n=await this.pageProxy.currentUrlAsync()}catch{}this.sessionRegistry.requestHandoff({sessionId:r,reason:e,pageState:{url:n,snapshot:t}}),d=!0,o&&await o(r)},specArgs:a};for(let e of m){let{runtimeUse:t,unsupportedKeys:i,fixtureOverrides:a}=n(e.config.use);i.length>0&&f.add(`test.use() ignores unsupported fixture keys in extension mode: ${i.join(`, `)}`),Object.keys(t).length>0&&f.add(`test.use() runtime browser config is not applied in extension mode: ${Object.keys(t).join(`, `)}`);let o=e.serialScopeId,s=o!==void 0&&p.has(o);if(e.skip||s){c.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:0}),l++;continue}let d=this.sessionRegistry.getSession(r);if(d?.handoffRequested)break;let m=Date.now();try{await e.fn({...h,...a}),c.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-m,handoffTriggered:d?.handoffRequested}),l++}catch(t){let n=t instanceof Error?t.message:String(t);c.push({title:e.title,fullTitle:e.fullTitle,passed:!1,error:n,duration:Date.now()-m}),u++,o!==void 0&&p.add(o)}}return{specPath:t,totalTests:m.length,passed:l,failed:u,testResults:c,success:u===0,duration:Date.now()-s,handoffOccurred:d,sessionId:r,warnings:f.size>0?[...f]:void 0}}catch(e){return{specPath:t,totalTests:0,passed:0,failed:1,testResults:[{title:`Spec Loading`,fullTitle:`Failed to load: ${t}`,passed:!1,error:e instanceof Error?e.message:String(e),duration:Date.now()-s}],success:!1,duration:Date.now()-s,handoffOccurred:!1,sessionId:r,warnings:f.size>0?[...f]:void 0}}finally{this.sessionRegistry.setActiveSpec(r,void 0)}}filterTests(e,t){let n=[...e];if(t.onlyMarked){let e=n.filter(e=>e.only);e.length>0&&(n=e)}if(t.testName&&(n=n.filter(e=>e.title===t.testName)),t.testPattern){let e=new RegExp(t.testPattern,`i`);n=n.filter(t=>e.test(t.fullTitle))}if(t.describeFilter){let e=new RegExp(t.describeFilter,`i`);n=n.filter(t=>e.test(t.fullTitle))}return n}};gn=B([h(),z(0,m(L.SpecBundlerService)),z(1,m(L.ExtensionPageProxy)),z(2,m(L.ExtensionSessionRegistry)),R(`design:paramtypes`,[Object,Object,Object])],gn);const _n=`browse-tool-http`,vn=[`pnpm-workspace.yaml`,`nx.json`,`.git`],yn=new Set([`127.0.0.1`,`localhost`,`::1`,`0.0.0.0`]);function bn(e){return e===`1`||e===`true`}function xn(e){return Array.isArray(e)?JSON.stringify(e):typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:JSON.stringify(e)}function Sn(e){if(!e)return;let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=xn(r));return Object.keys(t).length>0?t:void 0}function Cn(e){if(!e)return;let t={};for(let n of e.split(`,`)){let[e,...r]=n.split(`=`),i=e?.trim(),a=r.join(`=`).trim();!i||!a||(t[i]=a)}return Object.keys(t).length>0?t:void 0}function wn(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>0?t:void 0}function Tn(e,t){let n=e.endsWith(`/`)?e:`${e}/`;return n.endsWith(`/v1/${t}/`)||n.endsWith(`/v1/${t}`)?e:new URL(`v1/${t}`,n).toString()}function En(e,t){return(t===`traces`?e.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:e.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)||(e.OTEL_EXPORTER_OTLP_ENDPOINT?Tn(e.OTEL_EXPORTER_OTLP_ENDPOINT,t):void 0)}function Dn(e){return yn.has(e)}function On(e=process.cwd()){let t=v.resolve(e);for(;;){for(let e of vn)if(D(v.join(t,e)))return t;let e=v.dirname(t);if(e===t)return process.cwd();t=e}}function kn(e,t){try{let n=new URL(e),r=new URL(t);return!Dn(n.hostname)||Dn(r.hostname)||(n.hostname=r.hostname),n.toString()}catch{return e}}async function An(e,t,n){let r=await Ee({env:e,workspaceRoot:On(),healthCheck:n?.healthCheck});return t===`traces`?r.tracesEndpoint:r.logsEndpoint}async function jn(e,t,n,r){let i=t===`traces`?e.BROWSE_TOOL_OTEL_BROWSER_TRACES_ENDPOINT:e.BROWSE_TOOL_OTEL_BROWSER_LOGS_ENDPOINT;if(i)return kn(i,n);let a=e.BROWSE_TOOL_OTEL_BROWSER_ENDPOINT;if(a)return kn(Tn(a,t),n);let o=await An(e,t,r);return o?kn(o,n):void 0}async function Mn(e,t,n){let r=e.BROWSE_TOOL_OTEL_BROWSER_SERVICE_NAME??`${e.OTEL_SERVICE_NAME??_n}-extension`,[i,a]=await Promise.all([jn(e,`traces`,t,n),jn(e,`logs`,t,n)]);return{enabled:!!(i||a),tracesEndpoint:i,logsEndpoint:a,serviceName:r}}function Nn(e,t){let n=t===`traces`?e.OTEL_EXPORTER_OTLP_TRACES_HEADERS:e.OTEL_EXPORTER_OTLP_LOGS_HEADERS,r=t===`traces`?e.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT:e.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT;return{url:En(e,t),headers:Cn(n??e.OTEL_EXPORTER_OTLP_HEADERS),timeoutMillis:wn(r??e.OTEL_EXPORTER_OTLP_TIMEOUT)}}function Pn(e){return e instanceof Error?e.message:String(e)}function Fn(e){if(e instanceof Error)return Sn({"exception.type":e.name,"exception.message":e.message,"exception.stacktrace":e.stack})}let G=class{contextStore=new we;tracesEnabled;logsEnabled;tracerProvider;tracer;logSinkReady;delegateReady;delegateHandle=null;constructor(e={}){let t=e.env??process.env,n=bn(t.OTEL_SDK_DISABLED),r=bn(t.BROWSE_TOOL_OTEL_ENABLED),i=Nn(t,`traces`),a=!!e.traceExporter;this.tracesEnabled=!n&&(a||r||!!i.url),this.logsEnabled=!n;let o=e.serviceName??t.BROWSE_TOOL_OTEL_SERVICE_NAME??t.OTEL_SERVICE_NAME??_n,s=e.serviceVersion??t.npm_package_version,c={"service.name":o};s&&(c[`service.version`]=s);let l=Me(c);if(this.tracesEnabled&&a){let t=e.traceExporter??new je(i);this.tracerProvider=new Pe({resource:l,spanProcessors:[e.traceExporter?new Fe(t):new Ne(t)]})}this.tracer=this.tracerProvider?.getTracer(o,s)??Ae.getTracer(o,s),this.delegateReady=!n&&!a?Te({env:t,serviceName:o,serviceVersion:s,workspaceRoot:On(),enableTraces:!0,enableLogs:!0}).then(e=>(this.delegateHandle=e,e)).catch(()=>null):Promise.resolve(null),this.logSinkReady=this.logsEnabled&&a?Te({env:t,serviceName:o,serviceVersion:s,workspaceRoot:On(),enableTraces:!1,enableLogs:!0}).catch(()=>null):this.delegateReady}isEnabled(){return this.tracesEnabled||this.logsEnabled}getActiveTraceContext(){let e=this.delegateHandle?.getTraceContext();if(e?.traceId||e?.spanId)return e;let t=this.contextStore.getStore();if(!t)return{};let n=Ae.getSpan(t);if(!n)return{};let r=n.spanContext();return{traceId:r.traceId,spanId:r.spanId}}async runInSpan(e,t,n){let r=await this.delegateReady;if(r)return await r.runInSpan(e,t,n);if(!this.tracesEnabled||!this.tracerProvider)return await n(void 0);let i=this.contextStore.getStore()??ke.active(),a=this.tracer.startSpan(e,{kind:t.kind??De.INTERNAL,attributes:Sn(t.attributes)},i),o=Ae.setSpan(i,a);return await this.contextStore.run(o,async()=>{try{return await n(a)}catch(e){throw a.recordException(e instanceof Error?e:Error(Pn(e))),a.setStatus({code:Oe.ERROR,message:Pn(e)}),e}finally{a.end()}})}log(e,t,n={}){this.logsEnabled&&this.logSinkReady.then(r=>{if(!r)return;let i=n.context??this.contextStore.getStore()??ke.active(),a=Fn(n.exception);r.logger[e](t,{attributes:Sn({...n.attributes,...a}),context:i,exception:n.exception})})}async forceFlush(){let[e,t]=await Promise.all([this.logSinkReady,this.delegateReady]);await Promise.allSettled([this.tracerProvider?.forceFlush(),e?.flush(),t?.flush()])}async shutdown(){let[e,t]=await Promise.all([this.logSinkReady,this.delegateReady]);await Promise.allSettled([this.tracerProvider?.shutdown(),e?.shutdown(),t?.shutdown()])}};G=B([h(),R(`design:paramtypes`,[Object])],G);let In=class{pendingTasks=new Map;taskQueue=[];lastPollAt;lastResultAt;maxQueueSize=100;defaultTimeoutMs=V;constructor(e,t=new G){this.webSocketHub=e,this.telemetry=t}async queueTask(e,t,n=this.defaultTimeoutMs,r){if(this.pendingTasks.size>=this.maxQueueSize)throw Error(`Task queue full (max ${this.maxQueueSize} tasks)`);let i=r??t.browserId,a={id:crypto.randomUUID(),tool:e,arguments:t,createdAt:new Date,timeoutMs:n,browserId:i,pageId:typeof t.pageId==`string`?t.pageId:void 0,telemetry:this.getTaskTelemetryContext()};return new Promise((e,t)=>{let r=setTimeout(()=>{this.pendingTasks.delete(a.id);let e=this.taskQueue.findIndex(e=>e.id===a.id);e!==-1&&this.taskQueue.splice(e,1),t(Error(`Task ${a.id} timed out after ${n}ms`))},n);if(this.pendingTasks.set(a.id,{task:a,resolve:e,reject:t,timeoutId:r}),this.webSocketHub){let e=i?this.pushTaskViaWebSocket(a,i):!1;if(e||=this.pushTaskToAnyConnection(a),e)return}this.taskQueue.push(a)})}pushTaskToAnyConnection(e){if(!this.webSocketHub)return!1;let t=this.webSocketHub.getStats();if(t.totalConnections===0)return!1;let n=t.connections[0];return n?this.pushTaskViaWebSocket(e,n.browserId):!1}pushTaskViaWebSocket(e,t){if(!this.webSocketHub)return!1;let n={type:`task:push`,id:crypto.randomUUID(),payload:{taskId:e.id,tool:e.tool,arguments:e.arguments,telemetry:e.telemetry}};return this.webSocketHub.pushTask(t,n)}getNextTask(){return this.lastPollAt=new Date,this.taskQueue.shift()}submitResult(e){this.lastResultAt=new Date;let t=this.pendingTasks.get(e.taskId);if(t)return clearTimeout(t.timeoutId),this.pendingTasks.delete(e.taskId),t.resolve(e),t.task}getConnectionStatus(){let e=new Date,t=this.lastPollAt?e.getTime()-this.lastPollAt.getTime()<5e3:!1,n=this.webSocketHub?this.webSocketHub.getStats().totalConnections>0:!1;return{connected:t||n,lastPollAt:this.lastPollAt,lastResultAt:this.lastResultAt,pendingTasks:this.pendingTasks.size}}clearAllTasks(e){for(let[t,n]of this.pendingTasks)clearTimeout(n.timeoutId),n.reject(Error(e)),this.pendingTasks.delete(t);this.taskQueue.length=0}get queueSize(){return this.taskQueue.length}get pendingCount(){return this.pendingTasks.size}getTaskTelemetryContext(){let e=this.telemetry.getActiveTraceContext();if(!(!e.traceId&&!e.spanId))return{traceId:e.traceId,parentSpanId:e.spanId}}};In=B([h(),z(0,m(L.WebSocketHub)),z(0,g()),z(1,m(L.TelemetryService)),z(1,g()),R(`design:paramtypes`,[Object,Object])],In);const Ln=new Set(`browser_navigate.browser_go_back.browser_go_forward.browser_reload.browser_click.browser_fill.browser_type.browser_upload_file.browser_select.browser_hover.browser_drag.browser_press_key.browser_inspect_element.browser_resolve_locator.browser_pdf.browser_screenshot.browser_snapshot.browser_list_pages.browser_new_page.browser_select_page.browser_close_page.browser_wait_for.browser_evaluate_script.browser_resize_page.browser_handle_dialog.browser_emulate.browser_list_network_requests.browser_get_network_request.browser_list_console_messages.browser_expect`.split(`.`)),Rn=new Set([`browser_launch`,`browser_list_profiles`,`browser_create_profile`,`browser_delete_profile`,`browser_save_profile_state`,`browser_start_trace`,`browser_stop_trace`,`run_spec`,`discover_specs`]);let zn=class{constructor(e){this.taskQueue=e}isToolSupported(e){return Ln.has(e)}isToolNotApplicable(e){return Rn.has(e)}async executeTool(e,t){if(this.isToolNotApplicable(e))return{content:[{type:`text`,text:`Tool "${e}" is not available in Chrome extension mode. This tool requires Playwright and cannot be run through the extension.`}],isError:!0};if(!this.isToolSupported(e))return{content:[{type:`text`,text:`Tool "${e}" is not yet implemented in Chrome extension mode.`}],isError:!0};if(!this.taskQueue.getConnectionStatus().connected)return{content:[{type:`text`,text:`Chrome extension is not connected. Please ensure the extension is running and connected to the server.`}],isError:!0};try{let n=await this.taskQueue.queueTask(e,t);return n.success?n.result?n.result:{content:[{type:`text`,text:`Tool executed successfully`}]}:{content:[{type:`text`,text:n.error||n.result?.content?.[0]?.text||`Unknown error from Chrome extension`}],isError:!0}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}getSupportedTools(){return Array.from(Ln)}getNotApplicableTools(){return Array.from(Rn)}};zn=B([h(),z(0,m(L.ExtensionTaskQueue)),R(`design:paramtypes`,[Object])],zn);const Bn=l.min,Vn=`PLAYWRIGHT_PORT`;function K(){return process.env.PLAYWRIGHT_HOST?.trim()||`localhost`}function Hn(){let e=process.env[Vn];if(!e)return Bn;let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0||t>65535)throw Error(`Invalid ${Vn} value: ${e}`);return t}function q(e=void 0,t){let n=typeof e==`string`?e:e?.host??K(),r=e&&typeof e!=`string`?e.port:t??Hn();return`http://${n===`0.0.0.0`?`127.0.0.1`:n===`::`?`::1`:n}:${r}`}let Un=class{baseUrl;defaultTimeout=V;constructor(){this.baseUrl=q()}setBaseUrl(e){this.baseUrl=e}getBaseUrl(){return this.baseUrl}async execute(e,t={}){let n=`${this.baseUrl}/execute`,r=new AbortController,i=setTimeout(()=>r.abort(),this.defaultTimeout);try{let a=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tool:e,arguments:t}),signal:r.signal});if(clearTimeout(i),!a.ok){let e=await a.text();return{content:[{type:`text`,text:`HTTP error ${a.status}: ${e}`}],isError:!0}}let o=await a.json();return o.success?o.result||{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:o.error||o.result?.content?.[0]?.text||`Unknown error`}],isError:!0}}catch(e){return clearTimeout(i),e instanceof Error&&e.name===`AbortError`?{content:[{type:`text`,text:`Request timed out after ${this.defaultTimeout}ms`}],isError:!0}:{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}async isHealthy(){try{let e=await fetch(`${this.baseUrl}/health`,{headers:{"Content-Type":`application/json`}});return e.ok?(await e.json()).status===`healthy`:!1}catch{return!1}}};Un=B([h(),R(`design:paramtypes`,[])],Un);let Wn=class{defaultTimeout=5e3;async check(e,t=this.defaultTimeout){try{let n=`${q(K(),e)}/health`,r=new AbortController,i=setTimeout(()=>r.abort(),t);try{let t=await fetch(n,{signal:r.signal,headers:{"Content-Type":`application/json`}});if(clearTimeout(i),!t.ok)return{healthy:!1,error:`HTTP error ${t.status}: ${t.statusText}`};let a=await t.json();return a.status===`healthy`?{healthy:!0,port:e,serviceName:a.service,browserCount:a.browsers?.count}:{healthy:!1,error:`Unexpected health status: ${a.status||`unknown`}`}}catch(e){return clearTimeout(i),e instanceof Error?e.name===`AbortError`?{healthy:!1,error:`Health check timed out after ${t}ms`}:`code`in e&&e.code===`ECONNREFUSED`?{healthy:!1,error:`Connection refused - server not running`}:{healthy:!1,error:`Network error: ${e.message}`}:{healthy:!1,error:`Unknown error: ${String(e)}`}}}catch(e){return{healthy:!1,error:e instanceof Error?e.message:String(e)}}}};Wn=B([h()],Wn);var Gn;const Kn=_e(import.meta.url),qn=_.dirname(Kn),Jn=[`pnpm-workspace.yaml`,`nx.json`,`.git`],J=`browse-tool-http`,Yn=`tool`,Xn=`http-serve`,Zn=`ESRCH`,Qn=`node`,$n=`cli.mjs`,er=`cli.ts`,Y=`[HttpServerManager]`;var tr=class extends Error{code=`HTTP_SERVER_PORT_ERROR`;recovery=`Check if other services are occupying ports in the configured range.`;constructor(e,t){super(e,t),this.name=`HttpServerPortError`}},nr=class extends Error{code=`HTTP_SERVER_STARTUP_ERROR`;recovery=`Check server logs, ensure CLI binary exists, and verify port availability.`;constructor(e,t){super(e,t),this.name=`HttpServerStartupError`}},rr=class extends Error{code=`HTTP_SERVER_STOP_ERROR`;recovery=`Manually kill the process and clean up the port registry.`;causes;constructor(e,t=[],n){super(e,n),this.name=`HttpServerStopError`,this.causes=t}};async function ir(e){try{return await re.access(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}const ar=Ie(he);async function or(e){try{let{stdout:t}=await ar(`git`,[`rev-parse`,`--git-common-dir`],{cwd:e}),n=t.trim(),r=_.isAbsolute(n)?n:_.join(e,n),i=_.dirname(r);return i===e?null:i}catch{return null}}async function sr(e=process.cwd()){let t=_.resolve(e);for(;;){for(let e of Jn)if(await ir(_.join(t,e)))return t;let e=_.dirname(t);if(e===t)return process.cwd();t=e}}let cr=class{repositoryPath;environment;host;constructor(e,t,n=new d(process.env.PROCESS_REGISTRY_PATH)){this.healthCheck=e,this.portRegistry=t,this.processRegistry=n,this.environment=process.env.NODE_ENV||`development`,this.host=K()}async getRepositoryPath(){return this.repositoryPath||=await sr(process.cwd()),this.repositoryPath}buildErrorStatus(e){return{running:!1,error:e instanceof Error?e.message:String(e),errorCode:e instanceof Error&&`code`in e?e.code:void 0,errorRecovery:e instanceof Error&&`recovery`in e?e.recovery:void 0,errorCause:e instanceof Error&&e.cause instanceof Error?e.cause.message:void 0}}async listServiceEntries(){let e=await this.getRepositoryPath(),t={serviceName:J,serviceType:Yn,environment:this.environment},n=await this.portRegistry.listAllocations({...t,repositoryPath:e});if(n.length>0)return n;let r=await or(e);if(r){let e=await this.portRegistry.listAllocations({...t,repositoryPath:r});if(e.length>0)return e}return this.portRegistry.listAllocations(t)}async releaseEntry(e){if(e.pid){let t=await this.processRegistry.releaseProcess({repositoryPath:e.repositoryPath,serviceName:J,serviceType:`service`,environment:e.environment,pid:e.pid,kill:!1,releasePort:!1});!t.success&&!t.error?.includes(`No matching process entry`)&&console.error(`${Y} Failed to release process entry for port ${e.port}: ${t.error}`)}let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:J,serviceType:Yn,environment:e.environment,pid:e.pid});!t.success&&!t.error?.includes(`No matching registry entry`)&&console.error(`${Y} Failed to release entry for port ${e.port}: ${t.error}`)}async discoverHealthyService(){let e=await this.listServiceEntries();for(let t of e)try{let e=await this.healthCheck.check(t.port);if(e.healthy&&e.serviceName===J)return{entry:t,browserCount:e.browserCount}}catch(e){console.error(`${Y} Health check threw for port ${t.port}: ${e instanceof Error?e.message:String(e)}`)}return null}async discoverHealthyServiceOnPort(e){let t=await this.findRegistrationByPort(e);if(!t)return null;try{let n=await this.healthCheck.check(e);if(n.healthy&&n.serviceName===J)return{entry:t,browserCount:n.browserCount}}catch(t){console.error(`${Y} Health check threw for requested port ${e}: ${t instanceof Error?t.message:String(t)}`)}return null}async cleanupStaleEntries(){let e=await this.listServiceEntries(),t=[];for(let n of e)try{let e;try{e=await this.healthCheck.check(n.port)}catch(e){console.error(`${Y} Health check threw for port ${n.port}, skipping cleanup: ${e instanceof Error?e.message:String(e)}`);continue}e.healthy&&e.serviceName===J||(n.pid&&await this.killProcess(n.pid),await this.releaseEntry(n))}catch(e){let r=`Failed to clean up entry on port ${n.port}: ${e instanceof Error?e.message:String(e)}`;console.error(`${Y} ${r}`),t.push(r)}t.length>0&&console.error(`${Y} ${t.length} entry cleanup failure(s) during stale entry removal`)}async findRegistrationByPort(e){return(await this.listServiceEntries()).find(t=>t.port===e)??null}async findAvailablePort(e,t={}){let n=await this.getRepositoryPath(),r=await this.portRegistry.findAvailablePort({repositoryPath:n,serviceName:J,serviceType:Yn,environment:this.environment,host:this.host,preferredPort:e,portRange:t.exactPort?{min:e,max:e}:void 0});if(!r.success||!r.port)throw new tr(`No available port found from ${e}: ${r.error}`);return r.port}async ensureExactPortAvailable(e){let t=await this.findAvailablePort(e,{exactPort:!0});if(t!==e)throw new tr(`Requested port ${e} is unavailable; next available port is ${t}`)}async resolveCliCommand(e){let t=_.resolve(qn,$n),n=_.resolve(qn,`..`,er),r=[`--port`,e.toString(),`--host`,this.host];if(await ir(t))return[Qn,[t,Xn,...r]];if(await ir(n))return[`bun`,[`run`,n,Xn,...r]];let i=_.resolve(qn,`..`,`..`),a=_.join(i,`dist`,$n),o=_.join(i,`src`,er);if(await ir(a))return[Qn,[a,Xn,...r]];if(await ir(o))return[`bun`,[`run`,o,Xn,...r]];throw new nr(`Cannot find CLI at ${t}, ${n}, ${a}, or ${o}`)}async startHttpServer(e){let[t,n]=await this.resolveCliCommand(e),r=P(t,n,{detached:!0,stdio:`ignore`,env:{...process.env,NODE_ENV:this.environment,PLAYWRIGHT_SKIP_DYNAMIC_TOOL_COMMANDS:`1`}}),i=await new Promise(e=>{let t=setTimeout(()=>{r.removeAllListeners(`error`),e(null)},100);r.once(`error`,n=>{clearTimeout(t),e(n)})});if(i)throw new nr(`Failed to spawn HTTP server process: ${i.message}`,{cause:i});r.unref();let a=r.pid;if(!a)throw new nr(`Failed to spawn HTTP server - no PID returned`);return a}async killProcess(e){try{process.kill(e,0),process.kill(e,`SIGTERM`)}catch(t){if((t instanceof Error&&`code`in t?t.code:void 0)===Zn){console.error(`${Y} Process ${e} does not exist — skipping kill`);return}throw t}await new Promise(e=>setTimeout(e,1e3));try{process.kill(e,0),process.kill(e,`SIGKILL`)}catch(t){if((t instanceof Error&&`code`in t?t.code:void 0)===Zn){console.error(`${Y} Process ${e} exited after SIGTERM`);return}throw t}}async cleanupFailedSpawn(e,t){await this.killProcess(e);let n=await this.findRegistrationByPort(t);n&&n.pid===e&&await this.releaseEntry(n)}async attemptSpawn(e){let t=await this.findAvailablePort(e),n=await this.startHttpServer(t);await new Promise(e=>setTimeout(e,1e4));let r;try{r=await this.healthCheck.check(t)}catch(e){let r=e instanceof Error?e.message:String(e);return console.error(`${Y} Health check threw on port ${t}: ${r}`),await this.cleanupFailedSpawn(n,t),{success:!1,reason:`health_check_error`,port:t,error:r}}if(!r.healthy)return await this.cleanupFailedSpawn(n,t),{success:!1,reason:`health_check_failed`,port:t,error:r.error};if(r.serviceName!==J)return await this.cleanupFailedSpawn(n,t),{success:!1,reason:`service_name_mismatch`,port:t,error:`Expected ${J}, got ${r.serviceName}`};let i=await this.findRegistrationByPort(t);return!i||i.pid!==n?(console.error(`${Y} Ownership mismatch on port ${t}: expected pid ${n}, found ${i?.pid}`),await this.cleanupFailedSpawn(n,t),{success:!1,reason:`ownership_mismatch`,port:t}):(console.error(`${Y} Server self-registered on port ${t} (pid: ${i.pid})`),{success:!0,port:t,pid:n,browserCount:r.browserCount})}async ensureRunning(e=Hn(),t={}){try{if(t.exactPort){let t=await this.discoverHealthyServiceOnPort(e);if(t)return{running:!0,port:t.entry.port,pid:t.entry.pid,browserCount:t.browserCount,spawned:!1};await this.cleanupStaleEntries(),await this.ensureExactPortAvailable(e);let n=await this.attemptSpawn(e);if(n.success)return{running:!0,port:n.port,pid:n.pid,browserCount:n.browserCount,spawned:!0};let r=new nr(`Failed to start browse-tool HTTP server on requested port ${e}: ${n.reason}`);return this.buildErrorStatus(r)}let n=await this.discoverHealthyService();if(n)return{running:!0,port:n.entry.port,pid:n.entry.pid,browserCount:n.browserCount,spawned:!1};await this.cleanupStaleEntries();let r=e;for(let e=0;e<3;e+=1){let t=await this.attemptSpawn(r);if(t.success)return{running:!0,port:t.port,pid:t.pid,browserCount:t.browserCount,spawned:!0};console.error(`${Y} Spawn attempt ${e+1} failed: ${t.reason} on port ${t.port}`),r=t.port+1}let i=new nr(`Failed to start browse-tool HTTP server after 3 attempts`);return this.buildErrorStatus(i)}catch(e){return this.buildErrorStatus(e)}}async stop(){let e=await this.listServiceEntries();if(e.length===0)return!1;let t=[];for(let n of e)try{n.pid&&await this.killProcess(n.pid),await this.releaseEntry(n)}catch(e){let r=e instanceof Error?e:Error(`Failed to stop entry on port ${n.port}: ${String(e)}`);console.error(`${Y} ${r.message}`),t.push(r)}if(t.length>0){let n=t.map(e=>e.message);throw new rr(`Failed to stop ${t.length} of ${e.length} server(s): ${n.join(`; `)}`,t,{cause:t[0]})}return!0}async getStatus(){try{let e=await this.discoverHealthyService();if(e)return{running:!0,port:e.entry.port,pid:e.entry.pid,browserCount:e.browserCount};let t=await this.listServiceEntries();if(t.length>0){let e=t[0];return{running:!1,port:e.port,pid:e.pid,error:`Server registered but not healthy`,errorCode:`HTTP_SERVER_STALE_ENTRY`,errorRecovery:`Run ensureRunning() to start a fresh server or clean up stale entries.`}}return{running:!1,error:`No HTTP server registered`,errorCode:`HTTP_SERVER_NOT_REGISTERED`,errorRecovery:`Call ensureRunning() to discover or spawn a server.`}}catch(e){return this.buildErrorStatus(e)}}};cr=B([h(),z(0,m(L.HttpServerHealthCheck)),z(1,m(L.PortRegistryService)),z(2,m(L.ProcessRegistryService)),R(`design:paramtypes`,[Object,Object,typeof(Gn=d!==void 0&&d)==`function`?Gn:Object])],cr);const lr=900*1e3,ur=1800*1e3,dr=`PLAYWRIGHT_BROWSER_IDLE_TIMEOUT_MINUTES`;function fr(){let e=process.env[dr];if(!e)return ur;let t=Number.parseFloat(e);return!Number.isFinite(t)||t<=0?ur:Math.round(t*60*1e3)}let pr=class{intervalId=null;browserIdleTimeoutMs=fr();constructor(e,t){this.browserService=e,this.pageRegistry=t}start(){this.intervalId||(this.intervalId=setInterval(()=>{this.cleanup().catch(e=>{console.warn(`[IdleCleanup] Error during cleanup:`,e)})},6e4),this.intervalId&&typeof this.intervalId==`object`&&`unref`in this.intervalId&&this.intervalId.unref(),console.warn(`[IdleCleanup] Started (page timeout: ${lr/6e4}min, browser timeout: ${this.browserIdleTimeoutMs/6e4}min)`))}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null,console.warn(`[IdleCleanup] Stopped`))}async cleanup(){let e=Date.now();await this.cleanupIdlePages(e),await this.cleanupIdleBrowsers(e)}async cleanupIdlePages(e){let t=this.pageRegistry.list();for(let n of t){let t=e-n.lastAccessedAt.getTime();if(!(t<lr)){console.warn(`[IdleCleanup] Closing idle page "${n.id}" (idle ${Math.round(t/6e4)}min)`);try{if(n.page)await n.page.close();else{let e=this.browserService.getBrowser(n.browserId);if(e&&(e.pageIds.delete(n.id),e.currentPageId===n.id)){let t=Array.from(e.pageIds);e.currentPageId=t.length>0?t[0]:null}this.pageRegistry.remove(n.id)}}catch(e){console.warn(`[IdleCleanup] Failed to close page "${n.id}":`,e)}}}}async cleanupIdleBrowsers(e){let t=this.browserService.listBrowsers();for(let n of t){let t=n.specOrigin?lr:this.browserIdleTimeoutMs,r=e-n.lastAccessedAt.getTime();if(!(r<t)){console.warn(`[IdleCleanup] Closing idle browser "${n.id}" (idle ${Math.round(r/6e4)}min)`);try{await this.browserService.closeBrowser(n.id)}catch(e){console.warn(`[IdleCleanup] Failed to close browser "${n.id}":`,e)}}}}};pr=B([h(),z(0,m(L.BrowserService)),z(1,m(L.PageRegistry)),R(`design:paramtypes`,[Object,Object])],pr);let mr=class{constructor(e){this.portRegistry=e}async findAvailablePort(e){let t=await this.portRegistry.findAvailablePort({repositoryPath:e.repositoryPath,serviceName:`browse-tool-http`,serviceType:`tool`,environment:e.environment,host:e.host,preferredPort:e.preferredPort,portRange:e.exactPort?{min:e.preferredPort,max:e.preferredPort}:void 0});if(!t.success||!t.port)throw Error(t.error||`Failed to acquire HTTP port from ${e.preferredPort}`);return t.port}async acquirePorts(e){let t,n=!1;try{if(e.reserveMcpPort){let n=e.preferredMcpPort??l.min,r=await this.portRegistry.reservePort({repositoryPath:e.repositoryPath,serviceName:e.mcpServiceName??`browse-tool-mcp-http`,serviceType:e.mcpServiceType??`tool`,environment:e.environment,preferredPort:n,pid:e.pid,host:e.host,force:!0,portRange:{min:n,max:n},metadata:e.mcpMetadata});if(!r.success||!r.record)throw Error(r.error||`Failed to reserve MCP port ${n}`);t=r.record}return{httpPort:await this.findAvailablePort({repositoryPath:e.repositoryPath,environment:e.environment,host:e.host,preferredPort:e.preferredHttpPort??l.min,exactPort:e.exactHttpPort}),mcpPort:t?.port,release:async()=>{n||!t||(n=!0,await this.portRegistry.releasePort({repositoryPath:t.repositoryPath,serviceName:t.serviceName,serviceType:t.serviceType,environment:t.environment,pid:t.pid}))}}}catch(e){throw t&&(n=!0,await this.portRegistry.releasePort({repositoryPath:t.repositoryPath,serviceName:t.serviceName,serviceType:t.serviceType,environment:t.environment,pid:t.pid})),e}}};mr=B([h(),z(0,m(L.PortRegistryService)),R(`design:paramtypes`,[Object])],mr);let hr=class{browsers=new Map;pages=new Map;trackBrowser(e,t){this.browsers.has(e)||this.browsers.set(e,{browserId:e,mode:t,createdAt:new Date,pageIds:new Set})}trackPage(e,t,n){if(this.pages.has(e))return;this.pages.set(e,{pageId:e,browserId:t,url:n,createdAt:new Date});let r=this.browsers.get(t);r&&r.pageIds.add(e)}untrackBrowser(e){let t=this.browsers.get(e);if(t){for(let e of t.pageIds)this.pages.delete(e);this.browsers.delete(e)}}untrackPage(e){let t=this.pages.get(e);if(t){let n=this.browsers.get(t.browserId);n&&n.pageIds.delete(e),this.pages.delete(e)}}getBrowserIds(){return Array.from(this.browsers.keys())}getPageIds(){return Array.from(this.pages.keys())}getPageIdsForBrowser(e){let t=this.browsers.get(e);return t?Array.from(t.pageIds):[]}getSessionState(){return{browsers:Array.from(this.browsers.values()).map(e=>({browserId:e.browserId,mode:e.mode,createdAt:e.createdAt.toISOString(),pageIds:Array.from(e.pageIds)})),pages:Array.from(this.pages.values()).map(e=>({pageId:e.pageId,browserId:e.browserId,url:e.url,createdAt:e.createdAt.toISOString()})),totalBrowsers:this.browsers.size,totalPages:this.pages.size}}clear(){this.browsers.clear(),this.pages.clear()}};hr=B([h()],hr);let gr=class{pageStates=new Map;pageInstances=new Map;startMonitoring(e,t){this.stopMonitoring(e);let n={networkRequests:new Map,consoleMessages:[],requestCounter:0,messageCounter:0,listeners:{}};n.listeners.request=e=>{let t=`req-${++n.requestCounter}`,r={id:t,url:e.url(),method:e.method(),resourceType:e.resourceType(),headers:e.headers(),postData:e.postData()??void 0,timestamp:new Date};n.networkRequests.set(t,r)},n.listeners.response=e=>{let t=e.request();for(let[,r]of n.networkRequests)if(r.url===t.url()&&!r.response){r.response={status:e.status(),statusText:e.statusText(),headers:e.headers(),timing:e.request().timing().responseEnd};break}},n.listeners.console=e=>{let t=`msg-${++n.messageCounter}`,r=e.location(),i={id:t,type:e.type(),text:e.text(),location:r.url?{url:r.url,lineNumber:r.lineNumber,columnNumber:r.columnNumber}:void 0,timestamp:new Date};n.consoleMessages.push(i)},t.on(`request`,n.listeners.request),t.on(`response`,n.listeners.response),t.on(`console`,n.listeners.console),this.pageStates.set(e,n),this.pageInstances.set(e,t),t.once(`close`,()=>{this.stopMonitoring(e)})}stopMonitoring(e){let t=this.pageStates.get(e),n=this.pageInstances.get(e);t&&n&&(t.listeners.request&&n.off(`request`,t.listeners.request),t.listeners.response&&n.off(`response`,t.listeners.response),t.listeners.console&&n.off(`console`,t.listeners.console)),this.pageStates.delete(e),this.pageInstances.delete(e)}getNetworkRequests(e){let t=this.pageStates.get(e);return t?Array.from(t.networkRequests.values()):[]}getNetworkRequest(e,t){let n=this.pageStates.get(e);if(n)return n.networkRequests.get(t)}getConsoleMessages(e,t){let n=this.pageStates.get(e);return n?t?n.consoleMessages.filter(e=>e.type===t):[...n.consoleMessages]:[]}clearData(e){let t=this.pageStates.get(e);t&&(t.networkRequests.clear(),t.consoleMessages=[],t.requestCounter=0,t.messageCounter=0)}};gr=B([h()],gr);let _r=class{pages=new Map;pageIdCounter=0;constructor(e){this.webSocketHub=e}async register(e){let{page:t,browser:n,context:r,browserId:i,profileName:a,ownerId:o,mode:s,onClose:c}=e,l=`page-${++this.pageIdCounter}`,u={id:l,mode:s??`playwright`,page:t,browser:n,context:r,browserId:i,url:t.url(),title:await t.title(),profileName:a,ownerId:o??Ht(),createdAt:new Date,lastAccessedAt:new Date,onClose:c};return this.pages.set(l,u),t.once(`close`,()=>{try{c?.(l)}catch{}this.remove(l)}),l}registerExtensionPage(e,t,n,r=!0){let i=`page-${++this.pageIdCounter}`,a={id:i,mode:`extension`,page:void 0,browser:void 0,context:void 0,browserId:e,url:n??`about:blank`,title:`Extension Tab`,ownerId:Ht(),extensionTabId:t,createdAt:new Date,lastAccessedAt:new Date};return this.pages.set(i,a),r&&this.webSocketHub?.broadcastPageCreated(e,i,n),i}get(e){return this.pages.get(e)}remove(e){let t=this.pages.get(e);t&&(this.pages.delete(e),t.mode===`extension`&&this.webSocketHub?.broadcastPageRemoved(t.browserId,e))}list(){return Array.from(this.pages.values())}findByBrowser(e){return this.list().filter(t=>t.browserId===e)}findByProfile(e){return this.list().filter(t=>t.profileName===e)}findByOwner(e){return this.list().filter(t=>t.ownerId===e)}touchPage(e){let t=this.pages.get(e);t&&(t.lastAccessedAt=new Date)}async updateMetadata(e){let t=this.get(e);t&&t.page&&(t.url=t.page.url(),t.title=await t.page.title())}clear(){this.pages.clear(),this.pageIdCounter=0}};_r=B([h(),z(0,m(L.WebSocketHub)),z(0,g()),R(`design:paramtypes`,[Object])],_r);let vr=class{pauseStates=new Map;getOrCreateState(e){let t=this.pauseStates.get(e);return t||(t={isPaused:!1,stepName:null,reason:null,pausedAt:null,resolver:null},this.pauseStates.set(e,t)),t}pause(e,t,n){let r=this.getOrCreateState(e);if(r.isPaused)throw Error(`Session "${e}" is already paused at step "${r.stepName}"`);return r.isPaused=!0,r.stepName=t,r.reason=n??null,r.pausedAt=new Date,new Promise(e=>{r.resolver=e})}resume(e){let t=this.pauseStates.get(e);if(!t||!t.isPaused)return!1;let n=t.resolver;return t.isPaused=!1,t.stepName=null,t.reason=null,t.pausedAt=null,t.resolver=null,n&&n(),!0}waitForResume(e){let t=this.pauseStates.get(e);return!t||!t.isPaused||!t.resolver?Promise.resolve():new Promise(e=>{let n=t.resolver;t.resolver=()=>{n&&n(),e()}})}getStatus(e){let t=this.pauseStates.get(e);return{sessionId:e,isPaused:t?.isPaused??!1,stepName:t?.stepName??null,reason:t?.reason??null,pausedAt:t?.pausedAt??null}}isPaused(e){return this.pauseStates.get(e)?.isPaused??!1}clear(e){let t=this.pauseStates.get(e);t?.resolver&&t.resolver(),this.pauseStates.delete(e)}};vr=B([h()],vr);let yr=class{profilesDir;constructor(){this.profilesDir=process.env.PLAYWRIGHT_PROFILES_DIR||S(pe(),`.browse-tool`,`profiles`)}getProfilesDir(){return this.profilesDir}getProfileDir(e){return S(this.getProfilesDir(),e)}getProfilePath(e){return S(this.getProfileDir(e),`profile.json`)}getStorageStatePath(e){return S(this.getProfileDir(e),`storage-state.json`)}async ensureProfilesDir(){let e=this.getProfilesDir();D(e)||await k(e,{recursive:!0})}async list(){await this.ensureProfilesDir();let e=await oe(this.getProfilesDir(),{withFileTypes:!0}),t=[];for(let n of e)if(n.isDirectory()){let e=await this.get(n.name);e&&t.push(e)}return t}async get(e){let t=this.getProfilePath(e);if(!D(t))return null;let n=await A(t,`utf-8`);return JSON.parse(n)}async create(e){if(await this.get(e.name))throw Error(`Profile "${e.name}" already exists`);await k(this.getProfileDir(e.name),{recursive:!0});let t=new Date().toISOString(),n={...e,createdAt:t,updatedAt:t};return await j(this.getProfilePath(e.name),JSON.stringify(n,null,2)),n}async update(e,t){let n=await this.get(e);if(!n)throw Error(`Profile "${e}" not found`);let r={...n,...t,name:n.name,createdAt:n.createdAt,updatedAt:new Date().toISOString()};return await j(this.getProfilePath(e),JSON.stringify(r,null,2)),r}async delete(e){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await ce(this.getProfileDir(e),{recursive:!0})}async saveStorageState(e,t){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await j(this.getStorageStatePath(e),JSON.stringify(t,null,2))}async loadStorageState(e){let t=this.getStorageStatePath(e);if(!D(t))return null;let n=await A(t,`utf-8`);return JSON.parse(n)}};yr=B([h(),R(`design:paramtypes`,[])],yr);const br=`BROWSE_TOOL_PROXY_CONFIG_DIR`,xr=`session`,Sr=/\$\{([A-Z0-9_]+)\}/gi;function Cr(e){return e.PLAYWRIGHT_PROFILES_DIR||v.join(pe(),`.browse-tool`,`profiles`)}function wr(e){let t=e.env??process.env,n=e.proxyConfigDir??t.BROWSE_TOOL_PROXY_CONFIG_DIR;if(n)return v.resolve(n);let r=e.profilesDir??Cr(t);return v.join(v.dirname(v.resolve(r)),`proxy-config`)}function Tr(e){if(!e||e.includes(`/`)||e.includes(`\\`)||e===`.`||e===`..`)throw Error(`Invalid proxy config name: ${e}`)}function Er(e,t){Tr(t);let n=v.resolve(e),r=v.resolve(n,`${t}.yaml`),i=v.relative(n,r);if(i.startsWith(`..`)||v.isAbsolute(i))throw Error(`Proxy config path escapes configured directory: ${t}`);return r}function Dr(e,t,n){return e.replace(Sr,(e,r)=>{let i=t[r];if(i===void 0)throw Error(`Missing environment variable "${r}" referenced by proxy config ${n}`);return i})}function Or(e,t,n){let r=Er(e,t);if(!D(r))return;let i=Le(te(r,`utf8`));if(!i||typeof i!=`object`||Array.isArray(i))throw Error(`Proxy config ${r} must be a YAML object`);let a=i,o={server:kr(a,`server`,r,n)},s=Ar(a,`username`,r,n),c=Ar(a,`password`,r,n),l=Ar(a,`bypass`,r,n);return s!==void 0&&(o.username=s),c!==void 0&&(o.password=c),l!==void 0&&(o.bypass=l),o}function kr(e,t,n,r){let i=Ar(e,t,n,r);if(!i)throw Error(`Proxy config ${n} must define a non-empty "${t}" string`);return i}function Ar(e,t,n,r){let i=e[t];if(i!=null){if(typeof i!=`string`)throw Error(`Proxy config ${n} field "${t}" must be a string`);return Dr(i,r,n)}}function jr(e){return!!e&&e!==!0&&typeof e==`object`}let X=class{getProxyConfigDir(e={}){return wr(e)}resolve(e){if(jr(e.proxy))return e.proxy;let t=e.env??process.env,n=wr(e);if(e.profileName){let r=Or(n,e.profileName,t);if(r)return r}if(e.proxy===!0){let e=Or(n,xr,t);if(!e)throw Error(`Proxy requested but ${Er(n,xr)} was not found`);return e}return Or(n,`default`,t)}};X=B([h()],X);function Mr(e){return new X().resolve(e)}let Nr=class{constructor(e){this.httpClient=e}async execute(e,t={}){return this.httpClient.execute(e,t)}async isServerAvailable(){return this.httpClient.isHealthy()}setServerPort(e){this.httpClient.setBaseUrl(q(void 0,e))}async listTools(){let e=this.httpClient.getBaseUrl(),t=await fetch(`${e}/tools`);if(!t.ok)throw Error(`Failed to list tools: ${t.statusText}`);let n=await t.json();if(n.error)throw Error(n.error);return n.tools}};Nr=B([h(),z(0,m(L.HttpBrowserClient)),R(`design:paramtypes`,[Object])],Nr);let Pr=class{constructor(e){this.bundler=e}async runSetup(e,t,n){let r=null;try{r=await this.bundler.bundle(e);let i=await import(`${r.outputPath}?t=${Date.now()}`),a=await this.getSetupFunction(i,t,e)(n);return{state:this.validateSetupResult(a,e),success:!0}}catch(t){return{state:{},success:!1,error:`Setup failed for "${e}": ${t instanceof Error?t.message:String(t)}`}}finally{r&&await r.cleanup()}}getSetupFunction(e,t,n){if(t in e&&typeof e[t]==`function`)return e[t];if(t===`default`&&`default`in e){let t=e.default;if(typeof t==`function`)return t;throw Error(`Default export in "${n}" is not a function`)}let r=Object.keys(e).filter(t=>typeof e[t]==`function`);throw r.length===0?Error(`No function exports found in "${n}"`):Error(`Export "${t}" not found in "${n}". Available functions: ${r.join(`, `)}`)}validateSetupResult(e,t){if(e==null)return{};if(typeof e!=`object`||Array.isArray(e))throw Error(`Setup function in "${t}" must return an object or undefined, got ${typeof e}`);return e}};Pr=B([h(),z(0,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object])],Pr);const Z=`[SpecBundler]`,Fr=RegExp(`^@playwright\\/test$`),Ir=`__PLAYWRIGHT_MCP_STUB_PATH__`,Lr={OUTDIR_NAME:`browse-tool-bundles`,PLUGIN_NAME:`playwright-test-alias`,EXTERNALS:[`playwright`,`playwright/test`,`playwright-core`]},Rr={BUILD_FAILED:`SPEC_BUNDLER_BUILD_FAILED`,NO_OUTPUT:`SPEC_BUNDLER_NO_OUTPUT`,IO_FAILED:`SPEC_BUNDLER_IO_FAILED`},Q={UNEXPECTED_BUILD:`Unexpected build exception`,IO_MKDIR:`Failed to create output directory`,CLEANUP_FAILED:`Failed to clean up temporary file`};var zr=class extends Error{code=Rr.BUILD_FAILED;recovery=`Check the spec file for syntax errors and ensure all imports are resolvable.`;constructor(e,t,n){super(`Failed to bundle spec "${y(e)}": ${t}`,n),this.specPath=e,this.buildErrors=t,this.name=`SpecBundlerBuildError`}},Br=class extends Error{code=Rr.IO_FAILED;recovery=`Check filesystem permissions and available disk space.`;constructor(e,t,n){super(`${e} at "${t}"`,n),this.operation=e,this.targetPath=t,this.name=`SpecBundlerIoError`}};let Vr=class{stubPath;nodeModulesPath;constructor(){let e=b(import.meta.url.replace(`file://`,``));this.stubPath=S(e,`stubs`,`playwright-test.mjs`),this.nodeModulesPath=this.findNodeModules(e)}findNodeModules(e){let t=e,n=w(`/`);for(;t!==n;){let e=S(t,`node_modules`);if(D(S(e,`playwright`)))return e;t=b(t)}for(t=e;t!==n;){let e=S(t,`node_modules`);if(D(e))return e;t=b(t)}return S(e,`node_modules`)}async bundle(e){let t=S(N(),Lr.OUTDIR_NAME);try{await k(t,{recursive:!0})}catch(e){throw console.error(`${Z} ${Q.IO_MKDIR} "${t}":`,e),new Br(Q.IO_MKDIR,t,{cause:e})}let n=S(t,`node_modules`);if(!D(n))try{await ue(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleWithEsbuild(e,t)}async bundleWithEsbuild(e,t){let n=y(e).replace(/\.[^.]+$/,``),r=this.stubPath;try{let i=await Re({entryPoints:[e],outdir:t,bundle:!0,platform:`node`,format:`esm`,target:`node18`,banner:{js:`import { createRequire as __esbuildCreateRequire } from 'module'; const require = __esbuildCreateRequire(import.meta.url);`},external:[...Lr.EXTERNALS],define:{[Ir]:JSON.stringify(r)},plugins:[{name:Lr.PLUGIN_NAME,setup(e){e.onResolve({filter:Fr},()=>({path:r}))}}],write:!0,logLevel:`silent`});if(i.errors.length>0){let t=i.errors.map(e=>e.text).join(`
49
- `);throw console.error(`${Z} Build failed for "${e}": ${t}`),new zr(e,t,{cause:Error(t)})}let a=S(t,`${n}.js`);return{outputPath:a,cleanup:()=>this.safeUnlink(a)}}catch(t){if(t instanceof zr)throw t;let n=t instanceof Error?t.message:Q.UNEXPECTED_BUILD;throw console.error(`${Z} Build failed for "${e}":`,t),new zr(e,n,{cause:t})}}async bundleWithHooks(e){let t=S(N(),Lr.OUTDIR_NAME);try{await k(t,{recursive:!0})}catch(e){throw console.error(`${Z} ${Q.IO_MKDIR} "${t}":`,e),new Br(Q.IO_MKDIR,t,{cause:e})}let n=S(t,`node_modules`);if(!D(n))try{await ue(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleSpecWithHooksEntry(e.specPath,e.hooksPath,t)}async bundleSpecWithHooksEntry(e,t,n){let r=b(w(t)),i=this.stubPath,a=[`process.env.__HOOKS_SOURCE_DIR__ = ${JSON.stringify(r)};`,`import { setup as __hooksSetup } from ${JSON.stringify(t)};`,`await __hooksSetup();`,`delete process.env.__HOOKS_SOURCE_DIR__;`,`export * from ${JSON.stringify(e)};`].join(`
50
- `);try{let t=await Re({stdin:{contents:a,resolveDir:b(e),loader:`ts`,sourcefile:`spec-with-hooks.ts`},outdir:n,bundle:!0,platform:`node`,format:`esm`,target:`node18`,banner:{js:`import { createRequire as __esbuildCreateRequire } from 'module'; const require = __esbuildCreateRequire(import.meta.url);`},external:[...Lr.EXTERNALS],define:{[Ir]:JSON.stringify(i)},plugins:[{name:Lr.PLUGIN_NAME,setup(e){e.onResolve({filter:Fr},()=>({path:i}))}}],write:!0,logLevel:`silent`});if(t.errors.length>0){let n=t.errors.map(e=>e.text).join(`
51
- `);throw console.error(`${Z} Build failed for "${e}" (with hooks): ${n}`),new zr(e,n,{cause:Error(n)})}let r=S(n,`stdin.js`);return{outputPath:r,cleanup:()=>this.safeUnlink(r)}}catch(t){if(t instanceof zr)throw t;let n=t instanceof Error?t.message:Q.UNEXPECTED_BUILD;throw console.error(`${Z} Build failed for "${e}" (with hooks):`,t),new zr(e,n,{cause:t})}}async safeUnlink(e){try{await de(e)}catch(t){console.warn(`${Z} ${Q.CLEANUP_FAILED} "${e}":`,t)}}};Vr=B([h(),R(`design:paramtypes`,[])],Vr);let Hr=class{async discoverProjects(e){let t=e||process.cwd(),n=[],r=await this.findPlaywrightConfigs(t);for(let e of r){let t=b(e),r={name:y(t),path:t,configPath:e,testDir:await this.getTestDirFromConfig(e),specs:[]};r.specs=await this.getProjectSpecs(t),n.push(r)}return n}async getProjectSpecs(e){let t=S(e,`playwright.config.ts`);if(!D(t))throw Error(`No playwright.config.ts found in ${e}`);let n=S(e,await this.getTestDirFromConfig(t));if(!D(n))return[];let r=[],i=await this.findSpecFiles(n);for(let t of i){let n=await this.getSpecInfo(t,e);r.push(n)}return r}async getSpecInfo(e,t){let n=w(e);if(!D(n))throw Error(`Spec file not found: ${e}`);let r=y(n);return{name:r,path:n,relativePath:t?C(t,n):r,hasArgsSchema:await this.checkHasArgsSchema(n)}}async filterSpecs(e,t){let n=await this.discoverProjects(e),r=[];for(let e of n){if(t?.projectName&&e.name!==t.projectName)continue;let n=e.specs;if(t?.specPattern){let e=this.globToRegex(t.specPattern);n=n.filter(t=>e.test(t.name)||e.test(t.relativePath))}r=r.concat(n)}return r}async findPlaywrightConfigs(e){let t=[];return await this.walkDirectory(e,(e,n)=>{if(n){let t=y(e);return!(t===`node_modules`||t.startsWith(`.`))}return y(e)===`playwright.config.ts`&&t.push(e),!0}),t}async findSpecFiles(e){let t=[];return await this.walkDirectory(e,(e,n)=>n?y(e)!==`node_modules`:(e.endsWith(`.spec.ts`)&&t.push(e),!0)),t}async walkDirectory(e,t){if(!D(e))return;let n=await oe(e,{withFileTypes:!0});for(let r of n){let n=S(e,r.name);r.isDirectory()?t(n,!0)&&await this.walkDirectory(n,t):t(n,!1)}}async getTestDirFromConfig(e){try{let t=(await A(e,`utf-8`)).match(/testDir:\s*['"`]([^'"`]+)['"`]/);if(t)return t[1].replace(/^\.\//,``)}catch{}return`tests`}async checkHasArgsSchema(e){try{let t=await A(e,`utf-8`);return/export\s+(const\s+)?argsSchema\b/.test(t)}catch{return!1}}globToRegex(e){let t=e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`).replace(/\?/g,`.`);return RegExp(`^${t}$`,`i`)}};Hr=B([h()],Hr);let Ur=class{constructor(e){this.bundler=e}async extractSpecMetadata(e){let t=await this.bundler.bundle(e);try{let e=await import(`${t.outputPath}?t=${Date.now()}`),n=e.argsSchema??null;return{argsSchema:n,envPrefix:e.envPrefix??null,hasDynamicArgs:n!==null}}catch{return{argsSchema:null,envPrefix:null,hasDynamicArgs:!1}}finally{await t.cleanup()}}async loadPlaywrightConfig(e){try{let t=await A(e,`utf-8`),n={},r=t.match(/baseURL:\s*['"`]([^'"`]+)['"`]/);r&&(n.baseURL=r[1]);let i=t.match(/export\s+const\s+mcpConfig\s*=\s*\{([^}]+)\}/s);if(i){let e=i[1],t=e.match(/setupFile:\s*['"`]([^'"`]+)['"`]/);t&&(n.setupFile=t[1]);let r=e.match(/setupExport:\s*['"`]([^'"`]+)['"`]/);r&&(n.setupExport=r[1])}let a=t.match(/webServer:\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);if(a){let e=a[1],t=e.match(/command:\s*['"`]([^'"`]+)['"`]/),r=e.match(/url:\s*['"`]([^'"`]+)['"`]/),i=e.match(/reuseExistingServer:\s*(true|false)/),o=e.match(/timeout:\s*(\d+)/),s=e.match(/cwd:\s*['"`]([^'"`]+)['"`]/);t&&r&&(n.webServer={command:t[1],url:r[1],reuseExistingServer:i?i[1]===`true`:void 0,timeout:o?Number.parseInt(o[1],10):void 0,cwd:s?s[1]:void 0})}return n}catch{return{}}}parseArgsFromEnv(e,t){let n={},r=t.toUpperCase();for(let[e,t]of Object.entries(process.env))if(e.startsWith(r)&&t!==void 0){let i=this.envKeyToFieldName(e.slice(r.length));n[i]=this.parseEnvValue(t)}try{return e.parse(n)}catch{return n}}envKeyToFieldName(e){return e.toLowerCase().split(`_`).map((e,t)=>t===0?e:e.charAt(0).toUpperCase()+e.slice(1)).join(``)}parseEnvValue(e){if(e.toLowerCase()===`true`)return!0;if(e.toLowerCase()===`false`)return!1;let t=Number(e);if(!Number.isNaN(t)&&e.trim()!==``)return t;if(e.startsWith(`{`)&&e.endsWith(`}`)||e.startsWith(`[`)&&e.endsWith(`]`))try{return JSON.parse(e)}catch{}return e}};Ur=B([h(),z(0,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object])],Ur);function Wr(e,t){let{runtimeUse:r,unsupportedKeys:i}=n(t);return{options:{...e?{baseURL:e}:{},...r},warnings:i.length>0?[`test.use() ignores unsupported fixture keys: ${i.join(`, `)}`]:[]}}let Gr=class{constructor(e){this.bundler=e}async loadSpec(e,n){let a=n?await this.bundler.bundleWithHooks({specPath:e,hooksPath:n}):await this.bundler.bundle(e),o=process,s=o.__pw_initiator__;try{return t(e),o.__pw_initiator__=void 0,await import(`${a.outputPath}?t=${Date.now()}`),i()}catch(t){throw r(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{o.__pw_initiator__=s,await a.cleanup()}}async executeSpec(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,hooksPath:i,baseURL:o,keepBrowserOpen:s}=e,c=Date.now(),l=[],u=0,d=0,f=new Set,p=new Set,m=[],h=await ve[n].launch({channel:`chrome`,headless:r});try{let e=await this.loadSpec(t,i);for(let t of e.allTests){let{options:e,warnings:n}=Wr(o,t.config.use);for(let e of n)f.add(e);let r=t.serialScopeId,i=r!==void 0&&p.has(r);if(t.skip||i){l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:0}),u++;continue}let c=await h.newContext(e),g=await c.newPage(),_={page:g,context:c,browser:h,playwright:ve},v=Date.now();try{await t.fn(_),l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-v}),u++}catch(e){if(e instanceof a||e instanceof Error&&`isSkip`in e)l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-v}),u++;else{let n=e instanceof Error?e.message:String(e);l.push({title:t.title,fullTitle:t.fullTitle,passed:!1,error:n,duration:Date.now()-v}),d++,r!==void 0&&p.add(r)}}finally{s?m.push({page:g,context:c}):await c.close().catch(()=>{})}}if(!s){for(let e of h.contexts())await e.close().catch(()=>{});await h.close()}return{specPath:t,totalTests:e.testCount,passed:u,failed:d,testResults:l,success:d===0,duration:Date.now()-c,warnings:f.size>0?[...f]:void 0,browser:s?h:void 0,keptPages:m.length>0?m:void 0}}catch(e){for(let e of h.contexts())await e.close().catch(()=>{});return await h.close().catch(()=>{}),{specPath:t,totalTests:0,passed:0,failed:1,testResults:[{title:`Spec Loading`,fullTitle:`Failed to load: ${t}`,passed:!1,error:e instanceof Error?e.message:String(e),duration:Date.now()-c}],success:!1,duration:Date.now()-c,warnings:f.size>0?[...f]:void 0}}}async executeSpecEnhanced(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,testFilter:i,specArgs:o,hooksPath:s,baseURL:c,keepBrowserOpen:l}=e,u=Date.now(),d=[],f=0,p=0,m=new Set,h=new Set,g=[],_=await ve[n].launch({channel:`chrome`,headless:r});try{let e=await this.loadSpec(t,s),n=i?this.filterTests(e.allTests,i):e.allTests;for(let e of n){let{options:t,warnings:n}=Wr(c,e.config.use);for(let e of n)m.add(e);let r=e.serialScopeId,i=r!==void 0&&h.has(r);if(e.skip||i){d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:0}),f++;continue}let s=await _.newContext(t),u=await s.newPage(),v={page:u,context:s,browser:_,playwright:ve,specArgs:o},y=Date.now();try{await e.fn(v),d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-y}),f++}catch(t){if(t instanceof a||t instanceof Error&&`isSkip`in t)d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-y}),f++;else{let n=t instanceof Error?t.message:String(t);d.push({title:e.title,fullTitle:e.fullTitle,passed:!1,error:n,duration:Date.now()-y}),p++,r!==void 0&&h.add(r)}}finally{l?g.push({page:u,context:s}):await s.close().catch(()=>{})}}if(!l){for(let e of _.contexts())await e.close().catch(()=>{});await _.close()}return{specPath:t,totalTests:n.length,passed:f,failed:p,testResults:d,success:p===0,duration:Date.now()-u,warnings:m.size>0?[...m]:void 0,browser:l?_:void 0,keptPages:g.length>0?g:void 0}}catch(e){for(let e of _.contexts())await e.close().catch(()=>{});return await _.close().catch(()=>{}),{specPath:t,totalTests:0,passed:0,failed:1,testResults:[{title:`Spec Loading`,fullTitle:`Failed to load: ${t}`,passed:!1,error:e instanceof Error?e.message:String(e),duration:Date.now()-u}],success:!1,duration:Date.now()-u,warnings:m.size>0?[...m]:void 0}}}filterTests(e,t){let n=[...e];if(t.onlyMarked){let e=n.filter(e=>e.only);e.length>0&&(n=e)}if(t.testName&&(n=n.filter(e=>e.title===t.testName)),t.testPattern){let e=new RegExp(t.testPattern,`i`);n=n.filter(t=>e.test(t.fullTitle))}if(t.describeFilter){let e=new RegExp(t.describeFilter,`i`);n=n.filter(t=>e.test(t.fullTitle))}return n}};Gr=B([h(),z(0,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object])],Gr);let Kr=class{browsers=new Map;browserIdCounter=0;constructor(e,t,n){this.profileService=e,this.pageRegistry=t,this.lockManager=n}findChromePath(){let e=M.platform(),t={darwin:[`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,`/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary`,`${M.homedir()}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`],win32:[`C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe`,`C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe`,`${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`],linux:[`/usr/bin/google-chrome`,`/usr/bin/google-chrome-stable`,`/usr/bin/chromium`,`/usr/bin/chromium-browser`,`/snap/bin/chromium`]},n=t[e]??t.linux;for(let e of n)if(T.existsSync(e))return e;return null}resolveExtensionPath(e){if(e&&T.existsSync(v.join(e,`manifest.json`)))return e;let t=v.dirname(new URL(import.meta.url).pathname),n=[v.resolve(t,`../dist/extension`),v.resolve(t,`../../dist/extension`)];for(let e of n)if(T.existsSync(v.join(e,`manifest.json`)))return e;let r=v.resolve(process.cwd(),`dist/extension`);if(T.existsSync(v.join(r,`manifest.json`)))return r;throw Error(`Chrome extension not found. Build the extension first: pnpm build:extension`)}createUserDataDir(e){if(e){let t=v.join(this.profileService.getProfilesDir(),e,`stealth-user-data`);return T.mkdirSync(t,{recursive:!0}),t}let t=v.join(M.tmpdir(),`stealth-chrome-${Date.now()}`);return T.mkdirSync(t,{recursive:!0}),t}async launch(e={}){let t=this.findChromePath();if(!t)throw Error(`Chrome not found. Please install Google Chrome.
49
+ `);throw console.error(`${Z} Build failed for "${e}": ${t}`),new zr(e,t,{cause:Error(t)})}let a=S(t,`${n}.js`);return{outputPath:a,cleanup:()=>this.safeUnlink(a)}}catch(t){if(t instanceof zr)throw t;let n=t instanceof Error?t.message:Q.UNEXPECTED_BUILD;throw console.error(`${Z} Build failed for "${e}":`,t),new zr(e,n,{cause:t})}}async bundleWithHooks(e){let t=S(N(),Lr.OUTDIR_NAME);try{await k(t,{recursive:!0})}catch(e){throw console.error(`${Z} ${Q.IO_MKDIR} "${t}":`,e),new Br(Q.IO_MKDIR,t,{cause:e})}let n=S(t,`node_modules`);if(!D(n))try{await ue(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleSpecWithHooksEntry(e.specPath,e.hooksPath,t)}async bundleSpecWithHooksEntry(e,t,n){let r=b(w(t)),i=this.stubPath,a=`spec-with-hooks`,o=S(n,`${a}-${I()}.js`),s=[`process.env.__HOOKS_SOURCE_DIR__ = ${JSON.stringify(r)};`,`import { setup as __hooksSetup } from ${JSON.stringify(t)};`,`await __hooksSetup();`,`delete process.env.__HOOKS_SOURCE_DIR__;`,`export * from ${JSON.stringify(e)};`].join(`
50
+ `);try{let t=await Re({stdin:{contents:s,resolveDir:b(e),loader:`ts`,sourcefile:`${a}.ts`},outfile:o,bundle:!0,platform:`node`,format:`esm`,target:`node18`,banner:{js:`import { createRequire as __esbuildCreateRequire } from 'module'; const require = __esbuildCreateRequire(import.meta.url);`},external:[...Lr.EXTERNALS],define:{[Ir]:JSON.stringify(i)},plugins:[{name:Lr.PLUGIN_NAME,setup(e){e.onResolve({filter:Fr},()=>({path:i}))}}],write:!0,logLevel:`silent`});if(t.errors.length>0){let n=t.errors.map(e=>e.text).join(`
51
+ `);throw console.error(`${Z} Build failed for "${e}" (with hooks): ${n}`),new zr(e,n,{cause:Error(n)})}return{outputPath:o,cleanup:()=>this.safeUnlink(o)}}catch(t){if(t instanceof zr)throw t;let n=t instanceof Error?t.message:Q.UNEXPECTED_BUILD;throw console.error(`${Z} Build failed for "${e}" (with hooks):`,t),new zr(e,n,{cause:t})}}async safeUnlink(e){try{await de(e)}catch(t){console.warn(`${Z} ${Q.CLEANUP_FAILED} "${e}":`,t)}}};Vr=B([h(),R(`design:paramtypes`,[])],Vr);let Hr=class{async discoverProjects(e){let t=e||process.cwd(),n=[],r=await this.findPlaywrightConfigs(t);for(let e of r){let t=b(e),r={name:y(t),path:t,configPath:e,testDir:await this.getTestDirFromConfig(e),specs:[]};r.specs=await this.getProjectSpecs(t),n.push(r)}return n}async getProjectSpecs(e){let t=S(e,`playwright.config.ts`);if(!D(t))throw Error(`No playwright.config.ts found in ${e}`);let n=S(e,await this.getTestDirFromConfig(t));if(!D(n))return[];let r=[],i=await this.findSpecFiles(n);for(let t of i){let n=await this.getSpecInfo(t,e);r.push(n)}return r}async getSpecInfo(e,t){let n=w(e);if(!D(n))throw Error(`Spec file not found: ${e}`);let r=y(n);return{name:r,path:n,relativePath:t?C(t,n):r,hasArgsSchema:await this.checkHasArgsSchema(n)}}async filterSpecs(e,t){let n=await this.discoverProjects(e),r=[];for(let e of n){if(t?.projectName&&e.name!==t.projectName)continue;let n=e.specs;if(t?.specPattern){let e=this.globToRegex(t.specPattern);n=n.filter(t=>e.test(t.name)||e.test(t.relativePath))}r=r.concat(n)}return r}async findPlaywrightConfigs(e){let t=[];return await this.walkDirectory(e,(e,n)=>{if(n){let t=y(e);return!(t===`node_modules`||t.startsWith(`.`))}return y(e)===`playwright.config.ts`&&t.push(e),!0}),t}async findSpecFiles(e){let t=[];return await this.walkDirectory(e,(e,n)=>n?y(e)!==`node_modules`:(e.endsWith(`.spec.ts`)&&t.push(e),!0)),t}async walkDirectory(e,t){if(!D(e))return;let n=await oe(e,{withFileTypes:!0});for(let r of n){let n=S(e,r.name);r.isDirectory()?t(n,!0)&&await this.walkDirectory(n,t):t(n,!1)}}async getTestDirFromConfig(e){try{let t=(await A(e,`utf-8`)).match(/testDir:\s*['"`]([^'"`]+)['"`]/);if(t)return t[1].replace(/^\.\//,``)}catch{}return`tests`}async checkHasArgsSchema(e){try{let t=await A(e,`utf-8`);return/export\s+(const\s+)?argsSchema\b/.test(t)}catch{return!1}}globToRegex(e){let t=e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`).replace(/\?/g,`.`);return RegExp(`^${t}$`,`i`)}};Hr=B([h()],Hr);let Ur=class{constructor(e){this.bundler=e}async extractSpecMetadata(e){let t=await this.bundler.bundle(e);try{let e=await import(`${t.outputPath}?t=${Date.now()}`),n=e.argsSchema??null;return{argsSchema:n,envPrefix:e.envPrefix??null,hasDynamicArgs:n!==null}}catch{return{argsSchema:null,envPrefix:null,hasDynamicArgs:!1}}finally{await t.cleanup()}}async loadPlaywrightConfig(e){try{let t=await A(e,`utf-8`),n={},r=t.match(/baseURL:\s*['"`]([^'"`]+)['"`]/);r&&(n.baseURL=r[1]);let i=t.match(/export\s+const\s+mcpConfig\s*=\s*\{([^}]+)\}/s);if(i){let e=i[1],t=e.match(/setupFile:\s*['"`]([^'"`]+)['"`]/);t&&(n.setupFile=t[1]);let r=e.match(/setupExport:\s*['"`]([^'"`]+)['"`]/);r&&(n.setupExport=r[1])}let a=t.match(/webServer:\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);if(a){let e=a[1],t=e.match(/command:\s*['"`]([^'"`]+)['"`]/),r=e.match(/url:\s*['"`]([^'"`]+)['"`]/),i=e.match(/reuseExistingServer:\s*(true|false)/),o=e.match(/timeout:\s*(\d+)/),s=e.match(/cwd:\s*['"`]([^'"`]+)['"`]/);t&&r&&(n.webServer={command:t[1],url:r[1],reuseExistingServer:i?i[1]===`true`:void 0,timeout:o?Number.parseInt(o[1],10):void 0,cwd:s?s[1]:void 0})}return n}catch{return{}}}parseArgsFromEnv(e,t){let n={},r=t.toUpperCase();for(let[e,t]of Object.entries(process.env))if(e.startsWith(r)&&t!==void 0){let i=this.envKeyToFieldName(e.slice(r.length));n[i]=this.parseEnvValue(t)}try{return e.parse(n)}catch{return n}}envKeyToFieldName(e){return e.toLowerCase().split(`_`).map((e,t)=>t===0?e:e.charAt(0).toUpperCase()+e.slice(1)).join(``)}parseEnvValue(e){if(e.toLowerCase()===`true`)return!0;if(e.toLowerCase()===`false`)return!1;let t=Number(e);if(!Number.isNaN(t)&&e.trim()!==``)return t;if(e.startsWith(`{`)&&e.endsWith(`}`)||e.startsWith(`[`)&&e.endsWith(`]`))try{return JSON.parse(e)}catch{}return e}};Ur=B([h(),z(0,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object])],Ur);function Wr(e,t){let{runtimeUse:r,unsupportedKeys:i}=n(t);return{options:{...e?{baseURL:e}:{},...r},warnings:i.length>0?[`test.use() ignores unsupported fixture keys: ${i.join(`, `)}`]:[]}}let Gr=class{constructor(e){this.bundler=e}async loadSpec(e,n){let a=n?await this.bundler.bundleWithHooks({specPath:e,hooksPath:n}):await this.bundler.bundle(e),o=process,s=o.__pw_initiator__;try{return t(e),o.__pw_initiator__=void 0,await import(`${a.outputPath}?t=${Date.now()}`),i()}catch(t){throw r(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{o.__pw_initiator__=s,await a.cleanup()}}async executeSpec(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,hooksPath:i,baseURL:o,keepBrowserOpen:s}=e,c=Date.now(),l=[],u=0,d=0,f=new Set,p=new Set,m=[],h=await ve[n].launch({channel:`chrome`,headless:r});try{let e=await this.loadSpec(t,i);for(let t of e.allTests){let{options:e,warnings:n}=Wr(o,t.config.use);for(let e of n)f.add(e);let r=t.serialScopeId,i=r!==void 0&&p.has(r);if(t.skip||i){l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:0}),u++;continue}let c=await h.newContext(e),g=await c.newPage(),_={page:g,context:c,browser:h,playwright:ve},v=Date.now();try{await t.fn(_),l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-v}),u++}catch(e){if(e instanceof a||e instanceof Error&&`isSkip`in e)l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-v}),u++;else{let n=e instanceof Error?e.message:String(e);l.push({title:t.title,fullTitle:t.fullTitle,passed:!1,error:n,duration:Date.now()-v}),d++,r!==void 0&&p.add(r)}}finally{s?m.push({page:g,context:c}):await c.close().catch(()=>{})}}if(!s){for(let e of h.contexts())await e.close().catch(()=>{});await h.close()}return{specPath:t,totalTests:e.testCount,passed:u,failed:d,testResults:l,success:d===0,duration:Date.now()-c,warnings:f.size>0?[...f]:void 0,browser:s?h:void 0,keptPages:m.length>0?m:void 0}}catch(e){for(let e of h.contexts())await e.close().catch(()=>{});return await h.close().catch(()=>{}),{specPath:t,totalTests:0,passed:0,failed:1,testResults:[{title:`Spec Loading`,fullTitle:`Failed to load: ${t}`,passed:!1,error:e instanceof Error?e.message:String(e),duration:Date.now()-c}],success:!1,duration:Date.now()-c,warnings:f.size>0?[...f]:void 0}}}async executeSpecEnhanced(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,testFilter:i,specArgs:o,hooksPath:s,baseURL:c,keepBrowserOpen:l}=e,u=Date.now(),d=[],f=0,p=0,m=new Set,h=new Set,g=[],_=await ve[n].launch({channel:`chrome`,headless:r});try{let e=await this.loadSpec(t,s),n=i?this.filterTests(e.allTests,i):e.allTests;for(let e of n){let{options:t,warnings:n}=Wr(c,e.config.use);for(let e of n)m.add(e);let r=e.serialScopeId,i=r!==void 0&&h.has(r);if(e.skip||i){d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:0}),f++;continue}let s=await _.newContext(t),u=await s.newPage(),v={page:u,context:s,browser:_,playwright:ve,specArgs:o},y=Date.now();try{await e.fn(v),d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-y}),f++}catch(t){if(t instanceof a||t instanceof Error&&`isSkip`in t)d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-y}),f++;else{let n=t instanceof Error?t.message:String(t);d.push({title:e.title,fullTitle:e.fullTitle,passed:!1,error:n,duration:Date.now()-y}),p++,r!==void 0&&h.add(r)}}finally{l?g.push({page:u,context:s}):await s.close().catch(()=>{})}}if(!l){for(let e of _.contexts())await e.close().catch(()=>{});await _.close()}return{specPath:t,totalTests:n.length,passed:f,failed:p,testResults:d,success:p===0,duration:Date.now()-u,warnings:m.size>0?[...m]:void 0,browser:l?_:void 0,keptPages:g.length>0?g:void 0}}catch(e){for(let e of _.contexts())await e.close().catch(()=>{});return await _.close().catch(()=>{}),{specPath:t,totalTests:0,passed:0,failed:1,testResults:[{title:`Spec Loading`,fullTitle:`Failed to load: ${t}`,passed:!1,error:e instanceof Error?e.message:String(e),duration:Date.now()-u}],success:!1,duration:Date.now()-u,warnings:m.size>0?[...m]:void 0}}}filterTests(e,t){let n=[...e];if(t.onlyMarked){let e=n.filter(e=>e.only);e.length>0&&(n=e)}if(t.testName&&(n=n.filter(e=>e.title===t.testName)),t.testPattern){let e=new RegExp(t.testPattern,`i`);n=n.filter(t=>e.test(t.fullTitle))}if(t.describeFilter){let e=new RegExp(t.describeFilter,`i`);n=n.filter(t=>e.test(t.fullTitle))}return n}};Gr=B([h(),z(0,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object])],Gr);let Kr=class{browsers=new Map;browserIdCounter=0;constructor(e,t,n){this.profileService=e,this.pageRegistry=t,this.lockManager=n}findChromePath(){let e=M.platform(),t={darwin:[`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,`/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary`,`${M.homedir()}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`],win32:[`C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe`,`C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe`,`${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`],linux:[`/usr/bin/google-chrome`,`/usr/bin/google-chrome-stable`,`/usr/bin/chromium`,`/usr/bin/chromium-browser`,`/snap/bin/chromium`]},n=t[e]??t.linux;for(let e of n)if(T.existsSync(e))return e;return null}resolveExtensionPath(e){if(e&&T.existsSync(v.join(e,`manifest.json`)))return e;let t=v.dirname(new URL(import.meta.url).pathname),n=[v.resolve(t,`../dist/extension`),v.resolve(t,`../../dist/extension`)];for(let e of n)if(T.existsSync(v.join(e,`manifest.json`)))return e;let r=v.resolve(process.cwd(),`dist/extension`);if(T.existsSync(v.join(r,`manifest.json`)))return r;throw Error(`Chrome extension not found. Build the extension first: pnpm build:extension`)}createUserDataDir(e){if(e){let t=v.join(this.profileService.getProfilesDir(),e,`stealth-user-data`);return T.mkdirSync(t,{recursive:!0}),t}let t=v.join(M.tmpdir(),`stealth-chrome-${Date.now()}`);return T.mkdirSync(t,{recursive:!0}),t}async launch(e={}){let t=this.findChromePath();if(!t)throw Error(`Chrome not found. Please install Google Chrome.
52
52
  macOS: brew install --cask google-chrome
53
53
  Linux: sudo apt install google-chrome-stable`);let n=this.resolveExtensionPath(e.extensionPath),r=this.createUserDataDir(e.profileName);await this.lockManager.ensureAvailable(r);let i=`stealth-browser-${++this.browserIdCounter}`,a=[n];e.proxy?.username&&e.proxy?.password&&a.push(Lt(e.proxy.username,e.proxy.password));let o=a.join(`,`),s=P(t,[`--user-data-dir=${r}`,`--load-extension=${o}`,`--disable-extensions-except=${o}`,`--disable-blink-features=AutomationControlled`,`--disable-infobars`,`--no-first-run`,`--no-default-browser-check`,`--disable-background-timer-throttling`,`--disable-backgrounding-occluded-windows`,`--disable-renderer-backgrounding`,`--disable-component-update`,`--disable-features=TranslateUI`,`--disable-ipc-flooding-protection`,`--password-store=basic`,`--use-mock-keychain`,`--disable-dev-shm-usage`,`--disable-gpu-sandbox`,`--silent-debugger-extension-api`,...e.windowSize?[`--window-size=${e.windowSize.width},${e.windowSize.height}`]:[`--window-size=1280,720`],...e.proxy?Rt(e.proxy):[],e.url??`about:blank`,...e.extraArgs??[]],{detached:!1,stdio:[`ignore`,`ignore`,`ignore`],env:{...process.env,DISPLAY:process.env.DISPLAY}});if(!s.pid)throw Error(`Failed to start Chrome process`);let c={id:i,process:s,pid:s.pid,userDataDir:r,extensionPath:n,profileName:e.profileName,createdAt:new Date,isRunning:!0};return s.on(`exit`,()=>{c.isRunning=!1}),s.on(`error`,e=>{console.error(`Chrome process ${i} error:`,e),c.isRunning=!1}),this.browsers.set(i,c),this.pageRegistry.registerExtensionPage(i),c}async close(e){let t=this.browsers.get(e);if(t&&(t.isRunning&&t.pid&&await It(t.pid),t.isRunning=!1,this.browsers.delete(e),!t.profileName&&t.userDataDir.includes(`stealth-chrome-`)))try{T.rmSync(t.userDataDir,{recursive:!0,force:!0})}catch{}}async closeAll(){let e=Array.from(this.browsers.keys()).map(e=>this.close(e));await Promise.all(e)}getBrowser(e){return this.browsers.get(e)}listBrowsers(){return Array.from(this.browsers.values()).filter(e=>e.isRunning)}};Kr=B([h(),z(0,m(L.ProfileService)),z(1,m(L.PageRegistry)),z(2,m(L.BrowserLockManager)),R(`design:paramtypes`,[Object,Object,Object])],Kr);function qr(e){if(!e.isError)return;let t=e.content[0];return t?.type===`text`&&typeof t.text==`string`?t.text:`Unknown tool execution error`}let Jr=class{constructor(e,t,n,r=new G){this.pageRegistry=e,this.browserService=t,this.delegator=n,this.telemetry=r}formatPageNotFoundMessage(e){let t=this.pageRegistry.list()??[];return t.length===0?`Page "${e}" not found. No pages are currently registered. Launch or select a browser page, then retry with the returned pageId.`:`Page "${e}" not found. Available pages: ${t.slice(0,5).map(e=>`${e.id} (${e.url||`about:blank`}, browser ${e.browserId})`).join(`; `)}${t.length>5?`; and ${t.length-5} more`:``}. Use browser_list_session or browser_list_pages to refresh page IDs, then retry with a current pageId.`}formatBrowserNotFoundMessage(e){let t=this.browserService.listBrowsers()??[];return t.length===0?`Browser "${e}" not found. No browsers are currently registered. Launch a browser, then retry with the returned browserId.`:`Browser "${e}" not found. Available browsers: ${t.slice(0,5).map(e=>`${e.id} (${e.mode}, pages ${e.pageIds.size})`).join(`; `)}${t.length>5?`; and ${t.length-5} more`:``}. Use browser_list_session to refresh browser IDs, then retry with a current browserId.`}async execute(e,t,n,r){return this.telemetry.runInSpan(`browse_tool.tool_executor.page`,{attributes:{"browse_tool.tool.name":e,"browse_tool.page.id":t}},async i=>{let a=this.pageRegistry.get(t);if(!a){let e=this.formatPageNotFoundMessage(t);return i?.setStatus({code:Oe.ERROR,message:e}),{content:[{type:`text`,text:e}],isError:!0}}i?.setAttributes({"browse_tool.browser.id":a.browserId,"browse_tool.execution.mode":a.mode});let o=a.mode===`extension`?await this.delegator.executeTool(e,{pageId:t,browserId:a.browserId,...n}):await r(),s=qr(o);if(s)i?.setStatus({code:Oe.ERROR,message:s});else if(a.mode===`extension`)try{await this.browserService.ensureExtensionRecordingActive(a.browserId,t)}catch(e){console.warn(`[ToolExecutor] Failed to start extension recording for browser "${a.browserId}" on page "${t}":`,e)}return o})}isExtensionMode(e){return this.pageRegistry.get(e)?.mode===`extension`}async executeForBrowser(e,t,n,r){return this.telemetry.runInSpan(`browse_tool.tool_executor.browser`,{attributes:{"browse_tool.tool.name":e,"browse_tool.browser.id":t}},async i=>{let a=this.browserService.getBrowser(t);if(!a){let e=this.formatBrowserNotFoundMessage(t);return i?.setStatus({code:Oe.ERROR,message:e}),{content:[{type:`text`,text:e}],isError:!0}}i?.setAttributes({"browse_tool.execution.mode":a.mode});let o=a.mode===`extension`||a.mode===`vm`?await this.delegator.executeTool(e,{browserId:t,...n}):await r(),s=qr(o);return s&&i?.setStatus({code:Oe.ERROR,message:s}),o})}isBrowserExtensionMode(e){let t=this.browserService.getBrowser(e);return t?.mode===`extension`||t?.mode===`vm`}};Jr=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ExtensionToolDelegator)),z(3,m(L.TelemetryService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Jr);let Yr=class{serverProcess=null;currentUrl=null;cleanupRegistered=!1;constructor(){this.registerCleanup()}registerCleanup(){if(this.cleanupRegistered)return;this.cleanupRegistered=!0;let e=()=>this.cleanupSync();process.on(`exit`,e),process.on(`SIGINT`,e),process.on(`SIGTERM`,e)}async startServer(e,t){let{command:n,url:r,reuseExistingServer:i=!1,timeout:a=3e4,cwd:o}=e;if(i&&await this.isServerRunning(r))return this.currentUrl=r,{started:!1,url:r,reused:!0};await this.stopServer();let s=o?w(t,o):t,[c,...l]=this.parseCommand(n);if(this.serverProcess=P(c,l,{cwd:s,shell:!0,stdio:[`ignore`,`pipe`,`pipe`],detached:!1}),this.currentUrl=r,this.serverProcess.on(`error`,e=>{console.error(`Server process error: ${e.message}`)}),!await this.waitForServer(r,a))throw await this.stopServer(),Error(`Server failed to start within ${a}ms. URL: ${r}, Command: ${n}`);return{started:!0,url:r,reused:!1}}async waitForServer(e,t){let n=Date.now(),r=100;for(;Date.now()-n<t;){if(await this.isServerRunning(e))return!0;await this.sleep(r),r=Math.min(r*1.5,2e3)}return!1}async stopServer(){if(this.serverProcess)return new Promise(e=>{if(!this.serverProcess){e();return}let t=this.serverProcess;this.serverProcess=null,this.currentUrl=null;let n=setTimeout(()=>{try{t.kill(`SIGKILL`)}catch{}e()},5e3);t.on(`exit`,()=>{clearTimeout(n),e()});try{t.kill(`SIGTERM`)}catch{clearTimeout(n),e()}})}async isServerRunning(e){try{let t=new AbortController,n=setTimeout(()=>t.abort(),3e3),r=await fetch(e,{method:`HEAD`,signal:t.signal});return clearTimeout(n),r.status>=200&&r.status<400}catch{return!1}}parseCommand(e){let t=[],n=``,r=!1,i=``;for(let a of e)(a===`"`||a===`'`)&&!r?(r=!0,i=a):a===i&&r?(r=!1,i=``):a===` `&&!r?n&&=(t.push(n),``):n+=a;return n&&t.push(n),t}sleep(e){return new Promise(t=>setTimeout(t,e))}getCurrentUrl(){return this.currentUrl}cleanupSync(){if(this.serverProcess){try{this.serverProcess.kill(`SIGKILL`)}catch{}this.serverProcess=null,this.currentUrl=null}}};Yr=B([h(),R(`design:paramtypes`,[])],Yr);const Xr=F.object({type:F.string(),text:F.string().optional(),data:F.string().optional(),mimeType:F.string().optional()}),Zr=F.object({traceId:F.string().regex(/^[0-9a-f]{32}$/i).optional(),parentSpanId:F.string().regex(/^[0-9a-f]{16}$/i).optional()}),Qr=F.object({content:F.array(Xr),isError:F.boolean().optional()}),$r=F.object({type:F.literal(`task:push`),id:F.string(),payload:F.object({taskId:F.string(),tool:F.string(),arguments:F.record(F.string(),F.unknown()),pageId:F.string().optional(),telemetry:Zr.optional()})}),ei=F.object({type:F.literal(`page:created`),payload:F.object({pageId:F.string(),browserId:F.string(),url:F.string().optional()})}),ti=F.object({type:F.literal(`page:removed`),payload:F.object({pageId:F.string()})}),ni=F.object({type:F.literal(`session:ack`),id:F.string(),payload:F.object({sessionId:F.string(),controlMode:F.string()})}),ri=F.object({type:F.literal(`pong`)}),ii=F.object({type:F.literal(`error`),payload:F.object({message:F.string(),code:F.string().optional()})});F.discriminatedUnion(`type`,[$r,ei,ti,ni,ri,ii]);const ai=F.object({type:F.literal(`session:register`),id:F.string(),payload:F.object({browserId:F.string(),tabId:F.number().optional(),url:F.string().optional()})}),oi=F.object({type:F.literal(`task:result`),payload:F.object({taskId:F.string(),success:F.boolean(),result:Qr.optional(),error:F.string().optional()})}),si=F.object({type:F.literal(`tab:mapped`),payload:F.object({pageId:F.string(),tabId:F.number()})}),ci=F.object({type:F.literal(`ping`)}),li=F.object({type:F.literal(`heartbeat`),payload:F.object({sessionId:F.string(),tabId:F.number().optional(),url:F.string().optional()})}),ui=F.discriminatedUnion(`type`,[ai,oi,si,ci,li]);function di(e){let t=ui.safeParse(e);return t.success?t.data:null}let fi=class{connections=new Map;browserConnections=new Map;eventHandlers={};maxConnections=100;setEventHandlers(e){this.eventHandlers=e}addConnection(e,t){if(this.connections.size>=this.maxConnections)throw Error(`Maximum connections (${this.maxConnections}) reached`);let n=crypto.randomUUID(),r={id:n,ws:e,browserId:t,connectedAt:new Date,lastMessageAt:new Date};return this.connections.set(n,r),this.browserConnections.has(t)||this.browserConnections.set(t,new Set),this.browserConnections.get(t).add(n),console.log(`[WebSocketHub] Connection added: ${n} for browser ${t}`),n}removeConnection(e){let t=this.connections.get(e);if(!t)return;this.eventHandlers.onDisconnect?.(t);let n=this.browserConnections.get(t.browserId);n&&(n.delete(e),n.size===0&&this.browserConnections.delete(t.browserId)),this.connections.delete(e),console.log(`[WebSocketHub] Connection removed: ${e}`)}getConnection(e){return this.connections.get(e)}getConnectionsByBrowser(e){let t=this.browserConnections.get(e);return t?Array.from(t).map(e=>this.connections.get(e)).filter(e=>e!==void 0):[]}hasConnection(e){return this.browserConnections.has(e)&&(this.browserConnections.get(e)?.size??0)>0}reassignConnection(e,t){let n=this.connections.get(e);if(!n)return!1;let r=n.browserId;if(r===t)return!0;let i=this.browserConnections.get(r);return i&&(i.delete(e),i.size===0&&this.browserConnections.delete(r)),this.browserConnections.has(t)||this.browserConnections.set(t,new Set),this.browserConnections.get(t).add(e),n.browserId=t,!0}handleMessage(e,t){let n=this.connections.get(e);if(!n){console.warn(`[WebSocketHub] Message from unknown connection: ${e}`);return}n.lastMessageAt=new Date;try{let r=typeof t==`string`?t:new TextDecoder().decode(t),i=JSON.parse(r),a=di(i);if(!a){console.warn(`[WebSocketHub] Invalid message format from ${e}:`,i),this.sendError(n,`Invalid message format`);return}this.routeMessage(n,a)}catch(t){console.error(`[WebSocketHub] Error parsing message from ${e}:`,t),this.sendError(n,`Failed to parse message`)}}routeMessage(e,t){switch(t.type){case`session:register`:this.eventHandlers.onSessionRegister?.(e,t);break;case`task:result`:this.eventHandlers.onTaskResult?.(e,t);break;case`tab:mapped`:this.eventHandlers.onTabMapped?.(e,t);break;case`heartbeat`:this.eventHandlers.onHeartbeat?.(e,t);break;case`ping`:this.sendToConnection(e,{type:`pong`});break}}sendToConnection(e,t){try{return e.ws.send(JSON.stringify(t)),!0}catch(t){return console.error(`[WebSocketHub] Failed to send to ${e.id}:`,t),!1}}sendError(e,t,n){this.sendToConnection(e,{type:`error`,payload:{message:t,code:n}})}pushTask(e,t){let n=this.getConnectionsByBrowser(e);if(n.length===0)return console.warn(`[WebSocketHub] No connections for browser ${e}`),!1;let r=n[0];return this.sendToConnection(r,t)}broadcastPageCreated(e,t,n){let r={type:`page:created`,payload:{pageId:t,browserId:e,url:n}},i=this.getConnectionsByBrowser(e);for(let e of i)this.sendToConnection(e,r)}broadcastPageRemoved(e,t){let n={type:`page:removed`,payload:{pageId:t}},r=this.getConnectionsByBrowser(e);for(let e of r)this.sendToConnection(e,n)}sendSessionAck(e,t,n,r){e.sessionId=n;let i={type:`session:ack`,id:t,payload:{sessionId:n,controlMode:r}};this.sendToConnection(e,i)}getStats(){return{totalConnections:this.connections.size,browserCount:this.browserConnections.size,connections:Array.from(this.connections.values()).map(e=>({id:e.id,browserId:e.browserId,connectedAt:e.connectedAt}))}}closeAll(){for(let[e,t]of this.connections){try{t.ws.close()}catch{}this.removeConnection(e)}}};fi=B([h()],fi);let $=class{constructor(e,t,n){this.pageRegistry=e,this.browserService=t,this.toolExecutor=n}getPage(e){return this.pageRegistry.get(e)}resolvePage(e){let t=this.pageRegistry.get(e);if(!t)throw Error(`Page "${e}" not found`);if(!t.page)throw Error(`Page "${e}" is in extension mode and has no Playwright page`);return t.page}resolvePageEntry(e){let t=this.pageRegistry.get(e);if(!t)throw Error(`Page "${e}" not found`);return t}success(e){return{content:[{type:`text`,text:e}]}}successJson(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}successImage(e,t=`image/png`){return{content:[{type:`image`,data:e,mimeType:t}]}}error(e){return{content:[{type:`text`,text:e}],isError:!0}}async safeExecute(e){try{return await e()}catch(e){let t=e instanceof Error?e.message:String(e);return this.error(t)}}async executeWithMode(e,t,n,r){return this.toolExecutor.execute(e,t,n,r)}};$=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],$);var pi;const mi=U.extend({pageId:F.string().describe(`Page ID to operate on`),limit:F.number().int().min(1).max(10).optional().default(5).describe(`Maximum number of candidates to return`)});async function hi(e,t,n=5){return(await _i(e,t.frame)).evaluate(({selector:e,limit:t})=>{let n=e=>(e??``).replace(/\s+/g,` `).trim(),r=(e,t,r=!1)=>{if(!t)return!1;let i=n(e),a=n(t);return!i||!a?!1:r?i===a:i.toLowerCase().includes(a.toLowerCase())},i=e=>{if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement){if(typeof e.labels?.length==`number`&&e.labels.length>0)return n(e.labels[0]?.textContent);if(e.id){let t=document.querySelector(`label[for="${CSS.escape(e.id)}"]`);if(t?.textContent)return n(t.textContent)}}let t=e.closest(`label`);return t?.textContent?n(t.textContent):null},a=e=>{let t=e.getAttribute(`aria-label`);if(t)return n(t);let r=e.getAttribute(`aria-labelledby`);if(r){let e=r.split(/\s+/).map(e=>n(document.getElementById(e)?.textContent)).filter(Boolean);if(e.length>0)return e.join(` `)}return i(e)||(e instanceof HTMLInputElement&&e.placeholder?n(e.placeholder):n(e.textContent))},o=e=>{let t=e.getAttribute(`role`);if(t)return t;if(e instanceof HTMLInputElement)return{button:`button`,checkbox:`checkbox`,email:`textbox`,number:`spinbutton`,password:`textbox`,radio:`radio`,range:`slider`,search:`searchbox`,submit:`button`,tel:`textbox`,text:`textbox`,url:`textbox`}[(e.type||`text`).toLowerCase()]||`textbox`;let n=e.tagName.toLowerCase();return{a:e.hasAttribute(`href`)?`link`:`none`,button:`button`,img:`img`,option:`option`,select:`combobox`,textarea:`textbox`}[n]||`none`},s=new Set([`html`,`body`,`head`,`script`,`style`,`noscript`,`template`,`meta`,`link`]),c=t=>{if(s.has(t.tagName.toLowerCase()))return null;let c=e.exact??!1,l=a(t),u=i(t),d=t.getAttribute(`placeholder`),f=n(t.textContent),p=o(t),m=t.getAttribute(`data-testid`)||t.getAttribute(`data-test-id`),h=t.getBoundingClientRect(),g=h.width>0&&h.height>0,_=!(t instanceof HTMLButtonElement||t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement)||!t.disabled,v=0,y=0,b=[],x=p!==`none`||t instanceof HTMLButtonElement||t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLAnchorElement&&t.hasAttribute(`href`);return e.role&&p===e.role&&(v+=30,y+=1,b.push(`role match`)),e.name&&(r(l,e.name,!0)?(v+=60,y+=1,b.push(`exact accessible name match`)):r(l,e.name,c)&&(v+=35,y+=1,b.push(`accessible name match`))),e.label&&(r(u,e.label,!0)?(v+=55,y+=1,b.push(`exact label match`)):r(u,e.label,c)&&(v+=30,y+=1,b.push(`label match`))),e.placeholder&&r(d,e.placeholder,c)&&(v+=25,y+=1,b.push(`placeholder match`)),e.text&&r(f,e.text,c)&&(v+=20,y+=1,b.push(`text match`)),e.testId&&m===e.testId&&(v+=50,y+=1,b.push(`test id match`)),x&&(v+=8,b.push(`interactive element`)),g&&(v+=10,b.push(`visible`)),_&&(v+=5,b.push(`enabled`)),y===0?null:{score:v,reasons:b,candidate:{tagName:t.tagName.toLowerCase(),id:t.id||``,className:t.getAttribute(`class`)||``,role:p,accessibleName:l,label:u,placeholder:d,testId:m,textContent:f.slice(0,500),visible:g,enabled:_,rect:{x:h.x,y:h.y,width:h.width,height:h.height}}}},l=Array.from(document.querySelectorAll(`*`)).map(e=>c(e)).filter(e=>e!==null&&e.score>0).sort((e,t)=>t.score-e.score).slice(0,t);return{totalCandidates:l.length,candidates:l}},{selector:t,limit:n})}let gi=class extends ${static{pi=this}static TOOL_NAME=`browser_resolve_locator`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return mi}getDefinition(){return{name:pi.TOOL_NAME,description:`Returns ranked locator candidates for a selector intent. Use this after a failed click/fill to recover using semantic matches instead of brittle guesses.`,inputSchema:F.toJSONSchema(mi,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>this.executeWithMode(pi.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=this.buildSelector(e),r=await hi(t,n,e.limit??5);return this.successJson({pageId:e.pageId,selector:n,...r})}))}buildSelector(e){return{uid:e.uid,role:e.role,name:e.name,label:e.label,placeholder:e.placeholder,testId:e.testId,text:e.text,exact:e.exact,selector:e.selector,xpath:e.xpath,frame:e.frame}}};gi=pi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],gi);async function _i(e,t){if(!t)return e;let n=await(await e.locator(t).first().elementHandle())?.contentFrame();if(!n)throw Error(`Frame not found: ${t}`);return n}var vi;const yi=U.extend({pageId:F.string().describe(`Optional page ID to override browser current page`),clickCount:F.number().optional().default(1).describe(`Number of clicks (default: 1, use 2 for double-click)`),button:F.enum([`left`,`middle`,`right`]).optional().default(`left`).describe(`Mouse button to use`),modifiers:F.array(F.enum([`Alt`,`Control`,`Meta`,`Shift`])).optional().describe(`Modifier keys to hold during click`),delay:F.number().optional().describe(`Time to wait between mousedown and mouseup in ms`),position:F.object({x:F.number(),y:F.number()}).optional().describe(`Position offset within the element`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds (default: ${V})`)});let bi=class extends ${static{vi=this}static TOOL_NAME=`browser_click`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return yi}getDefinition(){return{name:vi.TOOL_NAME,description:`Clicks on an element on the page. Supports CSS selector, XPath, text content, or accessibility snapshot UID for element identification.`,inputSchema:F.toJSONSchema(yi,{reused:`inline`})}}async execute(e){return this.executeWithMode(vi.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=this.buildSelector(e);try{return await(await this.elementLocator.locate(t,n)).click({clickCount:e.clickCount,button:e.button,modifiers:e.modifiers,delay:e.delay,position:e.position,timeout:e.timeout}),this.success(`Clicked element successfully`)}catch(r){let i=r instanceof Error?r.message:String(r);if(!i.includes(`Element not found`))throw r;let a=await hi(t,n,5);return this.error(JSON.stringify({error:i,pageId:e.pageId,selector:n,...a},null,2))}})}buildSelector(e){return{uid:e.uid,role:e.role,name:e.name,label:e.label,placeholder:e.placeholder,testId:e.testId,text:e.text,exact:e.exact,selector:e.selector,xpath:e.xpath,frame:e.frame}}};bi=vi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],bi);var xi;const Si=F.object({browserId:F.string().describe(`The browser ID to close (returned by browser_launch)`)});let Ci=class extends ${static{xi=this}static TOOL_NAME=`browser_close`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Si}getDefinition(){return{name:xi.TOOL_NAME,description:`Closes a browser instance by its ID. Terminates the browser process, cleans up all associated pages, and releases resources. Works for all modes: playwright, extension, vm (Docker), and stealth.`,inputSchema:F.toJSONSchema(Si,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.browserService.getBrowser(e.browserId);if(!t)return this.error(`Browser "${e.browserId}" not found`);let n=t.pageIds.size,r=t.mode??`playwright`;return await this.browserService.closeBrowser(e.browserId),this.successJson({closedBrowserId:e.browserId,mode:r,pagesRemoved:n,message:`Browser "${e.browserId}" closed successfully`})})}};Ci=xi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Ci);var wi;const Ti=F.object({pageId:F.string().describe(`Page ID to close`)});let Ei=class extends ${static{wi=this}static TOOL_NAME=`browser_close_page`;constructor(e,t,n,r){super(e,t,n),this.profileService=r}getInputSchema(){return Ti}getDefinition(){return{name:wi.TOOL_NAME,description:`Closes a page (tab) by its ID. Removes the page from the session and registry. If closed page was current, another page becomes current.`,inputSchema:F.toJSONSchema(Ti,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.pageRegistry.get(e.pageId);if(!t)return this.error(`Page "${e.pageId}" not found`);let{browserId:n,profileName:r,mode:i}=t,a=this.browserService.getBrowser(n);if(!a)return this.error(`Browser "${n}" not found`);let o=a.currentPageId===e.pageId,s=a.pageIds.size===1;if(i===`extension`||i===`vm`){let t=await this.executeWithMode(wi.TOOL_NAME,e.pageId,e,async()=>this.error(`Page "${e.pageId}" is not in extension mode`));if(t.isError)return t;if(await this.browserService.finalizeExtensionRecording(n,e.pageId),this.pageRegistry.remove(e.pageId),a.pageIds.delete(e.pageId),a.currentPageId===e.pageId&&(a.currentPageId=Array.from(a.pageIds)[0]??null),s)try{await this.browserService.closeBrowser(n)}catch(e){console.error(`Failed to close extension browser "${n}" after last tab shutdown:`,e)}return this.successJson({closedPageId:e.pageId,browserId:n,wasCurrentPage:o,newCurrentPageId:a.currentPageId,browserKeptOpen:!s,message:s?`Page "${e.pageId}" closed. Browser "${n}" was also closed because no tabs remained.`:`Page "${e.pageId}" closed successfully`})}if(!t.page)return this.error(`Page "${e.pageId}" is in extension mode and cannot be closed via Playwright`);await t.page.close();let c=this.browserService.getBrowser(n)?.currentPageId??null;if(s){if(r&&a.context)try{let e=await a.context.storageState();await this.profileService.saveStorageState(r,e)}catch(e){console.error(`Failed to auto-save profile "${r}":`,e)}if(!r)try{await this.browserService.closeBrowser(n)}catch(e){console.error(`Failed to auto-close browser "${n}":`,e)}}return this.successJson({closedPageId:e.pageId,browserId:n,wasCurrentPage:o,newCurrentPageId:c,browserKeptOpen:s&&!!r,message:s&&r?`Page "${e.pageId}" closed. Browser kept open for profile "${r}" reuse.`:`Page "${e.pageId}" closed successfully`})})}};Ei=wi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ProfileService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Ei);var Di;const Oi=F.object({name:F.string().describe(`Unique profile name`),browserType:F.enum([`chromium`,`firefox`,`webkit`]).optional().default(`chromium`).describe(`Preferred browser type for the profile`),viewport:F.object({width:F.number(),height:F.number()}).optional(),userAgent:F.string().optional(),locale:F.string().optional(),timezone:F.string().optional(),colorScheme:F.enum([`light`,`dark`,`no-preference`]).optional()});let ki=class{static{Di=this}static TOOL_NAME=`browser_create_profile`;constructor(e){this.profileService=e}getInputSchema(){return Oi}getDefinition(){return{name:Di.TOOL_NAME,description:`Creates a browser profile for persistent browser settings and session reuse.`,inputSchema:F.toJSONSchema(Oi,{reused:`inline`})}}async execute(e){try{let t=await this.profileService.create({name:e.name,browserType:e.browserType??`chromium`,viewport:e.viewport,userAgent:e.userAgent,locale:e.locale,timezone:e.timezone,colorScheme:e.colorScheme});return{content:[{type:`text`,text:JSON.stringify(t,null,2)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}};ki=Di=B([h(),z(0,m(L.ProfileService)),R(`design:paramtypes`,[Object])],ki);var Ai;const ji=F.object({name:F.string().describe(`Name of the profile to delete`)});let Mi=class{static{Ai=this}static TOOL_NAME=`browser_delete_profile`;constructor(e){this.profileService=e}getInputSchema(){return ji}getDefinition(){return{name:Ai.TOOL_NAME,description:`Deletes a browser profile and all its associated storage state data.`,inputSchema:F.toJSONSchema(ji,{reused:`inline`})}}async execute(e){try{return await this.profileService.delete(e.name),{content:[{type:`text`,text:JSON.stringify({success:!0,message:`Profile "${e.name}" deleted successfully`},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};Mi=Ai=B([h(),z(0,m(L.ProfileService)),R(`design:paramtypes`,[Object])],Mi);var Ni;const Pi=F.object({basePath:F.string().describe(`Absolute path to search for e2e projects (required)`),projectName:F.string().optional().describe(`Filter by project name (e.g., 'boomlink-e2e')`),specPattern:F.string().optional().describe(`Filter specs by name pattern (supports * and ? wildcards, e.g., "auth*", "*.spec.ts")`),includeMetadata:F.boolean().optional().default(!0).describe(`Include metadata like hasArgsSchema detection (default: true)`)});let Fi=class{static{Ni=this}static TOOL_NAME=`discover_specs`;constructor(e){this.specDiscoveryService=e}getInputSchema(){return Pi}getDefinition(){return{name:Ni.TOOL_NAME,description:`Discovers e2e projects (directories with playwright.config.ts) and their spec files. Use to find available specs before running them.`,inputSchema:F.toJSONSchema(Pi,{reused:`inline`})}}async execute(e){if(!e.basePath||!x(e.basePath))return{content:[{type:`text`,text:`Error: basePath must be an absolute path (e.g., /Users/me/project)`}],isError:!0};try{let t=e.basePath,n=e.includeMetadata!==!1;if(e.projectName||e.specPattern){let r=await this.specDiscoveryService.filterSpecs(t,{projectName:e.projectName,specPattern:e.specPattern}),i={filter:{basePath:t,projectName:e.projectName,specPattern:e.specPattern},totalSpecs:r.length,specs:r.map(e=>({name:e.name,path:e.path,relativePath:e.relativePath,...n&&{hasArgsSchema:e.hasArgsSchema}}))};return{content:[{type:`text`,text:JSON.stringify(i,null,2)}]}}let r=await this.specDiscoveryService.discoverProjects(t),i={basePath:t,totalProjects:r.length,totalSpecs:r.reduce((e,t)=>e+t.specs.length,0),projects:r.map(e=>({name:e.name,path:e.path,configPath:e.configPath,testDir:e.testDir,specCount:e.specs.length,specs:e.specs.map(e=>({name:e.name,path:e.path,relativePath:e.relativePath,...n&&{hasArgsSchema:e.hasArgsSchema}}))}))};return{content:[{type:`text`,text:JSON.stringify(i,null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error discovering specs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};Fi=Ni=B([h(),z(0,m(L.SpecDiscoveryService)),R(`design:paramtypes`,[Object])],Fi);var Ii;const Li=U.extend({x:F.number().optional().describe(`X coordinate`),y:F.number().optional().describe(`Y coordinate`)}),Ri=F.object({pageId:F.string().describe(`Page ID to operate on`),source:U.describe(`Source element selector`),target:Li.describe(`Target element selector or coordinates`),force:F.boolean().optional().default(!1).describe(`Bypass actionability checks`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let zi=class extends ${static{Ii=this}static TOOL_NAME=`browser_drag`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return Ri}getDefinition(){return{name:Ii.TOOL_NAME,description:`Drags an element to a target location or element.`,inputSchema:F.toJSONSchema(Ri,{reused:`inline`})}}async execute(e){return this.executeWithMode(Ii.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=await this.elementLocator.locate(t,e.source);if(`x`in e.target&&`y`in e.target)await n.dragTo(t.locator(`body`),{targetPosition:{x:e.target.x,y:e.target.y},force:e.force,timeout:e.timeout});else{let r=await this.elementLocator.locate(t,e.target);await n.dragTo(r,{force:e.force,timeout:e.timeout})}return this.success(`Dragged element successfully`)})}};zi=Ii=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],zi);var Bi;const Vi=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),geolocation:F.object({latitude:F.number().min(-90).max(90),longitude:F.number().min(-180).max(180),accuracy:F.number().min(0).optional()}).optional().describe(`Geolocation coordinates to emulate`),locale:F.string().optional().describe(`Locale to emulate (e.g., "en-US", "de-DE")`),timezone:F.string().optional().describe(`Timezone to emulate (e.g., "America/New_York", "Europe/London")`),userAgent:F.string().optional().describe(`User agent string to use`),offline:F.boolean().optional().describe(`Whether to emulate offline mode`),colorScheme:F.enum([`light`,`dark`,`no-preference`]).optional().describe(`Color scheme preference`),reducedMotion:F.enum([`reduce`,`no-preference`]).optional().describe(`Reduced motion preference`)});let Hi=class extends ${static{Bi=this}static TOOL_NAME=`browser_emulate`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Vi}getDefinition(){return{name:Bi.TOOL_NAME,description:`Emulates device characteristics like geolocation, timezone, locale, color scheme, and network conditions.`,inputSchema:F.toJSONSchema(Vi,{reused:`inline`})}}async execute(e){return this.executeWithMode(Bi.TOOL_NAME,e.pageId,e,async()=>this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId),n=t.context,r=t.page;if(!n||!r)return this.error(`Page "${e.pageId}" is in extension mode and cannot be emulated via Playwright`);let i={};return e.geolocation&&(await n.setGeolocation(e.geolocation),i.geolocation=e.geolocation),e.offline!==void 0&&(await n.setOffline(e.offline),i.offline=e.offline),e.colorScheme&&(await r.emulateMedia({colorScheme:e.colorScheme}),i.colorScheme=e.colorScheme),e.reducedMotion&&(await r.emulateMedia({reducedMotion:e.reducedMotion}),i.reducedMotion=e.reducedMotion),e.locale&&(i.locale=e.locale,i.localeNote=`Locale changes may require a new browser context to take full effect`),e.timezone&&(i.timezone=e.timezone,i.timezoneNote=`Timezone changes may require a new browser context to take full effect`),e.userAgent&&(i.userAgent=e.userAgent,i.userAgentNote=`User agent changes may require a new browser context to take full effect`),this.successJson({success:!0,pageId:t.id,appliedSettings:i})}))}};Hi=Bi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Hi);var Ui;const Wi=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),script:F.string().describe(`JavaScript code to execute. Can be a function body or expression (e.g., "document.title" or "() => window.location.href")`),arg:F.unknown().optional().describe(`Optional argument to pass to the script if it is a function`)});let Gi=class extends ${static{Ui=this}static TOOL_NAME=`browser_evaluate_script`;static INLINE_SIZE_THRESHOLD=1e4;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Wi}getDefinition(){return{name:Ui.TOOL_NAME,description:`Executes JavaScript code in the page context and returns the result. The script can be a function body or an expression.`,inputSchema:F.toJSONSchema(Wi,{reused:`inline`})}}async execute(e){return this.executeWithMode(Ui.TOOL_NAME,e.pageId,{script:e.script,arg:e.arg},async()=>{let t=await this.resolvePage(e.pageId).evaluate(e.script,e.arg),n;try{n=JSON.stringify(t,null,2)}catch{n=String(t)}if(Buffer.byteLength(n,`utf-8`)<=Ui.INLINE_SIZE_THRESHOLD)return this.success(n);let r=`evaluate-${I()}.json`,i=S(N(),r);return await j(i,n,`utf-8`),this.success(`Result saved to: ${i}`)})}};Gi=Ui=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Gi);var Ki;const qi=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),selector:F.string().optional().describe(`CSS selector for the element to assert on (not required for page-level assertions like toHaveTitle, toHaveURL)`),assertion:F.enum([`toBeVisible`,`toBeHidden`,`toBeEnabled`,`toBeDisabled`,`toBeChecked`,`toBeEditable`,`toBeFocused`,`toBeEmpty`,`toHaveText`,`toContainText`,`toHaveValue`,`toHaveAttribute`,`toHaveClass`,`toHaveCount`,`toHaveCSS`,`toHaveId`,`toHaveTitle`,`toHaveURL`]).describe(`Type of assertion to perform`),expected:F.union([F.string(),F.number()]).optional().describe(`Expected value for assertions that require one (e.g., toHaveText, toHaveValue)`),attributeName:F.string().optional().describe(`Attribute name for toHaveAttribute or toHaveCSS assertion`),not:F.boolean().optional().describe(`Whether to negate the assertion (not.toBeVisible, etc.)`),timeout:F.number().optional().describe(`Timeout in milliseconds for the assertion (default: 5000)`)});let Ji=class extends ${static{Ki=this}static TOOL_NAME=`browser_expect`;constructor(e,t,n,r){super(e,t,n),this.extensionPageProxy=r}getInputSchema(){return qi}getDefinition(){return{name:Ki.TOOL_NAME,description:`Performs Playwright expect() assertions on page elements. Supports various assertion types like toBeVisible, toHaveText, toHaveValue, toBeEnabled, etc.`,inputSchema:F.toJSONSchema(qi,{reused:`inline`})}}async execute(t){return this.safeExecute(async()=>{let n=this.resolvePageEntry(t.pageId),r=n.mode===`extension`?(this.extensionPageProxy.setTarget(n.id,n.browserId),this.extensionPageProxy):this.resolvePage(t.pageId),{selector:i,assertion:a,expected:o,attributeName:s,not:c,timeout:l=5e3}=t,u=[`toHaveTitle`,`toHaveURL`];if(!u.includes(a)&&!i)return this.error(`Selector is required for assertion type: ${a}`);try{if(u.includes(a)){let t=c?e(r).not:e(r);a===`toHaveTitle`?await t.toHaveTitle(o,{timeout:l}):a===`toHaveURL`&&await t.toHaveURL(o,{timeout:l})}else{let t=r.locator(i),n=c?e(t).not:e(t);switch(a){case`toBeVisible`:await n.toBeVisible({timeout:l});break;case`toBeHidden`:await n.toBeHidden({timeout:l});break;case`toBeEnabled`:await n.toBeEnabled({timeout:l});break;case`toBeDisabled`:await n.toBeDisabled({timeout:l});break;case`toBeChecked`:await n.toBeChecked({timeout:l});break;case`toBeEditable`:await n.toBeEditable({timeout:l});break;case`toBeFocused`:await n.toBeFocused({timeout:l});break;case`toBeEmpty`:await n.toBeEmpty({timeout:l});break;case`toHaveText`:await n.toHaveText(o,{timeout:l});break;case`toContainText`:await n.toContainText(o,{timeout:l});break;case`toHaveValue`:await n.toHaveValue(o,{timeout:l});break;case`toHaveAttribute`:if(!s)return this.error(`attributeName is required for toHaveAttribute assertion`);await n.toHaveAttribute(s,o,{timeout:l});break;case`toHaveClass`:await n.toHaveClass(o,{timeout:l});break;case`toHaveCount`:await n.toHaveCount(o,{timeout:l});break;case`toHaveCSS`:if(!s)return this.error(`attributeName (CSS property name) is required for toHaveCSS assertion`);await n.toHaveCSS(s,o,{timeout:l});break;case`toHaveId`:await n.toHaveId(o,{timeout:l});break;default:return this.error(`Unsupported assertion type: ${a}`)}}return this.successJson({success:!0,assertion:c?`not.${a}`:a,selector:i??null,passed:!0})}catch(e){let t=e instanceof Error?e.message:`Assertion failed`;return this.successJson({success:!0,assertion:c?`not.${a}`:a,selector:i??null,passed:!1,failureMessage:t})}})}};Ji=Ki=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ExtensionPageProxy)),R(`design:paramtypes`,[Object,Object,Object,Object])],Ji);var Yi;const Xi=U.extend({pageId:F.string().describe(`Page ID to operate on`),value:F.string().describe(`The text value to fill into the input`),force:F.boolean().optional().default(!1).describe(`Bypass actionability checks`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let Zi=class extends ${static{Yi=this}static TOOL_NAME=`browser_fill`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return Xi}getDefinition(){return{name:Yi.TOOL_NAME,description:`Fills an input field with text, clearing any existing content first. Use for form inputs, textareas, and contenteditable elements.`,inputSchema:F.toJSONSchema(Xi,{reused:`inline`})}}async execute(e){return this.executeWithMode(Yi.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=this.buildSelector(e);try{return await(await this.elementLocator.locate(t,n)).fill(e.value,{force:e.force,timeout:e.timeout}),this.success(`Filled element with: "${e.value}"`)}catch(r){let i=r instanceof Error?r.message:String(r);if(!i.includes(`Element not found`))throw r;let a=await hi(t,n,5);return this.error(JSON.stringify({error:i,pageId:e.pageId,selector:n,...a},null,2))}})}buildSelector(e){return{uid:e.uid,role:e.role,name:e.name,label:e.label,placeholder:e.placeholder,testId:e.testId,text:e.text,exact:e.exact,selector:e.selector,xpath:e.xpath,frame:e.frame}}};Zi=Yi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Zi);var Qi;const $i=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),requestId:F.string().describe(`The ID of the network request to retrieve (e.g., "req-1")`)});let ea=class extends ${static{Qi=this}static TOOL_NAME=`browser_get_network_request`;constructor(e,t,n,r){super(e,t,n),this.pageMonitor=r}getInputSchema(){return $i}getDefinition(){return{name:Qi.TOOL_NAME,description:`Retrieves detailed information about a specific network request by ID, including headers, body, and response data.`,inputSchema:F.toJSONSchema($i,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId);return this.executeWithMode(Qi.TOOL_NAME,t.id,e,async()=>{let n=this.pageMonitor.getNetworkRequest(t.id,e.requestId);return n?this.successJson({id:n.id,url:n.url,method:n.method,resourceType:n.resourceType,headers:n.headers,postData:n.postData,timestamp:n.timestamp.toISOString(),response:n.response?{status:n.response.status,statusText:n.response.statusText,headers:n.response.headers,timing:n.response.timing}:null}):this.error(`Network request "${e.requestId}" not found for page "${t.id}"`)})})}};ea=Qi=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.PageMonitorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],ea);var ta;const na=F.object({pageId:F.string().describe(`Page ID to operate on`),waitUntil:F.enum([`load`,`domcontentloaded`,`networkidle`,`commit`]).optional().default(`load`).describe(`Wait until condition`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let ra=class extends ${static{ta=this}static TOOL_NAME=`browser_go_back`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return na}getDefinition(){return{name:ta.TOOL_NAME,description:`Navigates the browser back in history.`,inputSchema:F.toJSONSchema(na,{reused:`inline`})}}async execute(e){return this.executeWithMode(ta.TOOL_NAME,e.pageId,e,async()=>{let t=await this.resolvePage(e.pageId).goBack({waitUntil:e.waitUntil,timeout:e.timeout});if(t===null)return this.success(`No previous page in history`);let n=t.status();return this.success(`Navigated back (status: ${n})`)})}};ra=ta=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],ra);var ia;const aa=F.object({pageId:F.string().describe(`Page ID to operate on`),waitUntil:F.enum([`load`,`domcontentloaded`,`networkidle`,`commit`]).optional().default(`load`).describe(`Wait until condition`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let oa=class extends ${static{ia=this}static TOOL_NAME=`browser_go_forward`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return aa}getDefinition(){return{name:ia.TOOL_NAME,description:`Navigates the browser forward in history.`,inputSchema:F.toJSONSchema(aa,{reused:`inline`})}}async execute(e){return this.executeWithMode(ia.TOOL_NAME,e.pageId,e,async()=>{let t=await this.resolvePage(e.pageId).goForward({waitUntil:e.waitUntil,timeout:e.timeout});if(t===null)return this.success(`No next page in history`);let n=t.status();return this.success(`Navigated forward (status: ${n})`)})}};oa=ia=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],oa);var sa;const ca=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),accept:F.boolean().describe(`Whether to accept (true) or dismiss (false) the dialog`),promptText:F.string().optional().describe(`Text to enter for prompt dialogs (only used when accept is true)`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds to wait for dialog (default: ${V})`)});let la=class extends ${static{sa=this}static TOOL_NAME=`browser_handle_dialog`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return ca}getDefinition(){return{name:sa.TOOL_NAME,description:`Handles JavaScript dialogs (alert, confirm, prompt) by accepting or dismissing them. Set up a listener before triggering the action that opens the dialog.`,inputSchema:F.toJSONSchema(ca,{reused:`inline`})}}async execute(e){return this.safeExecute(()=>this.executeWithMode(sa.TOOL_NAME,e.pageId,{accept:e.accept,promptText:e.promptText,timeout:e.timeout},async()=>{let t=this.resolvePage(e.pageId),n=e.timeout??18e4,r=setTimeout(()=>{t.off(`dialog`,i)},n),i=async n=>{clearTimeout(r);try{e.accept?await n.accept(e.promptText):await n.dismiss()}finally{t.off(`dialog`,i)}};return t.once(`dialog`,i),this.successJson({armed:!0,action:e.accept?`accept`:`dismiss`,timeout:n,promptText:e.promptText??null})}))}};la=sa=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],la);var ua;const da=U.extend({pageId:F.string().describe(`Page ID to operate on`),position:F.object({x:F.number(),y:F.number()}).optional().describe(`Position offset within element`),modifiers:F.array(F.enum([`Alt`,`Control`,`Meta`,`Shift`])).optional().describe(`Modifier keys to hold`),force:F.boolean().optional().default(!1).describe(`Bypass actionability checks`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let fa=class extends ${static{ua=this}static TOOL_NAME=`browser_hover`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return da}getDefinition(){return{name:ua.TOOL_NAME,description:`Hovers over an element to trigger hover states, tooltips, or dropdown menus.`,inputSchema:F.toJSONSchema(da,{reused:`inline`})}}async execute(e){return this.executeWithMode(ua.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId);return await(await this.elementLocator.locate(t,e)).hover({position:e.position,modifiers:e.modifiers,force:e.force,timeout:e.timeout}),this.success(`Hovered over element successfully`)})}};fa=ua=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],fa);var pa;const ma=U.extend({pageId:F.string().describe(`Page ID to operate on`)});let ha=class extends ${static{pa=this}static TOOL_NAME=`browser_inspect_element`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return ma}getDefinition(){return{name:pa.TOOL_NAME,description:`Inspects a matching element and returns structured metadata such as role, accessible name, label, visibility, and bounds. Use this for selector debugging and agent recovery.`,inputSchema:F.toJSONSchema(ma,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>this.executeWithMode(pa.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=this.buildSelector(e),r=await this.elementLocator.locateAll(t,n),i=await r.count();if(i===0)return this.successJson({pageId:e.pageId,selector:n,matched:!1,matchCount:0,element:null});let a=r.first(),[o,s,c,l]=await Promise.all([a.isVisible().catch(()=>!1),a.isEnabled().catch(()=>!1),a.boundingBox().catch(()=>null),a.evaluate(e=>{let t=e=>(e??``).replace(/\s+/g,` `).trim(),n=e=>{if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement){if(typeof e.labels?.length==`number`&&e.labels.length>0)return t(e.labels[0]?.textContent);if(e.id){let n=document.querySelector(`label[for="${CSS.escape(e.id)}"]`);if(n?.textContent)return t(n.textContent)}}let n=e.closest(`label`);return n?.textContent?t(n.textContent):null},r=e=>{let r=e.getAttribute(`aria-label`);if(r)return t(r);let i=e.getAttribute(`aria-labelledby`);if(i){let e=i.split(/\s+/).map(e=>t(document.getElementById(e)?.textContent)).filter(Boolean);if(e.length>0)return e.join(` `)}return n(e)||(e instanceof HTMLInputElement&&e.placeholder?t(e.placeholder):e instanceof HTMLImageElement&&e.alt?t(e.alt):t(e.textContent))},i=e=>{let t=e.getAttribute(`role`);if(t)return t;if(e instanceof HTMLInputElement)return{button:`button`,checkbox:`checkbox`,email:`textbox`,number:`spinbutton`,password:`textbox`,radio:`radio`,range:`slider`,search:`searchbox`,submit:`button`,tel:`textbox`,text:`textbox`,url:`textbox`}[(e.type||`text`).toLowerCase()]||`textbox`;let n=e.tagName.toLowerCase();return{a:e.hasAttribute(`href`)?`link`:`none`,button:`button`,img:`img`,option:`option`,select:`combobox`,textarea:`textbox`}[n]||`none`},a=e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement?e.value:null;return{tagName:e.tagName.toLowerCase(),id:e.id||``,className:e.getAttribute(`class`)||``,role:i(e),accessibleName:r(e),label:n(e),placeholder:e.getAttribute(`placeholder`),testId:e.getAttribute(`data-testid`)||e.getAttribute(`data-test-id`),textContent:t(e.textContent).slice(0,500),value:a}})]),u={...l,visible:o,enabled:s,rect:c?{x:c.x,y:c.y,width:c.width,height:c.height}:null};return this.successJson({pageId:e.pageId,selector:n,matched:!0,matchCount:i,element:u})}))}buildSelector(e){return{uid:e.uid,role:e.role,name:e.name,label:e.label,placeholder:e.placeholder,testId:e.testId,text:e.text,exact:e.exact,selector:e.selector,xpath:e.xpath,frame:e.frame}}};ha=pa=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],ha);var ga;const _a=[`pnpm-workspace.yaml`,`nx.json`,`.git`],va=`about:blank`,ya=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`,ba=`[LaunchBrowserTool]`,xa=new Set([`docker`,`docker-vm`,`docker-chromium`,`docker-chrome-testing`]);function Sa(e=process.cwd()){let t=v.resolve(e);for(;;){for(let e of _a)if(D(v.join(t,e)))return t;let e=v.dirname(t);if(e===t)return process.cwd();t=e}}const Ca=Ie(he);async function wa(e){try{let{stdout:t}=await Ca(`git`,[`rev-parse`,`--git-common-dir`],{cwd:e}),n=t.trim(),r=v.isAbsolute(n)?n:v.join(e,n),i=v.dirname(r);return i===e?null:i}catch{return null}}const Ta=F.object({server:F.string().describe(`Proxy server URL (e.g., "http://proxy.example.com:8080")`),username:F.string().optional().describe(`Proxy username for authentication`),password:F.string().optional().describe(`Proxy password for authentication`),bypass:F.string().optional().describe(`Comma-separated list of hosts to bypass the proxy (e.g., "localhost,127.0.0.1")`)}),Ea=F.object({mode:F.enum([`playwright`,`extension`,`vm`,`stealth`]).optional().default(`playwright`).describe(`Execution mode. "playwright" uses Playwright APIs. "extension" uses Playwright with Chrome extension. "vm" launches in an isolated runtime (for example via Docker). "stealth" spawns Chrome directly without automation flags.`),browserType:F.enum([`chromium`,`firefox`,`webkit`]).optional().default(`chromium`).describe(`Browser type to use (only applies to playwright mode)`),headless:F.boolean().optional().default(!0).describe(`Run browser in headless mode (only applies to playwright mode)`),url:F.string().optional().describe(`Optional URL to navigate to after launch`),profileName:F.string().optional().describe(`Optional profile name to use for browser settings and persistent storage`),vmCommand:F.string().optional().describe(`Executable/alias for vm mode launch (e.g., "docker" for isolated container runtime)`),vmCommandArgs:F.array(F.string()).optional().describe(`Browser arguments for vm command runtime`),vmDockerBrowser:F.enum([`cloakbrowser`,`chrome-for-testing`]).optional().describe(`Docker browser runtime for vm mode. "cloakbrowser" (default) uses stealth CloakBrowser with C++ anti-detection patches. "chrome-for-testing" uses Google Chrome for Testing.`),vmBrowserId:F.string().optional().describe(`Optional browserId override for vm mode`),startupDelayMs:F.number().optional().describe(`Optional startup delay in milliseconds before returning in vm mode`),vmServerUrl:F.string().optional().describe(`Explicit MCP server URL reachable from inside VM (e.g., "http://host.docker.internal:3200"). Overrides registry discovery.`),vmExtensionPath:F.string().optional().describe(`Explicit extension path that exists inside VM filesystem. Useful when VM does not share host workspace paths.`),vmEnableVnc:F.boolean().optional().default(!1).describe(`Expose VNC/noVNC ports when using Docker VM runtime`),vmVncPort:F.number().min(1).max(65535).optional().describe(`Host port for VNC (container port 5900) when vmEnableVnc is true`),vmNoVncPort:F.number().min(1).max(65535).optional().describe(`Host port for noVNC web UI (container port 7900) when vmEnableVnc is true`),vmVncPassword:F.string().optional().describe(`Optional VNC password for Docker VM runtime`),baseURL:F.string().optional().describe(`Base URL for relative navigation (e.g., page.goto("/") will use this as the base)`),videoDir:F.string().optional().describe(`Directory to save video recordings. If provided, video will be recorded for all pages. Videos are saved when the page or context is closed.`),proxy:F.union([Ta,F.literal(!0)]).optional().describe(`Proxy configuration for routing browser traffic. Pass an object for explicit proxy settings, or true to load session.yaml from the proxy config directory.`)});let Da=class{static{ga=this}static TOOL_NAME=`browser_launch`;constructor(e,t,n=new X){this.browserService=e,this.stealthLauncher=t,this.proxyConfigService=n}getInputSchema(){return Ea}getDefinition(){return{name:ga.TOOL_NAME,description:`Launches a new browser instance. Returns browserId and pageId for subsequent browser operations. Use mode "extension" for bot-detection-free automation via Chrome extension, or mode "vm" for isolated runtime launch.`,inputSchema:F.toJSONSchema(Ea,{reused:`inline`})}}async execute(e){let t=e.mode??`playwright`;try{let n={...e,proxy:this.proxyConfigService.resolve({proxy:e.proxy,profileName:e.profileName})};return t===`stealth`?await this.launchStealthMode(n):t===`vm`?await this.launchVmMode(n):t===`extension`?await this.launchExtensionMode(n):await this.launchPlaywrightMode(n)}catch(e){return{content:[{type:`text`,text:this.formatLaunchError(e)}],isError:!0}}}formatLaunchError(e){let t=e instanceof Error?e.message:String(e);return/Browser session conflict|already in use|profile ".*" is busy/i.test(t)?`${t}\n\nThis is a browser automation session conflict, not an application failure. Use browser_list_session to see reusable browserId/pageId values, retry after the active launch completes, or launch with a different profileName.`:t}async launchPlaywrightMode(e){let t=await this.browserService.launch({browserType:e.browserType,headless:e.headless,profileName:e.profileName,baseURL:e.baseURL,recordVideo:e.videoDir?{dir:e.videoDir}:void 0,proxy:e.proxy});return e.url&&await t.page.goto(e.url),{content:[{type:`text`,text:JSON.stringify({browserId:t.browserInstance.id,pageId:t.pageId,mode:`playwright`,url:e.url??t.page.url()},null,2)}]}}async launchStealthMode(e){let t=await this.stealthLauncher.launch({url:e.url,profileName:e.profileName,proxy:e.proxy});return{content:[{type:`text`,text:JSON.stringify({browserId:t.id,pageId:`page-${t.id}`,mode:`stealth`,url:e.url??`about:blank`,pid:t.pid},null,2)}]}}async launchExtensionMode(e){ya&&console.warn(`[ExtensionRecordingDebug] launch tool mode=extension videoDir=${e.videoDir??``} url=${e.url??``}`);let t=e.profileName?`browser-${e.profileName}`:`browser-extension-${I()}`,n=e.url??va,r=await this.resolveExtensionBootstrapServerUrl(),i=this.buildVmBootstrapUrl(r,t,n),a=await this.browserService.launchWithExtension({url:i,baseURL:e.baseURL,profileName:e.profileName,recordVideo:e.videoDir?{dir:e.videoDir}:void 0,proxy:e.proxy,mode:`extension`,browserId:t});return{content:[{type:`text`,text:JSON.stringify({browserId:a.browserInstance.id,pageId:a.pageId,mode:`extension`,url:n,pid:a.process.pid},null,2)}]}}async launchVmMode(e){let t=e.vmBrowserId??`browser-vm-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,n=e.url??va,r=e.vmCommand??`docker`,i=await this.resolveVmBootstrapServerUrl(e.vmServerUrl,r),a=this.buildVmBootstrapUrl(i,t,n),o=this.buildVmDockerRuntimeOptions(e),s=await this.browserService.launchWithExtension({url:a,baseURL:e.baseURL,profileName:e.profileName,recordVideo:e.videoDir?{dir:e.videoDir}:void 0,proxy:e.proxy,extensionPath:e.vmExtensionPath??process.env.PLAYWRIGHT_VM_EXTENSION_PATH,mode:`vm`,vmDockerBrowser:e.vmDockerBrowser,command:r,commandArgs:e.vmCommandArgs,dockerRunArgs:o.runArgs,dockerEnv:o.env,dockerEnableVnc:o.enableVnc,browserId:t,startupDelayMs:e.startupDelayMs}),c=o.vnc?{vncUrl:o.vnc.vncUrl,noVncUrl:o.vnc.noVncUrl}:void 0;return{content:[{type:`text`,text:JSON.stringify({browserId:s.browserInstance.id,pageId:s.pageId,mode:`vm`,url:n,pid:s.process.pid,...c?{vm:c}:{}},null,2)}]}}buildVmBootstrapUrl(e,t,n){let r=new URL(e);return r.searchParams.set(`playwright_mcp_connect`,`1`),r.searchParams.set(`playwright_mcp_server`,e),r.searchParams.set(`playwright_mcp_browser_id`,t),r.searchParams.set(`playwright_mcp_navigate`,n),r.toString()}async resolveVmBootstrapServerUrl(e,t){let n=e??process.env.PLAYWRIGHT_VM_SERVER_URL;if(n)return this.normalizeVmServerUrl(n);let r=K(),i=await this.getRegisteredPort(r);return this.isDockerVmCommand(t??`docker`)?q(`host.docker.internal`,i.port):q(i.host,i.port)}async resolveExtensionBootstrapServerUrl(){let e=K(),t=await this.getRegisteredPort(e);return q(t.host,t.port)}normalizeVmServerUrl(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid vmServerUrl: "${e}"`)}if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`vmServerUrl must use http or https: "${e}"`);return t.origin}isDockerVmCommand(e){return xa.has(e.trim().toLowerCase())}buildVmDockerRuntimeOptions(e){let t=e.vmEnableVnc||e.vmVncPort!==void 0||e.vmNoVncPort!==void 0,n=[],r={},i;if(t){let t=e.vmVncPort??5901,r=e.vmNoVncPort??7901;n.push(`-p`,`${t}:5900`,`-p`,`${r}:7900`),i={vncUrl:`vnc://localhost:${t}`,noVncUrl:`http://localhost:${r}/`}}return e.vmVncPassword&&e.vmVncPassword.trim().length>0&&(r.SE_VNC_PASSWORD=e.vmVncPassword.trim()),{runArgs:n.length>0?n:void 0,env:Object.keys(r).length>0?r:void 0,enableVnc:t,vnc:i}}getFallbackPort(){try{return Hn()}catch{return Bn}}isValidPort(e){return typeof e==`number`&&Number.isInteger(e)&&e>0&&e<=65535}async getRegisteredPort(e){let t={host:e,port:this.getFallbackPort()},n=Sa(process.cwd()),r=process.env.NODE_ENV||`development`,i=new u(process.env.PORT_REGISTRY_PATH),a={serviceName:`browse-tool-http`,serviceType:`tool`,environment:r},o=async t=>{try{let n=await i.getPort({...a,repositoryPath:t});if(n.success&&n.record&&this.isValidPort(n.record.port))return{host:n.record.host||e,port:n.record.port}}catch(e){let n=e instanceof Error?e.message:String(e);console.warn(`${ba} Registry lookup failed (path=${t}): ${n}`)}return null},s=await o(n);if(s)return s;let c=await wa(n);if(c){let e=await o(c);if(e)return e}try{let t=(await i.listAllocations(a)).filter(e=>this.isValidPort(e.port)).sort((e,t)=>new Date(t.updatedAt).getTime()-new Date(e.updatedAt).getTime())[0];if(t)return{host:t.host||e,port:t.port}}catch(e){let t=e instanceof Error?e.message:String(e);console.warn(`${ba} Global registry lookup failed: ${t}`)}return t}};Da=ga=B([h(),z(0,m(L.BrowserService)),z(1,m(L.StealthLauncher)),z(2,m(L.ProxyConfigService)),R(`design:paramtypes`,[Object,Object,Object])],Da);var Oa;const ka=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),type:F.enum([`log`,`info`,`warning`,`error`,`debug`,`trace`,`dir`,`dirxml`,`table`,`count`,`assert`]).optional().describe(`Filter by message type (log, info, warning, error, debug, trace, etc.)`),limit:F.number().min(1).optional().describe(`Maximum number of messages to return`)});let Aa=class extends ${static{Oa=this}static TOOL_NAME=`browser_list_console_messages`;constructor(e,t,n,r){super(e,t,n),this.pageMonitor=r}getInputSchema(){return ka}getDefinition(){return{name:Oa.TOOL_NAME,description:`Lists all console messages captured during page automation. Requires monitoring to be started first.`,inputSchema:F.toJSONSchema(ka,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId);return this.executeWithMode(Oa.TOOL_NAME,t.id,e,async()=>{let n=this.pageMonitor.getConsoleMessages(t.id,e.type);return e.limit&&(n=n.slice(0,e.limit)),this.successJson({pageId:t.id,messageCount:n.length,messages:n.map(e=>({id:e.id,type:e.type,text:e.text,location:e.location,timestamp:e.timestamp.toISOString()}))})})})}};Aa=Oa=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.PageMonitorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Aa);var ja;const Ma=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),resourceType:F.string().optional().describe(`Filter by resource type (document, xhr, fetch, script, stylesheet, image, etc.)`),urlPattern:F.string().optional().describe(`Filter by URL pattern (substring match)`),method:F.string().optional().describe(`Filter by HTTP method (GET, POST, etc.)`),limit:F.number().min(1).optional().describe(`Maximum number of requests to return`)});let Na=class extends ${static{ja=this}static TOOL_NAME=`browser_list_network_requests`;constructor(e,t,n,r){super(e,t,n),this.pageMonitor=r}getInputSchema(){return Ma}getDefinition(){return{name:ja.TOOL_NAME,description:`Lists all network requests captured during page automation. Requires monitoring to be started first.`,inputSchema:F.toJSONSchema(Ma,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId);return this.executeWithMode(ja.TOOL_NAME,t.id,e,async()=>{let n=this.pageMonitor.getNetworkRequests(t.id);if(e.resourceType&&(n=n.filter(t=>t.resourceType===e.resourceType)),e.urlPattern){let t=e.urlPattern;n=n.filter(e=>e.url.includes(t))}return e.method&&(n=n.filter(t=>t.method===e.method)),e.limit&&(n=n.slice(0,e.limit)),this.successJson({pageId:t.id,requestCount:n.length,requests:n.map(e=>({id:e.id,url:e.url,method:e.method,resourceType:e.resourceType,status:e.response?.status,statusText:e.response?.statusText,timing:e.response?.timing,timestamp:e.timestamp.toISOString()}))})})})}};Na=ja=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.PageMonitorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Na);var Pa;const Fa=F.object({browserId:F.string().describe(`Browser ID to list pages for`)});let Ia=class extends ${static{Pa=this}static TOOL_NAME=`browser_list_pages`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Fa}getDefinition(){return{name:Pa.TOOL_NAME,description:`Lists all pages (tabs) in a browser. Returns page IDs, URLs, titles, and indicates which page is currently active.`,inputSchema:F.toJSONSchema(Fa,{reused:`inline`})}}async execute(e){return this.toolExecutor.executeForBrowser(Pa.TOOL_NAME,e.browserId,e,async()=>{let t=this.browserService.getBrowser(e.browserId);if(!t)return this.error(`Browser "${e.browserId}" not found`);let n=this.pageRegistry.findByBrowser(e.browserId),r=await Promise.all(n.map(async e=>{await this.pageRegistry.updateMetadata(e.id);let n=this.pageRegistry.get(e.id);return{pageId:e.id,url:n?.url??e.url,title:n?.title??e.title,isActive:e.id===t.currentPageId,browserId:e.browserId,profileName:e.profileName,createdAt:e.createdAt.toISOString()}}));return this.successJson({browserId:e.browserId,currentPageId:t.currentPageId,pageCount:r.length,pages:r})})}};Ia=Pa=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Ia);var La;const Ra=F.object({});let za=class{static{La=this}static TOOL_NAME=`browser_list_profiles`;constructor(e){this.profileService=e}getInputSchema(){return Ra}getDefinition(){return{name:La.TOOL_NAME,description:`Lists all available browser profiles with their settings, browser type, viewport, and timestamps.`,inputSchema:F.toJSONSchema(Ra,{reused:`inline`})}}async execute(){try{let e=await this.profileService.list();return{content:[{type:`text`,text:JSON.stringify({profileCount:e.length,profiles:e.map(e=>({name:e.name,browserType:e.browserType,viewport:e.viewport??null,userAgent:e.userAgent??null,locale:e.locale??null,timezone:e.timezone??null,colorScheme:e.colorScheme??null,createdAt:e.createdAt,updatedAt:e.updatedAt}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};za=La=B([h(),z(0,m(L.ProfileService)),R(`design:paramtypes`,[Object])],za);var Ba;const Va=F.object({});let Ha=class{static{Ba=this}static TOOL_NAME=`browser_list_snippets`;constructor(e){this.snippetService=e}getInputSchema(){return Va}getDefinition(){return{name:Ba.TOOL_NAME,description:`Lists saved code snippets by name, description, and snippetPath for reuse with browser_run_code.`,inputSchema:F.toJSONSchema(Va,{reused:`inline`})}}async execute(){try{let e=await this.snippetService.listSnippets();return{content:[{type:`text`,text:JSON.stringify({snippetCount:e.length,snippets:e},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};Ha=Ba=B([h(),z(0,m(L.CodeSnippetService)),R(`design:paramtypes`,[Object])],Ha);var Ua;const Wa=F.object({pageId:F.string().describe(`Page ID to operate on`),url:F.string().describe(`URL to navigate to`),waitUntil:F.enum([`load`,`domcontentloaded`,`networkidle`,`commit`]).optional().default(`load`).describe(`Wait until condition`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`),referer:F.string().optional().describe(`Referer header value`)});let Ga=class extends ${static{Ua=this}static TOOL_NAME=`browser_navigate`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Wa}getDefinition(){return{name:Ua.TOOL_NAME,description:`Navigates the browser to a specified URL.`,inputSchema:F.toJSONSchema(Wa,{reused:`inline`})}}async execute(e){return this.executeWithMode(Ua.TOOL_NAME,e.pageId,e,async()=>{let t=(await this.resolvePage(e.pageId).goto(e.url,{waitUntil:e.waitUntil,timeout:e.timeout,referer:e.referer}))?.status()??`unknown`;return this.success(`Navigated to ${e.url} (status: ${t})`)})}};Ga=Ua=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Ga);var Ka;const qa=F.object({browserId:F.string().optional().describe(`Browser ID to create the page in (optional - uses default browser if not specified)`),url:F.string().optional().describe(`Optional URL to navigate to after creation`),setAsCurrent:F.boolean().optional().default(!0).describe(`Whether to set as current page for the browser (default: true)`)});let Ja=class extends ${static{Ka=this}static TOOL_NAME=`browser_new_page`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return qa}getDefinition(){return{name:Ka.TOOL_NAME,description:`Creates a new page (tab) in a browser instance. Uses the default browser if browserId is not specified, launching one if needed.`,inputSchema:F.toJSONSchema(qa,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=e.browserId?this.browserService.getBrowser(e.browserId):await this.browserService.getOrCreateDefaultBrowser();if(!t)return this.error(`Browser "${e.browserId}" not found`);let n=t.id,r=e.setAsCurrent!==!1;if(t.mode===`extension`||t.mode===`vm`){let i=this.pageRegistry.registerExtensionPage(n,void 0,e.url,!1),a=await this.toolExecutor.executeForBrowser(Ka.TOOL_NAME,n,{...e,pageId:i},async()=>this.error(`Browser "${n}" is not in extension mode`));if(a.isError)return this.pageRegistry.remove(i),a;t.pageIds.add(i),(r||!t.currentPageId)&&(t.currentPageId=i);let o=a.content[0]?.type===`text`?a.content[0].text:void 0,s={};if(typeof o==`string`)try{s=JSON.parse(o)}catch{s={}}let c=this.pageRegistry.get(i);return c&&(c.url=s.url??c.url,c.title=s.title??c.title,c.extensionTabId=s.tabId??c.extensionTabId),this.browserService.recordBrowserActivity(n,i),this.successJson({pageId:i,url:c?.url??e.url??`about:blank`,title:c?.title??``,browserId:n,isActive:t.currentPageId===i})}let{pageId:i,page:a}=await this.browserService.newPage(n);if(e.url)try{await a.goto(e.url),await this.pageRegistry.updateMetadata(i)}catch(e){throw await a.close(),e}r&&this.browserService.setCurrentPage(n,i);let o=this.pageRegistry.get(i);return this.successJson({pageId:i,url:o?.url??a.url(),title:o?.title??``,browserId:n,isActive:r})})}};Ja=Ka=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Ja);var Ya;const Xa=F.object({pageId:F.string().describe(`Page ID to operate on`),format:F.enum([`Letter`,`Legal`,`Tabloid`,`Ledger`,`A0`,`A1`,`A2`,`A3`,`A4`,`A5`,`A6`]).optional().default(`Letter`).describe(`Paper format`),printBackground:F.boolean().optional().default(!1).describe(`Print background graphics`),landscape:F.boolean().optional().default(!1).describe(`Landscape orientation`),scale:F.number().min(.1).max(2).optional().default(1).describe(`Page scale (0.1 to 2)`),marginTop:F.string().optional().describe(`Top margin (e.g., "1cm", "0.5in")`),marginBottom:F.string().optional().describe(`Bottom margin`),marginLeft:F.string().optional().describe(`Left margin`),marginRight:F.string().optional().describe(`Right margin`),filename:F.string().optional().describe(`Custom filename without extension`)});let Za=class extends ${static{Ya=this}static TOOL_NAME=`browser_pdf`;constructor(e,t,n,r){super(e,t,n),this.extensionTaskQueue=r}getInputSchema(){return Xa}getDefinition(){return{name:Ya.TOOL_NAME,description:`Generates a PDF of the page with configurable format options. Saves to temp directory and returns the file path.`,inputSchema:F.toJSONSchema(Xa,{reused:`inline`})}}async execute(e){let t=this.getPage(e.pageId);return t?t.mode===`extension`?this.safeExecute(async()=>{let n=e.filename??`page-${I()}`,r=S(N(),`${n}.pdf`),i=await this.extensionTaskQueue.queueTask(Ya.TOOL_NAME,{pageId:e.pageId,format:e.format,printBackground:e.printBackground,landscape:e.landscape,scale:e.scale,marginTop:e.marginTop,marginBottom:e.marginBottom,marginLeft:e.marginLeft,marginRight:e.marginRight,filename:n},void 0,t.browserId),a=JSON.parse(i.result?.content?.[0]?.text??`{}`);if(!a.pdfBase64)throw Error(`Extension mode PDF generation returned no data`);return await j(r,Buffer.from(a.pdfBase64,`base64`)),this.success(`PDF saved to: ${r}`)}):this.executeWithMode(Ya.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=e.filename??`page-${I()}`,r=S(N(),`${n}.pdf`);return await t.pdf({path:r,format:e.format??`Letter`,printBackground:e.printBackground??!1,landscape:e.landscape??!1,scale:e.scale??1,margin:{top:e.marginTop,bottom:e.marginBottom,left:e.marginLeft,right:e.marginRight}}),this.success(`PDF saved to: ${r}`)}):this.error(`Page "${e.pageId}" not found`)}};Za=Ya=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ExtensionTaskQueue)),R(`design:paramtypes`,[Object,Object,Object,Object])],Za);var Qa;const $a=F.object({pageId:F.string().describe(`Page ID to operate on`),key:F.string().describe(`Key or key combination (e.g., "Enter", "Tab", "Control+a", "Meta+Shift+t", "ArrowDown")`),element:U.optional().describe(`Optional element to focus before pressing key`),delay:F.number().optional().describe(`Delay between keydown and keyup in ms`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let eo=class extends ${static{Qa=this}static TOOL_NAME=`browser_press_key`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return $a}getDefinition(){return{name:Qa.TOOL_NAME,description:`Presses a keyboard key or key combination. Supports modifier keys like Control, Shift, Alt, Meta.`,inputSchema:F.toJSONSchema($a,{reused:`inline`})}}async execute(e){return this.executeWithMode(Qa.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId);return e.element?await(await this.elementLocator.locate(t,e.element)).press(e.key,{delay:e.delay,timeout:e.timeout}):await t.keyboard.press(e.key,{delay:e.delay}),this.success(`Pressed key: "${e.key}"`)})}};eo=Qa=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],eo);var to;const no=F.object({pageId:F.string().describe(`Page ID to operate on`),waitUntil:F.enum([`load`,`domcontentloaded`,`networkidle`,`commit`]).optional().default(`load`).describe(`Wait until condition`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let ro=class extends ${static{to=this}static TOOL_NAME=`browser_reload`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return no}getDefinition(){return{name:to.TOOL_NAME,description:`Reloads the current page.`,inputSchema:F.toJSONSchema(no,{reused:`inline`})}}async execute(e){return this.executeWithMode(to.TOOL_NAME,e.pageId,e,async()=>{let t=(await this.resolvePage(e.pageId).reload({waitUntil:e.waitUntil,timeout:e.timeout}))?.status()??`unknown`;return this.success(`Page reloaded (status: ${t})`)})}};ro=to=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],ro);var io;const ao=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),width:F.number().min(1).describe(`Viewport width in pixels`),height:F.number().min(1).describe(`Viewport height in pixels`),deviceScaleFactor:F.number().min(1).max(3).optional().describe(`Device scale factor (default: 1)`),isMobile:F.boolean().optional().describe(`Whether the meta viewport tag is taken into account`),hasTouch:F.boolean().optional().describe(`Whether the viewport supports touch events`)});let oo=class extends ${static{io=this}static TOOL_NAME=`browser_resize_page`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return ao}getDefinition(){return{name:io.TOOL_NAME,description:`Resizes the viewport of a page to specified width and height dimensions.`,inputSchema:F.toJSONSchema(ao,{reused:`inline`})}}async execute(e){return this.executeWithMode(io.TOOL_NAME,e.pageId,{width:e.width,height:e.height},async()=>{let t=this.resolvePage(e.pageId);await t.setViewportSize({width:e.width,height:e.height});let n=t.viewportSize();return this.successJson({success:!0,viewport:{width:n?.width??e.width,height:n?.height??e.height}})})}};oo=io=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],oo);var so;const co=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),code:F.string().optional().describe(`JavaScript code to execute. Accepts a function body with page/context/browser in scope or an async function receiving { page, context, browser }. Example body: "await page.goto('https://example.com'); return await page.title();". Example function: "async ({ page }) => await page.title()".`),snippetPath:F.string().optional().describe(`Relative snippet path inside the configured snippets directory. When provided, executes the saved snippet instead of inline code.`),saveAs:F.object({name:F.string().describe(`Human-readable snippet name. The filename is generated from a sanitized version of this value.`),description:F.string().describe(`Short description shown by browser_list_snippets.`)}).optional().describe(`Optional snippet metadata used to save the inline code into the configured snippets directory.`)});let lo=class extends ${static{so=this}static TOOL_NAME=`browser_run_code`;constructor(e,t,n,r,i){super(e,t,n),this.extensionPageProxy=r,this.snippetService=i}getInputSchema(){return co}getDefinition(){return{name:so.TOOL_NAME,description:`Runs a Playwright code snippet. The code can be a function body with page/context/browser in scope, or an async function receiving { page, context, browser }. Return a value to include it in the response.`,inputSchema:F.toJSONSchema(co,{reused:`inline`})}}isFunctionExpressionCandidate(e){let t=e.trim();return/^(?:async\s*)?(?:\(\s*)?(?:\{\s*)?(?:page|context|browser)\b[\s\S]*=>/.test(t)||/^(?:async\s+)?function\s*\(/.test(t)}async executeInlineCode(e,t){if(this.isFunctionExpressionCandidate(e)){let n;try{n=Function(`return (${e}\n);`)()}catch{n=void 0}if(typeof n==`function`)return await n(t)}return await Function(`page`,`context`,`browser`,`return (async () => { ${e} })();`)(t.page,t.context,t.browser)}getSnippetCode(e){return this.isFunctionExpressionCandidate(e)?`return await (${e}\n)({ page, context, browser });`:e}async execute(e){return this.safeExecute(async()=>{if(!e.code&&!e.snippetPath)return this.error(`Either "code" or "snippetPath" is required`);if(e.saveAs&&!e.code)return this.error(`"saveAs" requires inline "code"`);let t=this.resolvePageEntry(e.pageId),{page:n,context:r,browser:i}=t,a=t.mode===`extension`?(this.extensionPageProxy.setTarget(t.id,t.browserId),this.extensionPageProxy):n,o=t.mode===`extension`?this.extensionPageProxy.context():r,s=t.mode===`extension`?{id:t.browserId,mode:this.browserService.getBrowser(t.browserId)?.mode??`extension`,pageId:t.id}:i,c;e.saveAs&&(c=await this.snippetService.saveSnippet({name:e.saveAs.name,description:e.saveAs.description,code:this.getSnippetCode(e.code)}));let l={page:a,context:o,browser:s},u=e.snippetPath?await this.snippetService.loadSnippet(e.snippetPath).then(e=>e.run(l)):await this.executeInlineCode(e.code,l);return u===void 0?c?this.successJson({savedSnippet:c}):this.success(`Code executed successfully`):this.successJson(c?{result:u,savedSnippet:c}:{result:u})})}};lo=so=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ExtensionPageProxy)),z(4,m(L.CodeSnippetService)),R(`design:paramtypes`,[Object,Object,Object,Object,Object])],lo);var uo;const fo={width:1920,height:1080},po=F.object({specPath:F.string().describe(`Absolute path to the Playwright spec file to execute`),automationId:F.string().optional().describe(`Optional automation session ID (auto-generated if not provided)`),mode:F.enum([`playwright`,`extension`]).optional().default(`playwright`).describe(`Execution mode: playwright (standard) or extension (Chrome extension)`),browserId:F.string().optional().describe(`Browser ID to use (required for extension mode)`),pageId:F.string().optional().describe(`Page ID to use (required for extension mode)`),browserType:F.enum([`chromium`,`firefox`,`webkit`]).optional().default(`chromium`).describe(`Browser type to use (only applies to playwright mode)`),headless:F.boolean().optional().default(!0).describe(`Run browser in headless mode (only applies to playwright mode)`),baseURL:F.string().optional().describe(`Base URL for relative navigation (e.g., page.goto("/") will use this as the base)`),keepBrowserOpen:F.boolean().optional().default(!1).describe(`Keep browser open after spec execution completes for further interaction`),hooksPath:F.string().optional().describe(`Explicit absolute path to run-spec-hooks.ts file. Skips auto-discovery when provided. The source directory is injected as process.env.__HOOKS_SOURCE_DIR__ so hooks can resolve paths relative to their original location.`),env:F.record(F.string(),F.string()).optional().describe(`Environment variables to set before running the spec. Applied before hooks execution. Useful for passing config like API endpoints without relying on hooks file path resolution.`),testName:F.string().optional().describe(`Filter by exact test name`),testPattern:F.string().optional().describe(`Filter by regex pattern on full test title (e.g., "login.*success")`),onlyMarked:F.boolean().optional().default(!1).describe(`Run only tests marked with test.only()`),describeFilter:F.string().optional().describe(`Filter by describe block name pattern`),specArgs:F.record(F.string(),F.unknown()).optional().describe(`Arguments to pass to the spec (available as fixtures.specArgs)`),loadArgsFromEnv:F.boolean().optional().default(!1).describe(`Load specArgs from environment variables using spec's envPrefix export`),videoDir:F.string().optional().describe(`Absolute directory path to save video recordings. Supported in both playwright and extension modes.`)});let mo=class{static{uo=this}static TOOL_NAME=`run_spec`;constructor(e,t,n,r,i){this.automationRunner=e,this.specMetadataService=t,this.extensionSpecRunner=n,this.extensionPageProxy=r,this.specBundlerService=i}getInputSchema(){return po}getDefinition(){return{name:uo.TOOL_NAME,description:`Runs a Playwright spec file with optional test filtering and dynamic arguments. Supports filtering by test name, pattern, or test.only markers.`,inputSchema:F.toJSONSchema(po,{reused:`inline`})}}async execute(e){if(!x(e.specPath))return{content:[{type:`text`,text:`Error: specPath must be an absolute path (e.g., /Users/me/project/tests/my-spec.ts)`}],isError:!0};if(e.hooksPath&&!x(e.hooksPath))return{content:[{type:`text`,text:`Error: hooksPath must be an absolute path`}],isError:!0};if(e.videoDir&&!x(e.videoDir))return{content:[{type:`text`,text:`Error: videoDir must be an absolute path`}],isError:!0};try{if(e.env)for(let[t,n]of Object.entries(e.env))process.env[t]=n;let t=this.resolveHooksPath(e.specPath,e.hooksPath),n=t?await this.runHooks(t):null;return n?.baseURL&&!e.baseURL&&(e.baseURL=n.baseURL),(e.mode??`playwright`)===`extension`?await this.executeExtensionMode(e):e.testName||e.testPattern||e.onlyMarked||e.describeFilter||e.loadArgsFromEnv?await this.executeEnhanced(e,t):await this.executeBasic(e,t)}catch(e){return{content:[{type:`text`,text:`Error: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}findHooksFile(e){let t=b(e);for(let e=0;e<4;e++){let e=S(t,`run-spec-hooks.ts`);if(D(e))return e;let n=b(t);if(n===t)break;t=n}return null}async runHooks(e){let t=b(w(e)),n=await this.specBundlerService.bundle(e);try{process.env.__HOOKS_SOURCE_DIR__=t;let e=await import(`${n.outputPath}?t=${Date.now()}`);return typeof e.setup==`function`?await e.setup():void 0}finally{process.env.__HOOKS_SOURCE_DIR__=void 0,await n.cleanup()}}resolveHooksPath(e,t){return t?w(t):this.findHooksFile(e)}async executeExtensionMode(e){if(!e.pageId||!e.browserId)return{content:[{type:`text`,text:`Error: pageId and browserId are required for extension mode`}],isError:!0};this.extensionPageProxy.setTarget(e.pageId,e.browserId);let t=e.specArgs??{};if(e.loadArgsFromEnv){let n=await this.specMetadataService.extractSpecMetadata(e.specPath);n.argsSchema&&n.envPrefix&&(t={...this.specMetadataService.parseArgsFromEnv(n.argsSchema,n.envPrefix),...t})}let n={};e.testName&&(n.testName=e.testName),e.testPattern&&(n.testPattern=e.testPattern),e.onlyMarked&&(n.onlyMarked=e.onlyMarked),e.describeFilter&&(n.describeFilter=e.describeFilter);let r=e.automationId??`ext-spec-${Date.now()}`,i=!!e.videoDir;i&&await this.extensionPageProxy.startRecording();let a;try{a=await this.extensionSpecRunner.executeSpec({specPath:e.specPath,sessionId:r,testFilter:Object.keys(n).length>0?n:void 0,specArgs:Object.keys(t).length>0?t:void 0})}finally{if(i&&e.videoDir)try{let t=await this.extensionPageProxy.stopRecording();if(t){O(e.videoDir,{recursive:!0});let n=S(e.videoDir,`recording-${r}.webm`);ne(n,Buffer.from(t,`base64`)),a.videoPath=n}}catch{}}return this.formatExtensionResult(a)}async executeBasic(e,t){let n=await this.automationRunner.runSpec({specPath:e.specPath,automationId:e.automationId,browserId:e.browserId,pageId:e.pageId,keepBrowserOpen:e.keepBrowserOpen??!1,recordVideo:e.videoDir?{dir:e.videoDir,size:fo}:void 0,browserOptions:{browserType:e.browserType,headless:e.headless,baseURL:e.baseURL},hooksPath:t??void 0});return this.formatResult(n,e.keepBrowserOpen??!1)}async executeEnhanced(e,t){let n=e.specArgs??{};if(e.loadArgsFromEnv){let t=await this.specMetadataService.extractSpecMetadata(e.specPath);t.argsSchema&&t.envPrefix&&(n={...this.specMetadataService.parseArgsFromEnv(t.argsSchema,t.envPrefix),...n})}let r={};e.testName&&(r.testName=e.testName),e.testPattern&&(r.testPattern=e.testPattern),e.onlyMarked&&(r.onlyMarked=e.onlyMarked),e.describeFilter&&(r.describeFilter=e.describeFilter);let i=await this.automationRunner.runSpecEnhanced({specPath:e.specPath,automationId:e.automationId,browserId:e.browserId,pageId:e.pageId,keepBrowserOpen:e.keepBrowserOpen??!1,recordVideo:e.videoDir?{dir:e.videoDir,size:fo}:void 0,browserOptions:{browserType:e.browserType,headless:e.headless,baseURL:e.baseURL},testFilter:Object.keys(r).length>0?r:void 0,specArgs:Object.keys(n).length>0?n:void 0,hooksPath:t??void 0});return this.formatResult(i,e.keepBrowserOpen??!1)}formatResult(e,t){let n=e.status===`completed`?t?`Spec completed. Browser kept open for further interaction.`:`Spec completed.`:t?`Spec failed. Browser kept open for further interaction.`:`Spec failed.`,r=e.status!==`completed`||e.specResult?.success===!1;return{content:[{type:`text`,text:JSON.stringify({automationId:e.automationId,status:e.status,pageIds:e.pageIds,browserId:e.browserId,specResult:e.specResult,warnings:e.specResult?.warnings,message:n},null,2)}],...r?{isError:!0}:{}}}formatExtensionResult(e){let t=e.success?`Spec completed: ${e.passed}/${e.totalTests} tests passed`:`Spec failed: ${e.failed}/${e.totalTests} tests failed`;return{content:[{type:`text`,text:JSON.stringify({sessionId:e.sessionId,status:e.success?`completed`:`failed`,totalTests:e.totalTests,passed:e.passed,failed:e.failed,duration:e.duration,handoffOccurred:e.handoffOccurred,testResults:e.testResults,videoPath:e.videoPath,warnings:e.warnings,message:t},null,2)}],isError:!e.success}}};mo=uo=B([h(),z(0,m(L.AutomationRunner)),z(1,m(L.SpecMetadataService)),z(2,m(L.ExtensionSpecRunner)),z(3,m(L.ExtensionPageProxy)),z(4,m(L.SpecBundlerService)),R(`design:paramtypes`,[Object,Object,Object,Object,Object])],mo);var ho;const go=F.object({profileName:F.string().describe(`Profile to save state into`),browserId:F.string().optional().describe(`Browser to read state from`)});let _o=class{static{ho=this}static TOOL_NAME=`browser_save_profile_state`;constructor(e,t,n){this.profileService=e,this.browserService=t,this.extensionPageProxy=n}getInputSchema(){return go}getDefinition(){return{name:ho.TOOL_NAME,description:`Saves the current browser state to the named profile for reuse across future launches.`,inputSchema:F.toJSONSchema(go,{reused:`inline`})}}async execute(e){try{let t=e.browserId?this.browserService.getBrowser(e.browserId):this.browserService.getBrowserByProfile(e.profileName);if(!t)throw Error(`Browser for profile "${e.profileName}" not found`);if(t.context){let n=await t.context.storageState();await this.profileService.saveStorageState(e.profileName,n)}else{let n=t.currentPageId??[...t.pageIds][0];if(!n)throw Error(`Browser "${t.id}" has no active page`);this.extensionPageProxy.setTarget(n,t.id);let r=await this.extensionPageProxy.getStorageState();await this.profileService.saveStorageState(e.profileName,r)}return{content:[{type:`text`,text:JSON.stringify({profileName:e.profileName,browserId:t.id,saved:!0},null,2)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}};_o=ho=B([h(),z(0,m(L.ProfileService)),z(1,m(L.BrowserService)),z(2,m(L.ExtensionPageProxy)),R(`design:paramtypes`,[Object,Object,Object])],_o);var vo;const yo=F.object({pageId:F.string().describe(`Page ID to operate on`),fullPage:F.boolean().optional().default(!1).describe(`Capture full page scrollable area`),selector:F.string().optional().describe(`CSS selector for element to screenshot`),clip:F.object({x:F.number().min(0).describe(`X coordinate of top-left corner`),y:F.number().min(0).describe(`Y coordinate of top-left corner`),width:F.number().min(1).describe(`Width of clipping region`),height:F.number().min(1).describe(`Height of clipping region`)}).optional().describe(`Clip region to capture by coordinates (cannot be used with selector)`),type:F.enum([`png`,`jpeg`]).optional().default(`png`).describe(`Image format`),quality:F.number().min(0).max(100).optional().describe(`Quality for jpeg format (0-100)`),omitBackground:F.boolean().optional().default(!1).describe(`Omit background for transparent PNG`),filename:F.string().optional().describe(`Custom filename without extension`)});let bo=class extends ${static{vo=this}static TOOL_NAME=`browser_screenshot`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return yo}getDefinition(){return{name:vo.TOOL_NAME,description:`Captures a screenshot of the page or specific element. Saves to temp directory and returns the file path.`,inputSchema:F.toJSONSchema(yo,{reused:`inline`})}}async execute(e){return this.executeWithMode(vo.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId);if(e.clip&&e.selector)throw Error(`Cannot use both clip and selector options together`);let n=e.type??`png`,r=e.filename??`screenshot-${I()}`,i=S(N(),`${r}.${n}`);if(e.selector){let r=await t.$(e.selector);if(!r)throw Error(`Element not found: ${e.selector}`);await r.screenshot({path:i,type:n,omitBackground:e.omitBackground??!1,quality:n===`jpeg`?e.quality:void 0})}else await t.screenshot({path:i,type:n,fullPage:e.fullPage??!1,omitBackground:e.omitBackground??!1,quality:n===`jpeg`?e.quality:void 0,clip:e.clip});return this.success(`Screenshot saved to: ${i}`)})}};bo=vo=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],bo);var xo;const So=F.object({pageId:F.string().describe(`Page ID to set as current`)});let Co=class extends ${static{xo=this}static TOOL_NAME=`browser_select_page`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return So}getDefinition(){return{name:xo.TOOL_NAME,description:`Switches the active page for a browser. Updates the current page ID so subsequent operations use this page by default.`,inputSchema:F.toJSONSchema(So,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.pageRegistry.get(e.pageId);if(!t)return this.error(`Page "${e.pageId}" not found`);let{browserId:n}=t;if(t.mode===`extension`||t.mode===`vm`){let t=await this.executeWithMode(xo.TOOL_NAME,e.pageId,e,async()=>this.error(`Page "${e.pageId}" is not in extension mode`));if(t.isError)return t}this.browserService.setCurrentPage(n,e.pageId),this.browserService.recordBrowserActivity(n,e.pageId),await this.pageRegistry.updateMetadata(e.pageId);let r=this.pageRegistry.get(e.pageId);return this.successJson({browserId:n,pageId:e.pageId,url:r?.url??t.url,title:r?.title??t.title,message:`Page "${e.pageId}" is now the active page for browser "${n}"`})})}};Co=xo=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Co);var wo;const To=U.extend({pageId:F.string().describe(`Page ID to operate on`),values:F.array(F.string()).optional().describe(`Option value(s) to select`),labels:F.array(F.string()).optional().describe(`Option label(s) to select`),indexes:F.array(F.number()).optional().describe(`Option index(es) to select`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let Eo=class extends ${static{wo=this}static TOOL_NAME=`browser_select`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return To}getDefinition(){return{name:wo.TOOL_NAME,description:`Selects option(s) from a dropdown/select element. Can select by value, label, or index.`,inputSchema:F.toJSONSchema(To,{reused:`inline`})}}async execute(e){return this.executeWithMode(wo.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=await this.elementLocator.locate(t,e),r=[];e.values?r=e.values.map(e=>({value:e})):e.labels?r=e.labels.map(e=>({label:e})):e.indexes&&(r=e.indexes.map(e=>({index:e})));let i=await n.selectOption(r,{timeout:e.timeout});return this.success(`Selected ${i.length} option(s): ${i.join(`, `)}`)})}};Eo=wo=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Eo);var Do;const Oo=F.object({pageId:F.string().describe(`Page ID to operate on`),root:F.string().optional().describe(`CSS selector for root element to snapshot (defaults to body)`),sizeThreshold:F.number().optional().default(1e4).describe(`Size threshold in bytes; snapshots larger than this are saved to file (default: 10000)`)});let ko=class extends ${static{Do=this}static TOOL_NAME=`browser_snapshot`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Oo}getDefinition(){return{name:Do.TOOL_NAME,description:`Returns the accessibility tree snapshot of the page. Preferred for LLM processing as it provides structured, semantic information about page content.`,inputSchema:F.toJSONSchema(Oo,{reused:`inline`})}}async execute(e){return this.executeWithMode(Do.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId),n=await this.captureSnapshot(t,e.root);if(!n)return this.success(`No accessibility tree available for this page`);let r=e.sizeThreshold??1e4;if(Buffer.byteLength(n,`utf-8`)<=r)return this.success(n);let i=`snapshot-${I()}.yaml`,a=S(N(),i);return await j(a,n,`utf-8`),this.success(`Snapshot saved to: ${a}`)})}async captureSnapshot(e,t){let n=t?[t]:[`body`,`html`];for(let t of n){let n=e.locator(t).first();if(await n.count()!==0)try{return await n.ariaSnapshot()}catch{}}if(!t){await e.waitForTimeout(250);for(let t of[`body`,`html`]){let n=e.locator(t).first();if(await n.count()!==0)try{return await n.ariaSnapshot()}catch{}}}throw Error(`Root element not found: ${t??`body/html`}`)}};ko=Do=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],ko);var Ao;const jo=F.object({pageId:F.string().describe(`The page ID to record.`),path:F.string().optional().describe(`Optional absolute output path for the .webm file. If omitted, browse-tool creates a temporary recording path.`)});let Mo=class extends ${static{Ao=this}static TOOL_NAME=`browser_start_recording`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return jo}getDefinition(){return{name:Ao.TOOL_NAME,description:`Starts recording an existing extension/vm browser page and saves the artifact to the provided path or a temporary .webm file.`,inputSchema:F.toJSONSchema(jo,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId);if(t.mode!==`extension`&&t.mode!==`vm`)return this.error(`Page "${e.pageId}" is in "${t.mode}" mode and cannot use extension recording`);if(e.path){if(!v.isAbsolute(e.path))return this.error(`Recording path must be an absolute path (e.g., /Users/me/project/recording.webm)`);if(v.extname(e.path).toLowerCase()!==`.webm`)return this.error(`Recording path must end with .webm`)}let n=await this.browserService.startPageRecording(t.browserId,t.id,e.path);return this.successJson({started:!0,browserId:t.browserId,pageId:t.id,outputPath:n.outputPath})})}};Mo=Ao=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Mo);var No;const Po=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),name:F.string().optional().describe(`Name for the trace (used in trace filename)`),screenshots:F.boolean().optional().describe(`Whether to capture screenshots during tracing (default: true)`),snapshots:F.boolean().optional().describe(`Whether to capture snapshots during tracing (default: true)`),sources:F.boolean().optional().describe(`Whether to include source files in the trace (default: false)`)});let Fo=class extends ${static{No=this}static TOOL_NAME=`browser_start_trace`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Po}getDefinition(){return{name:No.TOOL_NAME,description:`Starts Playwright performance tracing with configurable options for screenshots, snapshots, and sources. Use browser_stop_trace to stop and save the trace.`,inputSchema:F.toJSONSchema(Po,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId),n=t.context;if(!n)return this.error(`Page "${e.pageId}" is in extension mode and cannot start tracing`);let{name:r,screenshots:i=!0,snapshots:a=!0,sources:o=!1}=e;return await n.tracing.start({name:r,screenshots:i,snapshots:a,sources:o}),this.successJson({success:!0,message:`Tracing started`,pageId:t.id,options:{name:r??null,screenshots:i,snapshots:a,sources:o}})})}};Fo=No=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Fo);var Io;const Lo=F.object({pageId:F.string().describe(`The page ID whose recording should be stopped.`),includeBase64:F.boolean().optional().describe(`Whether to include the base64-encoded WebM payload in the response.`)});let Ro=class extends ${static{Io=this}static TOOL_NAME=`browser_stop_recording`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Lo}getDefinition(){return{name:Io.TOOL_NAME,description:`Stops an active extension/vm browser recording and returns the saved .webm path with optional base64 payload.`,inputSchema:F.toJSONSchema(Lo,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId);if(t.mode!==`extension`&&t.mode!==`vm`)return this.error(`Page "${e.pageId}" is in "${t.mode}" mode and cannot use extension recording`);let n=await this.browserService.stopPageRecording(t.browserId,t.id,{includeBase64:e.includeBase64});return this.successJson({stopped:!0,browserId:t.browserId,pageId:t.id,outputPath:n.outputPath,fileSizeBytes:n.fileSizeBytes,...n.videoBase64?{videoBase64:n.videoBase64}:{}})})}};Ro=Io=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Ro);var zo;const Bo=F.object({pageId:F.string().describe(`Optional page ID to override browser current page`),path:F.string().describe(`Absolute path where the trace file should be saved (e.g., "/Users/me/project/traces/trace.zip"). Must end with .zip extension.`)});let Vo=class extends ${static{zo=this}static TOOL_NAME=`browser_stop_trace`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Bo}getDefinition(){return{name:zo.TOOL_NAME,description:`Stops Playwright performance tracing and saves the trace file to a specified path. The trace can be viewed using Playwright Trace Viewer.`,inputSchema:F.toJSONSchema(Bo,{reused:`inline`})}}async execute(e){return this.safeExecute(async()=>{let t=this.resolvePageEntry(e.pageId),n=t.context;if(!n)return this.error(`Page "${e.pageId}" is in extension mode and cannot stop tracing`);let r=e.path;return v.isAbsolute(r)?r.endsWith(`.zip`)?(await n.tracing.stop({path:r}),this.successJson({success:!0,message:`Tracing stopped and saved`,pageId:t.id,tracePath:r,viewCommand:`npx playwright show-trace ${r}`})):this.error(`Trace path must end with .zip extension`):this.error(`Trace path must be an absolute path (e.g., /Users/me/project/traces/trace.zip)`)})}};Vo=zo=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Vo);var Ho;const Uo=U.extend({pageId:F.string().describe(`Page ID to operate on`),text:F.string().describe(`The text to type character by character`),delay:F.number().optional().default(0).describe(`Delay between key presses in ms`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let Wo=class extends ${static{Ho=this}static TOOL_NAME=`browser_type`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return Uo}getDefinition(){return{name:Ho.TOOL_NAME,description:`Types text into an element character by character, simulating real keyboard input. Does not clear existing content. Use browser_fill for replacing content.`,inputSchema:F.toJSONSchema(Uo,{reused:`inline`})}}async execute(e){return this.executeWithMode(Ho.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId);return await(await this.elementLocator.locate(t,{selector:e.selector,xpath:e.xpath,uid:e.uid,frame:e.frame})).pressSequentially(e.text,{delay:e.delay,timeout:e.timeout}),this.success(`Typed: "${e.text}"`)})}};Wo=Ho=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],Wo);var Go;const Ko=U.extend({pageId:F.string().describe(`Page ID to operate on`),files:F.union([F.string(),F.array(F.string())]).describe(`File path(s) to upload`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let qo=class extends ${static{Go=this}static TOOL_NAME=`browser_upload_file`;constructor(e,t,n,r){super(e,t,n),this.elementLocator=r}getInputSchema(){return Ko}getDefinition(){return{name:Go.TOOL_NAME,description:`Uploads file(s) to a file input element.`,inputSchema:F.toJSONSchema(Ko,{reused:`inline`})}}async execute(e){return this.executeWithMode(Go.TOOL_NAME,e.pageId,e,async()=>{let t=this.resolvePage(e.pageId);await(await this.elementLocator.locate(t,e)).setInputFiles(e.files,{timeout:e.timeout});let n=Array.isArray(e.files)?e.files.length:1;return this.success(`Uploaded ${n} file(s) successfully`)})}};qo=Go=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),z(3,m(L.ElementLocatorService)),R(`design:paramtypes`,[Object,Object,Object,Object])],qo);var Jo;const Yo=F.object({pageId:F.string().describe(`Page ID to operate on`),type:F.enum([`element`,`text`,`timeout`,`loadState`]).optional().describe(`Type of wait condition`),selector:F.string().optional().describe(`CSS selector for element wait`),text:F.string().optional().describe(`Text to wait for (supports regex patterns)`),duration:F.number().optional().describe(`Duration in milliseconds for timeout wait`),state:F.enum([`load`,`domcontentloaded`,`networkidle`]).optional().describe(`Load state to wait for`),elementState:F.enum([`attached`,`detached`,`visible`,`hidden`]).optional().default(`visible`).describe(`Element state to wait for`),timeout:F.number().optional().default(V).describe(`Timeout in milliseconds`)});let Xo=class extends ${static{Jo=this}static TOOL_NAME=`browser_wait_for`;constructor(e,t,n){super(e,t,n)}getInputSchema(){return Yo}getDefinition(){return{name:Jo.TOOL_NAME,description:`Waits for a condition to be met: element visibility, text appearance, timeout duration, or page load state.`,inputSchema:F.toJSONSchema(Yo,{reused:`inline`})}}async execute(e){let t=e.type??(e.text?`text`:e.selector?`element`:e.duration===void 0?e.state?`loadState`:void 0:`timeout`);if(!t)return this.error(`Unable to infer wait type. Provide "type" or one of selector, text, duration, or state.`);if(t===`element`&&!e.selector)return this.error(`selector is required for element wait`);if(t===`text`&&!e.text)return this.error(`text is required for text wait`);if(t===`timeout`){let t=e.duration??1e3;return await new Promise(e=>setTimeout(e,t)),this.success(`Waited for ${t}ms`)}return this.executeWithMode(Jo.TOOL_NAME,e.pageId,{...e,type:t},async()=>{let n=this.resolvePage(e.pageId);switch(t){case`element`:if(!e.selector)throw Error(`selector is required for element wait`);return await n.locator(e.selector).waitFor({state:e.elementState??`visible`,timeout:e.timeout}),this.success(`Element "${e.selector}" is ${e.elementState??`visible`}`);case`text`:if(!e.text)throw Error(`text is required for text wait`);return await n.getByText(e.text).waitFor({state:`visible`,timeout:e.timeout}),this.success(`Text "${e.text}" appeared on page`);case`loadState`:{let t=e.state??`load`;return await n.waitForLoadState(t,{timeout:e.timeout}),this.success(`Page reached "${t}" state`)}default:throw Error(`Unknown wait type: ${t}`)}})}};Xo=Jo=B([h(),z(0,m(L.PageRegistry)),z(1,m(L.BrowserService)),z(2,m(L.ToolExecutor)),R(`design:paramtypes`,[Object,Object,Object])],Xo);const Zo=new p(e=>{e.bind(L.PortRegistryService).toDynamicValue(()=>new u(process.env.PORT_REGISTRY_PATH)).inSingletonScope(),e.bind(L.ProcessRegistryService).toDynamicValue(()=>new d(process.env.PROCESS_REGISTRY_PATH)).inSingletonScope(),e.bind(L.HttpServerHealthCheck).to(Wn).inSingletonScope(),e.bind(L.HttpServerManager).to(cr).inSingletonScope(),e.bind(L.McpPortAllocationService).to(mr).inSingletonScope(),e.bind(L.McpSessionTracker).to(hr).inSingletonScope()}),Qo=new p(e=>{e.bind(L.ProfileService).to(yr).inSingletonScope(),e.bind(L.ProxyConfigService).to(X).inSingletonScope(),e.bind(L.PageRegistry).to(_r).inSingletonScope(),e.bind(L.BrowserService).to($t).inSingletonScope(),e.bind(L.BrowserLockManager).to(Ge).inSingletonScope(),e.bind(L.ChromeForTestingService).to(st).inSingletonScope(),e.bind(L.ElementLocatorService).to(cn).inSingletonScope(),e.bind(L.PageMonitorService).to(gr).inSingletonScope(),e.bind(L.PauseController).to(vr).inSingletonScope(),e.bind(L.SpecBundlerService).to(Vr).inSingletonScope(),e.bind(L.SpecDiscoveryService).to(Hr).inSingletonScope(),e.bind(L.SpecMetadataService).to(Ur).inSingletonScope(),e.bind(L.SetupRunner).to(Pr).inSingletonScope(),e.bind(L.WebServerManager).to(Yr).inSingletonScope(),e.bind(L.SpecRunner).to(Gr).inSingletonScope(),e.bind(L.AutomationRunner).to(Ue).inSingletonScope(),e.bind(L.TelemetryService).to(G).inSingletonScope(),e.bind(L.CodeSnippetService).toDynamicValue(()=>new an(process.env.BROWSE_TOOL_SNIPPETS_DIR)).inSingletonScope(),e.bind(L.ExtensionTaskQueue).to(In).inSingletonScope(),e.bind(L.ExtensionToolDelegator).to(zn).inSingletonScope(),e.bind(L.ToolExecutor).to(Jr).inSingletonScope(),e.bind(L.ExtensionSessionRegistry).to(hn).inSingletonScope(),e.bind(L.ExtensionPageProxy).to(pn).inSingletonScope(),e.bind(L.ExtensionSpecRunner).to(gn).inSingletonScope(),e.bind(L.StealthLauncher).to(Kr).inSingletonScope(),e.bind(L.HttpServerHealthCheck).to(Wn).inSingletonScope(),e.bind(L.HttpBrowserClient).to(Un).inSingletonScope(),e.bind(L.RemoteToolExecutor).to(Nr).inSingletonScope(),e.bind(L.McpPortAllocationService).to(mr).inSingletonScope(),e.bind(L.ProcessRegistryService).toDynamicValue(()=>new d(process.env.PROCESS_REGISTRY_PATH)).inSingletonScope(),e.bind(L.WebSocketHub).to(fi).inSingletonScope(),e.bind(L.IdleCleanupService).to(pr).inSingletonScope()}),$o=new p(e=>{e.bind(L.Tool).to(bi).inSingletonScope(),e.bind(L.Tool).to(Zi).inSingletonScope(),e.bind(L.Tool).to(Wo).inSingletonScope(),e.bind(L.Tool).to(Eo).inSingletonScope(),e.bind(L.Tool).to(fa).inSingletonScope(),e.bind(L.Tool).to(zi).inSingletonScope(),e.bind(L.Tool).to(eo).inSingletonScope(),e.bind(L.Tool).to(ha).inSingletonScope(),e.bind(L.Tool).to(qo).inSingletonScope(),e.bind(L.Tool).to(Ga).inSingletonScope(),e.bind(L.Tool).to(ra).inSingletonScope(),e.bind(L.Tool).to(oa).inSingletonScope(),e.bind(L.Tool).to(ro).inSingletonScope(),e.bind(L.Tool).to(gi).inSingletonScope(),e.bind(L.Tool).to(Xo).inSingletonScope(),e.bind(L.Tool).to(ko).inSingletonScope(),e.bind(L.Tool).to(bo).inSingletonScope(),e.bind(L.Tool).to(Za).inSingletonScope(),e.bind(L.Tool).to(Ia).inSingletonScope(),e.bind(L.Tool).to(Ja).inSingletonScope(),e.bind(L.Tool).to(Co).inSingletonScope(),e.bind(L.Tool).to(Ei).inSingletonScope(),e.bind(L.Tool).to(la).inSingletonScope(),e.bind(L.Tool).to(Na).inSingletonScope(),e.bind(L.Tool).to(ea).inSingletonScope(),e.bind(L.Tool).to(Aa).inSingletonScope(),e.bind(L.Tool).to(Gi).inSingletonScope(),e.bind(L.Tool).to(Hi).inSingletonScope(),e.bind(L.Tool).to(oo).inSingletonScope(),e.bind(L.Tool).to(Ji).inSingletonScope(),e.bind(L.Tool).to(Mo).inSingletonScope(),e.bind(L.Tool).to(Ro).inSingletonScope(),e.bind(L.Tool).to(Fo).inSingletonScope(),e.bind(L.Tool).to(Vo).inSingletonScope(),e.bind(L.Tool).to(za).inSingletonScope(),e.bind(L.Tool).to(Ha).inSingletonScope(),e.bind(L.Tool).to(ki).inSingletonScope(),e.bind(L.Tool).to(Mi).inSingletonScope(),e.bind(L.Tool).to(_o).inSingletonScope(),e.bind(L.Tool).to(mo).inSingletonScope(),e.bind(L.Tool).to(Fi).inSingletonScope(),e.bind(L.Tool).to(Da).inSingletonScope(),e.bind(L.Tool).to(Ci).inSingletonScope(),e.bind(L.Tool).to(lo).inSingletonScope()});function es(){let e=new f({defaultScope:`Singleton`});return e.load(Zo),e}function ts(){let e=new f({defaultScope:`Singleton`});return e.load(Qo,$o),e}const ns=new p(e=>{e.bind(L.PortRegistryService).toDynamicValue(()=>new u(process.env.PORT_REGISTRY_PATH)).inSingletonScope(),e.bind(L.ProfileService).to(yr).inSingletonScope(),e.bind(L.ProxyConfigService).to(X).inSingletonScope(),e.bind(L.PageRegistry).to(_r).inSingletonScope(),e.bind(L.BrowserService).to($t).inSingletonScope(),e.bind(L.ElementLocatorService).to(cn).inSingletonScope(),e.bind(L.PageMonitorService).to(gr).inSingletonScope(),e.bind(L.PauseController).to(vr).inSingletonScope(),e.bind(L.SpecBundlerService).to(Vr).inSingletonScope(),e.bind(L.SpecDiscoveryService).to(Hr).inSingletonScope(),e.bind(L.SpecMetadataService).to(Ur).inSingletonScope(),e.bind(L.SetupRunner).to(Pr).inSingletonScope(),e.bind(L.WebServerManager).to(Yr).inSingletonScope(),e.bind(L.SpecRunner).to(Gr).inSingletonScope(),e.bind(L.AutomationRunner).to(Ue).inSingletonScope(),e.bind(L.HttpServerHealthCheck).to(Wn).inSingletonScope(),e.bind(L.HttpServerManager).to(cr).inSingletonScope(),e.bind(L.HttpBrowserClient).to(Un).inSingletonScope(),e.bind(L.RemoteToolExecutor).to(Nr).inSingletonScope(),e.bind(L.McpPortAllocationService).to(mr).inSingletonScope(),e.bind(L.ProcessRegistryService).toDynamicValue(()=>new d(process.env.PROCESS_REGISTRY_PATH)).inSingletonScope(),e.bind(L.ExtensionTaskQueue).to(In).inSingletonScope(),e.bind(L.ExtensionToolDelegator).to(zn).inSingletonScope(),e.bind(L.ToolExecutor).to(Jr).inSingletonScope(),e.bind(L.StealthLauncher).to(Kr).inSingletonScope(),e.bind(L.BrowserLockManager).to(Ge).inSingletonScope(),e.bind(L.ExtensionSessionRegistry).to(hn).inSingletonScope(),e.bind(L.ExtensionPageProxy).to(pn).inSingletonScope(),e.bind(L.ExtensionSpecRunner).to(gn).inSingletonScope(),e.bind(L.McpSessionTracker).to(hr).inSingletonScope(),e.bind(L.ChromeForTestingService).to(st).inSingletonScope(),e.bind(L.IdleCleanupService).to(pr).inSingletonScope()}),rs=new f({defaultScope:`Singleton`});rs.load(ns,$o);function is(){let e=new f({defaultScope:`Singleton`});return e.load(ns,$o),e}var as=class extends Error{code=`TRANSPORT_CONNECTION_ERROR`;recovery=`Check that stdio streams are available and try again.`;constructor(e,t){super(e,t),this.name=`TransportConnectionError`}},os=class{server;transport=null;constructor(e){this.server=e}async start(){let e=new Be;try{await this.server.connect(e),this.transport=e,console.error(`browse-tool MCP server started on stdio`)}catch(t){try{await e.close()}catch{}let n=new as(`Failed to establish stdio transport connection`,{cause:t});throw console.error(`[${n.code}] ${n.message}. Recovery: ${n.recovery}`),n}}async stop(){this.transport&&=(await this.transport.close(),null)}},ss=class extends Error{code=`TRANSPORT_CONNECTION_ERROR`;recovery=`Check that the HTTP host and port are available and try again.`;constructor(e,t){super(e,t),this.name=`TransportConnectionError`}};async function cs(e){let t=[];for await(let n of e)t.push(typeof n==`string`?Buffer.from(n):n);if(t.length===0)return;let n=Buffer.concat(t).toString(`utf8`).trim();if(n)return JSON.parse(n)}function ls(e,t,n){e.headersSent||(e.writeHead(t,{"Content-Type":`application/json`}),e.end(JSON.stringify(n)))}var us=class{sessions=new Map;closingSessions=new Set;get(e){return this.sessions.get(e)}set(e,t){this.sessions.set(e,t)}has(e){return this.sessions.has(e)}remove(e){this.sessions.delete(e)}isClosing(e){return this.closingSessions.has(e)}async closeSession(e){let t=this.sessions.get(e);if(t){this.closingSessions.add(e),this.sessions.delete(e),t.server.close();try{await t.onClose?.()}finally{this.closingSessions.delete(e)}}}async clear(){let e=[...this.sessions.keys()];for(let t of e)await this.closeSession(t)}},ds=class{sessionFactory;config;sessionManager=new us;httpServer=null;constructor(e,t){this.sessionFactory=e,this.config={host:t.host,port:t.port,path:t.path??`/mcp`}}async createSession(e){let t=this.sessionFactory({headers:e.headers}),n=new He({sessionIdGenerator:()=>I(),enableJsonResponse:!0,onsessioninitialized:e=>{this.sessionManager.set(e,{server:t.server,transport:n,onClose:t.onClose})},onsessionclosed:async e=>{await this.sessionManager.closeSession(e)}});return n.onclose=()=>{n.sessionId&&(this.sessionManager.isClosing(n.sessionId)||this.sessionManager.remove(n.sessionId))},await t.server.connect(n),n}async handleMcpRequest(e,t){let n=e.headers[`mcp-session-id`],r=Array.isArray(n)?n[0]:n,i;if(e.method===`POST`)try{i=await cs(e)}catch(e){ls(t,400,{jsonrpc:`2.0`,error:{code:-32700,message:e instanceof Error?e.message:`Invalid JSON body`},id:null});return}let a;if(r?a=this.sessionManager.get(r)?.transport:e.method===`POST`&&ze(i)&&(a=await this.createSession(e)),!a){ls(t,r?404:400,{jsonrpc:`2.0`,error:{code:-32e3,message:r?`Session not found`:`Bad Request: No valid session ID provided`},id:null});return}await a.handleRequest(e,t,i)}async start(){if(this.httpServer)throw new ss(`HTTP transport server is already running`);await new Promise((e,t)=>{let n=Ve((e,t)=>{(async()=>{try{let n=new URL(e.url??`/`,`http://${e.headers.host??this.config.host}`).pathname;if(n===`/health`&&e.method===`GET`){ls(t,200,{status:`ok`,transport:`streamable-http`});return}if(n!==this.config.path){ls(t,404,{error:`Not found`});return}await this.handleMcpRequest(e,t)}catch(e){ls(t,500,{jsonrpc:`2.0`,error:{code:-32603,message:(e instanceof Error?e:Error(String(e))).message},id:null})}})()});n.once(`error`,e=>{t(new ss(`Failed to establish streamable HTTP transport connection`,{cause:e}))}),n.listen(this.config.port,this.config.host,()=>{this.httpServer=n,process.stderr.write(`browse-tool MCP server started on http://${this.config.host}:${this.config.port}${this.config.path}\n`),process.stderr.write(`Health check: http://${this.config.host}:${this.config.port}/health\n`),e()})})}async stop(){if(await this.sessionManager.clear(),!this.httpServer)return;let e=this.httpServer;this.httpServer=null,await new Promise((t,n)=>{e.close(e=>{if(e){n(e);return}t()})})}};export{Ct as C,Ke as D,Ye as E,L as O,Dt as S,qe as T,Mn as _,ts as a,Ht as b,Mr as c,Bn as d,q as f,G as g,In as h,is as i,hr as l,zn as m,os as n,es as o,K as p,rs as r,br as s,ds as t,dr as u,pn as v,tt as w,Vt as x,zt as y};
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../playwright-test-Z75N9VJh.cjs`);exports.SkipTestError=e.t,exports.chromium=e.n,exports.defineConfig=e.r,exports.devices=e.i,exports.expect=e.a,exports.firefox=e.o,exports.getCollectedSuite=e.s,exports.initCollector=e.c,exports.mergeExpects=e.l,exports.mergeTests=e.u,exports.peekCollectedSuite=e.d,exports.request=e.f,exports.resetCollector=e.p,exports.selectors=e.m,exports.splitUseConfig=e.h,exports.test=e.g,exports.webkit=e._;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../playwright-test-BGq6XsSX.cjs`);exports.SkipTestError=e.t,exports.chromium=e.n,exports.defineConfig=e.r,exports.devices=e.i,exports.expect=e.a,exports.firefox=e.o,exports.getCollectedSuite=e.s,exports.initCollector=e.c,exports.mergeExpects=e.l,exports.mergeTests=e.u,exports.peekCollectedSuite=e.d,exports.request=e.f,exports.resetCollector=e.p,exports.selectors=e.m,exports.splitUseConfig=e.h,exports.test=e.g,exports.webkit=e._;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agimon-ai/browse-tool",
3
3
  "description": "MCP server for browser automation using Playwright with profile management, page registry, and multi-browser support",
4
- "version": "0.17.2",
4
+ "version": "0.17.4",
5
5
  "license": "BUSL-1.1",
6
6
  "keywords": [
7
7
  "mcp",
@@ -44,10 +44,10 @@
44
44
  "ws": "8.20.0",
45
45
  "yaml": "2.8.3",
46
46
  "zod": "4.4.1",
47
- "@agimon-ai/foundation-port-registry": "0.17.2",
48
- "@agimon-ai/foundation-process-registry": "0.17.2",
49
- "@agimon-ai/log-sink-mcp": "0.17.2",
50
- "@agimon-ai/foundation-validator": "0.14.2"
47
+ "@agimon-ai/foundation-port-registry": "0.17.4",
48
+ "@agimon-ai/foundation-process-registry": "0.17.4",
49
+ "@agimon-ai/log-sink-mcp": "0.17.4",
50
+ "@agimon-ai/foundation-validator": "0.14.4"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/chrome": "0.1.40",