@agimon-ai/browse-tool 0.16.0 → 0.17.0

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
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./playwright-test-B8e8viSw.cjs`);require(`reflect-metadata/lite`);let l=require(`@agimon-ai/foundation-port-registry`),u=require(`@agimon-ai/foundation-process-registry`),d=require(`inversify`),f=require(`node:path`);f=s(f,1);let p=require(`node:fs`);p=s(p,1);let m=require(`node:fs/promises`);m=s(m,1);let h=require(`node:os`);h=s(h,1);let g=require(`node:child_process`),_=require(`node:url`),v=require(`playwright`);v=s(v,1);let y=require(`node:stream`),b=require(`node:stream/promises`),x=require(`node:async_hooks`),S=require(`node:module`),C=require(`zod`),w=require(`@agimon-ai/log-sink-mcp`),T=require(`@opentelemetry/api`),ee=require(`@opentelemetry/exporter-trace-otlp-http`),E=require(`@opentelemetry/resources`),D=require(`@opentelemetry/sdk-trace-node`),O=require(`node:util`),te=require(`yaml`),ne=require(`esbuild`),k=require(`node:crypto`),re=require(`@modelcontextprotocol/sdk/types.js`),ie=require(`@modelcontextprotocol/sdk/server/stdio.js`),ae=require(`node:http`),oe=require(`@modelcontextprotocol/sdk/server/streamableHttp.js`);const A={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 j(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function M(e,t){return function(n,r){t(n,r,e)}}function N(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 se=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:p,hooksPath:m}=e,h=n??`spec-${++this.sessionIdCounter}`,g={id:h,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(h,g);let _,v;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?(0,f.dirname)(s):process.cwd();l&&e?.webServer&&(_=await this.webServerManager.startServer(e.webServer,n));let y=!!(d&&p);if(y&&(g.browserId=d,g.pageIds.push(p)),g.status=`running`,g.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(v=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!v.success)throw Error(v.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let b=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:h,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,...v?.state},hooksPath:m,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&b.browser){let e=this.browserService.registerExternalBrowser(b.browser);if(g.browserId=e,b.keptPages?.length)for(let t of b.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=g.pageIds.indexOf(e);t>=0&&g.pageIds.splice(t,1)}});g.pageIds.push(n)}}return g.status=`completed`,g.updatedAt=new Date,u&&_?.started&&await this.webServerManager.stopServer(),{automationId:h,status:`completed`,pageIds:r||y?[...g.pageIds]:[],browserId:r||y?g.browserId:null,specResult:b,serverResult:_,setupResult:v}}catch(e){g.status=`failed`,g.error=e instanceof Error?e.message:String(e),g.updatedAt=new Date,u&&_?.started&&await this.webServerManager.stopServer();let t=!!(d&&p);return{automationId:h,status:`failed`,pageIds:t?[...g.pageIds]:[],browserId:t?g.browserId:null,serverResult:_,setupResult:v}}}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}};se=N([(0,d.injectable)(),M(0,(0,d.inject)(A.BrowserService)),M(1,(0,d.inject)(A.SpecRunner)),M(2,(0,d.inject)(A.SpecMetadataService)),M(3,(0,d.inject)(A.SetupRunner)),M(4,(0,d.inject)(A.WebServerManager)),M(5,(0,d.inject)(A.PageRegistry)),j(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],se);var ce=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let le=class{platform=h.default.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=[f.default.join(e,`SingletonLock`),f.default.join(e,`SingletonCookie`),f.default.join(e,`SingletonSocket`),f.default.join(e,`lockfile`)];for(let e of n)try{p.default.existsSync(e)&&await(0,m.unlink)(e)}catch{}let r=f.default.join(e,`Default`,`LOCK`);try{p.default.existsSync(r)&&await(0,m.unlink)(r)}catch{}return!0}async ensureAvailable(e){if(!p.default.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new ce(`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=f.default.join(e,`SingletonLock`),r=f.default.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=p.default.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=p.default.existsSync(r)}return!t.isLocked&&p.default.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return p.default.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=f.default.join(e,`lockfile`);if(t.lockFilePath=n,p.default.existsSync(n))try{let e=await(0,m.readFile)(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`}}};le=N([(0,d.injectable)()],le);var ue=`147.0.7727.50`,de=`browse-tool/chrome-for-testing:147.0.7727.50`,fe=`/usr/local/bin/google-chrome-for-testing`,pe=`linux/amd64`,me=`browse-tool/cloakbrowser:latest`,he=`/usr/local/bin/cloakbrowser`,ge=`linux/amd64`;const P=18e4;var _e;const ve=ue,ye=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function be(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 xe(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:ve}function Se(){let e=process.env[ye]?.trim();return e&&e.length>0?e:void 0}function Ce(e){return be(e,`147.0.7727.3`)>=0}function we(){return Ce(xe())}let Te=class{static{_e=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=(0,f.join)((0,h.homedir)(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=Se();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(0,p.existsSync)(t)&&n===e?t:this.download(r)}catch(e){if((0,p.existsSync)(t))return t;throw e}}async isInstalled(){return(0,p.existsSync)(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=(0,f.join)(this.cacheDir,`version.txt`);if(!(0,p.existsSync)(e))return null;try{return(await(0,m.readFile)(e,`utf-8`)).trim()}catch{return null}}async update(){let e=(0,f.join)(this.cacheDir,`chrome`);return(0,p.existsSync)(e)&&await(0,m.rm)(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){(0,p.mkdirSync)(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=(0,f.join)(this.cacheDir,`chrome.zip`),r=(0,f.join)(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await(0,m.rm)(n,{force:!0}),await(0,m.writeFile)((0,f.join)(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!(0,p.existsSync)(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return(0,h.platform)()!==`win32`&&await(0,m.chmod)(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 xe()}async validateConfiguredExecutablePath(e){try{if(!(await(0,m.stat)(e)).isFile())throw Error(`configured path is not a file`);return(0,h.platform)()!==`win32`&&await(0,m.access)(e,p.constants.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${ye} 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=(0,h.platform)(),t=(0,h.arch)();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=(0,h.platform)(),t=(0,f.join)(this.cacheDir,`chrome`);if(e===`darwin`)return(0,f.join)(t,`chrome-mac-${(0,h.arch)()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return(0,f.join)(t,`chrome-linux64`,`chrome`);if(e===`win32`)return(0,f.join)(t,`chrome-win${(0,h.arch)()===`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=(0,p.createWriteStream)(r),a=n.body.getReader();await(0,b.pipeline)(new y.Readable({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await(0,m.rename)(r,t)}async extractZip(e,t){let n=(0,h.platform)();return new Promise((r,i)=>{let a;n===`win32`?a=(0,g.spawn)(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):((0,p.mkdirSync)(t,{recursive:!0}),a=(0,g.spawn)(`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}`))})})}};Te=_e=N([(0,d.injectable)(),j(`design:paramtypes`,[])],Te);const Ee=[`pnpm-workspace.yaml`,`nx.json`,`.git`],De=de,Oe=pe,ke=De.split(`:`)[0]??De,Ae=me,je=Ae.split(`:`)[0]??Ae;function Me(e=process.cwd()){let t=f.default.resolve(e);for(;;){for(let e of Ee)if(Ne(f.default.join(t,e)))return t;let n=f.default.dirname(t);if(n===t)return e;t=n}}function Ne(e){return p.default.existsSync(e)}function Pe(){return ke}function Fe(e){return`${Pe()}:${e}`}function Ie(e){let t=f.default.resolve(e);for(let e=0;e<8;e+=1){let e=f.default.join(t,`package.json`);if(Ne(e))try{if(JSON.parse(p.default.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=f.default.dirname(t);if(n===t)break;t=n}return f.default.resolve(e,`../..`)}function Le(){let e=f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));return f.default.join(Ie(e),`docker/chrome-for-testing.Dockerfile`)}function Re(){return Me(Ie(f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href))))}function ze(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function Be(e){return(e?.trim()||Oe).toLowerCase()}function Ve(e){let t=Be(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function He(e){return e.startsWith(`${Pe()}:`)}async function Ue(e,t){return await new Promise((n,r)=>{let i=``,a=(0,g.spawn)(`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 We(e){return(await Ue([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Ge(e={}){let t=e.version?.trim()||ze(e.image)||xe(),n=e.image?.trim()||Fe(t),r=Be(e.platform),i=Ve(r),a=e.stdio??`inherit`,o=await Ue([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,Le(),Re()],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 Ke(e){return!He(e.image)||await We(e.image)?!1:(await Ge({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function qe(e){return e.startsWith(`${je}:`)}function Je(){let e=f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));return f.default.join(Ie(e),`docker/cloakbrowser.Dockerfile`)}async function Ye(e={}){let t=e.image?.trim()||Ae,n=Be(e.platform),r=e.stdio??`inherit`,i=await Ue([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,Je(),Re()],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 Xe(e){return!qe(e.image)||await We(e.image)?!1:(await Ye({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Ze(){let e=new Map,t=(0,g.execSync)(`ps -axo pid=,ppid=`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:3e3});for(let n of t.split(`
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./playwright-test-Z75N9VJh.cjs`);require(`reflect-metadata/lite`);let l=require(`@agimon-ai/foundation-port-registry`),u=require(`@agimon-ai/foundation-process-registry`),d=require(`inversify`),f=require(`node:path`);f=s(f,1);let p=require(`node:fs`);p=s(p,1);let m=require(`node:fs/promises`);m=s(m,1);let h=require(`node:os`);h=s(h,1);let g=require(`node:child_process`),_=require(`node:url`),v=require(`playwright`);v=s(v,1);let y=require(`node:stream`),b=require(`node:stream/promises`),x=require(`node:async_hooks`),S=require(`node:module`),C=require(`zod`),w=require(`@agimon-ai/log-sink-mcp`),T=require(`@opentelemetry/api`),ee=require(`@opentelemetry/exporter-trace-otlp-http`),E=require(`@opentelemetry/resources`),D=require(`@opentelemetry/sdk-trace-node`),O=require(`node:util`),te=require(`yaml`),ne=require(`esbuild`),k=require(`node:crypto`),re=require(`@modelcontextprotocol/sdk/types.js`),ie=require(`@modelcontextprotocol/sdk/server/stdio.js`),ae=require(`node:http`),oe=require(`@modelcontextprotocol/sdk/server/streamableHttp.js`);const A={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 j(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function M(e,t){return function(n,r){t(n,r,e)}}function N(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 se=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:p,hooksPath:m}=e,h=n??`spec-${++this.sessionIdCounter}`,g={id:h,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(h,g);let _,v;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?(0,f.dirname)(s):process.cwd();l&&e?.webServer&&(_=await this.webServerManager.startServer(e.webServer,n));let y=!!(d&&p);if(y&&(g.browserId=d,g.pageIds.push(p)),g.status=`running`,g.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(v=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!v.success)throw Error(v.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let b=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:h,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,...v?.state},hooksPath:m,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&b.browser){let e=this.browserService.registerExternalBrowser(b.browser);if(g.browserId=e,b.keptPages?.length)for(let t of b.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=g.pageIds.indexOf(e);t>=0&&g.pageIds.splice(t,1)}});g.pageIds.push(n)}}return g.status=`completed`,g.updatedAt=new Date,u&&_?.started&&await this.webServerManager.stopServer(),{automationId:h,status:`completed`,pageIds:r||y?[...g.pageIds]:[],browserId:r||y?g.browserId:null,specResult:b,serverResult:_,setupResult:v}}catch(e){g.status=`failed`,g.error=e instanceof Error?e.message:String(e),g.updatedAt=new Date,u&&_?.started&&await this.webServerManager.stopServer();let t=!!(d&&p);return{automationId:h,status:`failed`,pageIds:t?[...g.pageIds]:[],browserId:t?g.browserId:null,serverResult:_,setupResult:v}}}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}};se=N([(0,d.injectable)(),M(0,(0,d.inject)(A.BrowserService)),M(1,(0,d.inject)(A.SpecRunner)),M(2,(0,d.inject)(A.SpecMetadataService)),M(3,(0,d.inject)(A.SetupRunner)),M(4,(0,d.inject)(A.WebServerManager)),M(5,(0,d.inject)(A.PageRegistry)),j(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],se);var ce=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let le=class{platform=h.default.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=[f.default.join(e,`SingletonLock`),f.default.join(e,`SingletonCookie`),f.default.join(e,`SingletonSocket`),f.default.join(e,`lockfile`)];for(let e of n)try{p.default.existsSync(e)&&await(0,m.unlink)(e)}catch{}let r=f.default.join(e,`Default`,`LOCK`);try{p.default.existsSync(r)&&await(0,m.unlink)(r)}catch{}return!0}async ensureAvailable(e){if(!p.default.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new ce(`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=f.default.join(e,`SingletonLock`),r=f.default.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=p.default.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=p.default.existsSync(r)}return!t.isLocked&&p.default.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return p.default.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=f.default.join(e,`lockfile`);if(t.lockFilePath=n,p.default.existsSync(n))try{let e=await(0,m.readFile)(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`}}};le=N([(0,d.injectable)()],le);var ue=`147.0.7727.50`,de=`browse-tool/chrome-for-testing:147.0.7727.50`,fe=`/usr/local/bin/google-chrome-for-testing`,pe=`linux/amd64`,me=`browse-tool/cloakbrowser:latest`,he=`/usr/local/bin/cloakbrowser`,ge=`linux/amd64`;const P=18e4;var _e;const ve=ue,ye=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function be(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 xe(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:ve}function Se(){let e=process.env[ye]?.trim();return e&&e.length>0?e:void 0}function Ce(e){return be(e,`147.0.7727.3`)>=0}function we(){return Ce(xe())}let Te=class{static{_e=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=(0,f.join)((0,h.homedir)(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=Se();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(0,p.existsSync)(t)&&n===e?t:this.download(r)}catch(e){if((0,p.existsSync)(t))return t;throw e}}async isInstalled(){return(0,p.existsSync)(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=(0,f.join)(this.cacheDir,`version.txt`);if(!(0,p.existsSync)(e))return null;try{return(await(0,m.readFile)(e,`utf-8`)).trim()}catch{return null}}async update(){let e=(0,f.join)(this.cacheDir,`chrome`);return(0,p.existsSync)(e)&&await(0,m.rm)(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){(0,p.mkdirSync)(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=(0,f.join)(this.cacheDir,`chrome.zip`),r=(0,f.join)(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await(0,m.rm)(n,{force:!0}),await(0,m.writeFile)((0,f.join)(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!(0,p.existsSync)(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return(0,h.platform)()!==`win32`&&await(0,m.chmod)(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 xe()}async validateConfiguredExecutablePath(e){try{if(!(await(0,m.stat)(e)).isFile())throw Error(`configured path is not a file`);return(0,h.platform)()!==`win32`&&await(0,m.access)(e,p.constants.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${ye} 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=(0,h.platform)(),t=(0,h.arch)();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=(0,h.platform)(),t=(0,f.join)(this.cacheDir,`chrome`);if(e===`darwin`)return(0,f.join)(t,`chrome-mac-${(0,h.arch)()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return(0,f.join)(t,`chrome-linux64`,`chrome`);if(e===`win32`)return(0,f.join)(t,`chrome-win${(0,h.arch)()===`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=(0,p.createWriteStream)(r),a=n.body.getReader();await(0,b.pipeline)(new y.Readable({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await(0,m.rename)(r,t)}async extractZip(e,t){let n=(0,h.platform)();return new Promise((r,i)=>{let a;n===`win32`?a=(0,g.spawn)(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):((0,p.mkdirSync)(t,{recursive:!0}),a=(0,g.spawn)(`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}`))})})}};Te=_e=N([(0,d.injectable)(),j(`design:paramtypes`,[])],Te);const Ee=[`pnpm-workspace.yaml`,`nx.json`,`.git`],De=de,Oe=pe,ke=De.split(`:`)[0]??De,Ae=me,je=Ae.split(`:`)[0]??Ae;function Me(e=process.cwd()){let t=f.default.resolve(e);for(;;){for(let e of Ee)if(Ne(f.default.join(t,e)))return t;let n=f.default.dirname(t);if(n===t)return e;t=n}}function Ne(e){return p.default.existsSync(e)}function Pe(){return ke}function Fe(e){return`${Pe()}:${e}`}function Ie(e){let t=f.default.resolve(e);for(let e=0;e<8;e+=1){let e=f.default.join(t,`package.json`);if(Ne(e))try{if(JSON.parse(p.default.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=f.default.dirname(t);if(n===t)break;t=n}return f.default.resolve(e,`../..`)}function Le(){let e=f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));return f.default.join(Ie(e),`docker/chrome-for-testing.Dockerfile`)}function Re(){return Me(Ie(f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href))))}function ze(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function Be(e){return(e?.trim()||Oe).toLowerCase()}function Ve(e){let t=Be(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function He(e){return e.startsWith(`${Pe()}:`)}async function Ue(e,t){return await new Promise((n,r)=>{let i=``,a=(0,g.spawn)(`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 We(e){return(await Ue([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Ge(e={}){let t=e.version?.trim()||ze(e.image)||xe(),n=e.image?.trim()||Fe(t),r=Be(e.platform),i=Ve(r),a=e.stdio??`inherit`,o=await Ue([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,Le(),Re()],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 Ke(e){return!He(e.image)||await We(e.image)?!1:(await Ge({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function qe(e){return e.startsWith(`${je}:`)}function Je(){let e=f.default.dirname((0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));return f.default.join(Ie(e),`docker/cloakbrowser.Dockerfile`)}async function Ye(e={}){let t=e.image?.trim()||Ae,n=Be(e.platform),r=e.stdio??`inherit`,i=await Ue([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,Je(),Re()],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 Xe(e){return!qe(e.image)||await We(e.image)?!1:(await Ye({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Ze(){let e=new Map,t=(0,g.execSync)(`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 Qe(e){let t=[];try{let n;try{n=(0,g.execSync)(`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=Ze().get(e)??[]}for(let e of n)t.push(...Qe(e)),t.push(e)}catch{}return t}function $e(e){try{return process.kill(e,0),!0}catch{return!1}}async function et(e,t=3e3){if(!$e(e))return;let n=[...Qe(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($e))return;await new Promise(e=>setTimeout(e,100))}for(let e of n)try{$e(e)&&process.kill(e,`SIGKILL`)}catch{}}function tt(e,t){let n=f.default.join(h.default.tmpdir(),`proxy-auth-ext-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);p.default.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 p.default.writeFileSync(f.default.join(n,`manifest.json`),JSON.stringify(r,null,2)),p.default.writeFileSync(f.default.join(n,`background.js`),i),n}function nt(e){let t=[`--proxy-server=${e.server}`];return e.bypass&&t.push(`--proxy-bypass-list=${e.bypass}`),t}const rt=new x.AsyncLocalStorage;function it(e,t){return e?rt.run(e,t):t()}function at(){return rt.getStore()}var F;const ot=new Set([`docker`,`docker-vm`,`docker-chromium`,`docker-chrome-testing`,`docker-cloakbrowser`]),st=de,ct=fe,lt=me,ut=he,dt=4e3,I=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`,ft=[`Current Session`,`Current Tabs`,`Last Session`,`Last Tabs`,`Sessions`],pt=new Set([`Default`,`Guest Profile`,`System Profile`]);var mt=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 ht=class{static{F=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=f.default.join(o,`manifest.json`);if(!p.default.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],m=[];if(u.kind===`docker`&&m.push(...this.createDockerVolumeArgs(o,u.extensionPath,!0)),e.proxy?.username&&e.proxy?.password){let t=tt(e.proxy.username,e.proxy.password);if(u.kind===`docker`){let e=`${u.docker.extensionPathInContainer}-proxy-auth-${Date.now()}`;d.push(e),m.push(...this.createDockerVolumeArgs(t,e,!0))}else d.push(t)}let h=d.join(`,`),g=process.env.BROWSE_TOOL_AUTO_SELECT_TAB_CAPTURE_SOURCE_TITLE?.trim(),_=process.env.BROWSE_TOOL_AUTO_SELECT_DESKTOP_CAPTURE_SOURCE?.trim(),v=process.env.BROWSE_TOOL_USE_FAKE_UI_FOR_MEDIA_STREAM===`1`,y=u.kind===`docker`||process.platform===`linux`&&typeof process.getuid==`function`&&process.getuid()===0,b=[`--user-data-dir=${u.userDataDir}`,`--load-extension=${h}`,`--disable-extensions-except=${h}`,`--enable-extensions`,`--auto-accept-this-tab-capture`,...v?[`--use-fake-ui-for-media-stream`]:[],...g?[`--auto-select-tab-capture-source-by-title=${g}`]:[],..._?[`--auto-select-desktop-capture-source=${_}`]:[],`--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`}`,...y?[`--no-sandbox`,`--disable-setuid-sandbox`]:[],`--window-size=1920,1080`,...e.proxy?nt(e.proxy):[],e.url??`about:blank`],x=e=>{try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`||t.protocol===`file:`}catch{return!1}},S=e=>{if(!x(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}},C=S(e.url??`about:blank`),w=a===`vm`&&r.length>0?[...b,...r.filter(e=>x(e)?S(e)!==C:!0)]:r.length>0?r:b,T=u.kind===`docker`?{TZ:Intl.DateTimeFormat().resolvedOptions().timeZone,...e.dockerEnv}:e.dockerEnv,ee=u.kind===`docker`?[...this.buildDockerSpawnArgs(u.docker,l,m,e.dockerRunArgs,T,e.dockerEnableVnc),...w]:w,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 Ke({image:i,platform:s}):await Xe({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&&!p.default.existsSync(f.default.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||st,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||ct,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||pe}:{image:process.env.PLAYWRIGHT_VM_DOCKER_IMAGE||lt,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||ut,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||ge}}createDockerVolumeArgs(e,t,n){return[`-v`,`${f.default.resolve(e)}:${t}${n?`:ro`:``}`]}ensureDockerWritableUserDataDir(e){p.default.mkdirSync(e,{recursive:!0,mode:511});let t=[e];for(;t.length>0;){let e=t.pop();if(!e)continue;p.default.chmodSync(e,511);let n=p.default.readdirSync(e,{withFileTypes:!0});for(let r of n){let n=f.default.join(e,r.name);p.default.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(`
@@ -45,7 +45,7 @@ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=
45
45
  }
46
46
  return null;
47
47
  };
48
- `}};let At=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}};At=N([(0,d.injectable)()],At);let jt=class{constructor(e,t,n){this.bundler=e,this.pageProxy=t,this.sessionRegistry=n}async loadSpec(e){let t=await this.bundler.bundle(e),n=process,r=n.__pw_initiator__;try{return c.c(e),n.__pw_initiator__=void 0,await import(`${t.outputPath}?t=${Date.now()}`),c.s()}catch(t){throw c.p(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{n.__pw_initiator__=r,await t.cleanup()}}async executeSpec(e){let{specPath:t,sessionId:n,testFilter:r,specArgs:i,onHandoff:a}=e,o=Date.now(),s=[],l=0,u=0,d=!1,f=new Set,p=new Set;this.sessionRegistry.setActiveSpec(n,t);try{let e=await this.loadSpec(t),m=r?this.filterTests(e.allTests,r):e.allTests,h={page:this.pageProxy,requestHandoff:async e=>{if(!this.sessionRegistry.getSession(n))throw Error(`Session ${n} not found`);let t=await this.pageProxy.getSnapshot(),r=this.pageProxy.url();try{r=await this.pageProxy.currentUrlAsync()}catch{}this.sessionRegistry.requestHandoff({sessionId:n,reason:e,pageState:{url:r,snapshot:t}}),d=!0,a&&await a(n)},specArgs:i};for(let e of m){let{runtimeUse:t,unsupportedKeys:r,fixtureOverrides:i}=c.h(e.config.use);r.length>0&&f.add(`test.use() ignores unsupported fixture keys in extension mode: ${r.join(`, `)}`),Object.keys(t).length>0&&f.add(`test.use() runtime browser config is not applied in extension mode: ${Object.keys(t).join(`, `)}`);let a=e.serialScopeId,o=a!==void 0&&p.has(a);if(e.skip||o){s.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:0}),l++;continue}let d=this.sessionRegistry.getSession(n);if(d?.handoffRequested)break;let m=Date.now();try{await e.fn({...h,...i}),s.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);s.push({title:e.title,fullTitle:e.fullTitle,passed:!1,error:n,duration:Date.now()-m}),u++,a!==void 0&&p.add(a)}}return{specPath:t,totalTests:m.length,passed:l,failed:u,testResults:s,success:u===0,duration:Date.now()-o,handoffOccurred:d,sessionId:n,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()-o}],success:!1,duration:Date.now()-o,handoffOccurred:!1,sessionId:n,warnings:f.size>0?[...f]:void 0}}finally{this.sessionRegistry.setActiveSpec(n,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}};jt=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),M(1,(0,d.inject)(A.ExtensionPageProxy)),M(2,(0,d.inject)(A.ExtensionSessionRegistry)),j(`design:paramtypes`,[Object,Object,Object])],jt);const Mt=`browse-tool-http`,Nt=[`pnpm-workspace.yaml`,`nx.json`,`.git`],Pt=new Set([`127.0.0.1`,`localhost`,`::1`,`0.0.0.0`]);function Ft(e){return e===`1`||e===`true`}function It(e){return Array.isArray(e)?JSON.stringify(e):typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:JSON.stringify(e)}function Lt(e){if(!e)return;let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=It(r));return Object.keys(t).length>0?t:void 0}function Rt(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 zt(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>0?t:void 0}function Bt(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 Vt(e){return Pt.has(e)}function Ht(e=process.cwd()){let t=f.default.resolve(e);for(;;){for(let e of Nt)if((0,p.existsSync)(f.default.join(t,e)))return t;let e=f.default.dirname(t);if(e===t)return process.cwd();t=e}}function Ut(e,t=process.cwd()){try{let n=(0,p.readFileSync)(l.PortRegistryService.resolveRegistryPath(e.PORT_REGISTRY_PATH??l.DEFAULT_REGISTRY_PATH),`utf8`),r=l.PortRegistryStateSchema.parse(JSON.parse(n)),i=(0,l.normalizeRepositoryPath)(Ht(t)),a=e.NODE_ENV||`development`,o=e.BROWSE_TOOL_OTEL_LOG_SINK_SERVICE_NAME??`log-sink-mcp-http`,s=r.entries.find(e=>e.repositoryPath===i&&e.serviceName===o&&e.serviceType===`tool`&&(e.environment??`development`)===a);if(!s)return;let c=typeof s.metadata?.healthCheckUrl==`string`&&s.metadata.healthCheckUrl.length>0?s.metadata.healthCheckUrl:void 0;return c?new URL(c).origin:`http://${s.host===`0.0.0.0`?`127.0.0.1`:s.host}:${s.port}`}catch{return}}function Wt(e,t){try{let n=new URL(e),r=new URL(t);return!Vt(n.hostname)||Vt(r.hostname)||(n.hostname=r.hostname),n.toString()}catch{return e}}function Gt(e,t){let n=t===`traces`?e.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:e.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;if(n)return n;let r=e.OTEL_EXPORTER_OTLP_ENDPOINT;if(r)return Bt(r,t);let i=Ut(e);return i?Bt(i,t):void 0}function Kt(e,t,n){let r=t===`traces`?e.BROWSE_TOOL_OTEL_BROWSER_TRACES_ENDPOINT:e.BROWSE_TOOL_OTEL_BROWSER_LOGS_ENDPOINT;if(r)return Wt(r,n);let i=e.BROWSE_TOOL_OTEL_BROWSER_ENDPOINT;if(i)return Wt(Bt(i,t),n);let a=Gt(e,t);return a?Wt(a,n):void 0}function qt(e,t){let n=e.BROWSE_TOOL_OTEL_BROWSER_SERVICE_NAME??`${e.OTEL_SERVICE_NAME??Mt}-extension`,r=Kt(e,`traces`,t),i=Kt(e,`logs`,t);return{enabled:!!(r||i),tracesEndpoint:r,logsEndpoint:i,serviceName:n}}function Jt(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:Gt(e,t),headers:Rt(n??e.OTEL_EXPORTER_OTLP_HEADERS),timeoutMillis:zt(r??e.OTEL_EXPORTER_OTLP_TIMEOUT)}}function Yt(e){return e instanceof Error?e.message:String(e)}function Xt(e){if(e instanceof Error)return Lt({"exception.type":e.name,"exception.message":e.message,"exception.stacktrace":e.stack})}let z=class{contextStore=new x.AsyncLocalStorage;tracesEnabled;logsEnabled;tracerProvider;tracer;logSinkReady;constructor(e={}){let t=e.env??process.env,n=Ft(t.OTEL_SDK_DISABLED),r=Ft(t.BROWSE_TOOL_OTEL_ENABLED),i=Jt(t,`traces`);this.tracesEnabled=!n&&(r||!!i.url),this.logsEnabled=!n;let a=e.serviceName??t.BROWSE_TOOL_OTEL_SERVICE_NAME??t.OTEL_SERVICE_NAME??Mt,o=e.serviceVersion??t.npm_package_version,s={"service.name":a};o&&(s[`service.version`]=o);let c=(0,E.resourceFromAttributes)(s);if(this.tracesEnabled){let t=e.traceExporter??new ee.OTLPTraceExporter(i);this.tracerProvider=new D.NodeTracerProvider({resource:c,spanProcessors:[e.traceExporter?new D.SimpleSpanProcessor(t):new D.BatchSpanProcessor(t)]})}this.tracer=this.tracerProvider?.getTracer(a,o)??T.trace.getTracer(a,o),this.logSinkReady=this.logsEnabled?(0,w.createNodeTelemetry)({env:t,serviceName:a,serviceVersion:o,workspaceRoot:Ht(),enableTraces:!1,enableLogs:!0}).catch(()=>null):Promise.resolve(null)}isEnabled(){return this.tracesEnabled||this.logsEnabled}getActiveTraceContext(){let e=this.contextStore.getStore();if(!e)return{};let t=T.trace.getSpan(e);if(!t)return{};let n=t.spanContext();return{traceId:n.traceId,spanId:n.spanId}}async runInSpan(e,t,n){if(!this.tracesEnabled)return await n(void 0);let r=this.contextStore.getStore()??T.context.active(),i=this.tracer.startSpan(e,{kind:t.kind??T.SpanKind.INTERNAL,attributes:Lt(t.attributes)},r),a=T.trace.setSpan(r,i);return await this.contextStore.run(a,async()=>{try{return await n(i)}catch(e){throw i.recordException(e instanceof Error?e:Error(Yt(e))),i.setStatus({code:T.SpanStatusCode.ERROR,message:Yt(e)}),e}finally{i.end()}})}log(e,t,n={}){this.logsEnabled&&this.logSinkReady.then(r=>{if(!r)return;let i=n.context??this.contextStore.getStore()??T.context.active(),a=Xt(n.exception);r.logger[e](t,{attributes:Lt({...n.attributes,...a}),context:i,exception:n.exception})})}async forceFlush(){let e=await this.logSinkReady;await Promise.allSettled([this.tracerProvider?.forceFlush(),e?.flush()])}async shutdown(){let e=await this.logSinkReady;await Promise.allSettled([this.tracerProvider?.shutdown(),e?.shutdown()])}};z=N([(0,d.injectable)(),j(`design:paramtypes`,[Object])],z);let Zt=class{pendingTasks=new Map;taskQueue=[];lastPollAt;lastResultAt;maxQueueSize=100;defaultTimeoutMs=P;constructor(e,t=new z){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}}};Zt=N([(0,d.injectable)(),M(0,(0,d.inject)(A.WebSocketHub)),M(0,(0,d.optional)()),M(1,(0,d.inject)(A.TelemetryService)),M(1,(0,d.optional)()),j(`design:paramtypes`,[Object,Object])],Zt);const Qt=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(`.`)),$t=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 en=class{constructor(e){this.taskQueue=e}isToolSupported(e){return Qt.has(e)}isToolNotApplicable(e){return $t.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(Qt)}getNotApplicableTools(){return Array.from($t)}};en=N([(0,d.injectable)(),M(0,(0,d.inject)(A.ExtensionTaskQueue)),j(`design:paramtypes`,[Object])],en);const tn=l.DEFAULT_PORT_RANGE.min,nn=`PLAYWRIGHT_PORT`;function B(){return process.env.PLAYWRIGHT_HOST?.trim()||`localhost`}function rn(){let e=process.env[nn];if(!e)return tn;let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0||t>65535)throw Error(`Invalid ${nn} value: ${e}`);return t}function V(e=void 0,t){let n=typeof e==`string`?e:e?.host??B(),r=e&&typeof e!=`string`?e.port:t??rn();return`http://${n===`0.0.0.0`?`127.0.0.1`:n===`::`?`::1`:n}:${r}`}let an=class{baseUrl;defaultTimeout=P;constructor(){this.baseUrl=V()}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}}};an=N([(0,d.injectable)(),j(`design:paramtypes`,[])],an);let H=class{defaultTimeout=5e3;async check(e,t=this.defaultTimeout){try{let n=`${V(B(),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)}}}};H=N([(0,d.injectable)()],H);var on;const sn=(0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),cn=f.dirname(sn),ln=[`pnpm-workspace.yaml`,`nx.json`,`.git`],U=`browse-tool-http`,un=`tool`,dn=`http-serve`,fn=`ESRCH`,pn=`node`,mn=`cli.mjs`,hn=`cli.ts`,W=`[HttpServerManager]`;var gn=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`}},G=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`}},_n=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 K(e){try{return await m.access(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}const vn=(0,O.promisify)(g.execFile);async function yn(e){try{let{stdout:t}=await vn(`git`,[`rev-parse`,`--git-common-dir`],{cwd:e}),n=t.trim(),r=f.isAbsolute(n)?n:f.join(e,n),i=f.dirname(r);return i===e?null:i}catch{return null}}async function bn(e=process.cwd()){let t=f.resolve(e);for(;;){for(let e of ln)if(await K(f.join(t,e)))return t;let e=f.dirname(t);if(e===t)return process.cwd();t=e}}let xn=class{repositoryPath;environment;host;constructor(e,t,n=new u.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH)){this.healthCheck=e,this.portRegistry=t,this.processRegistry=n,this.environment=process.env.NODE_ENV||`development`,this.host=B()}async getRepositoryPath(){return this.repositoryPath||=await bn(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:U,serviceType:un,environment:this.environment},n=await this.portRegistry.listAllocations({...t,repositoryPath:e});if(n.length>0)return n;let r=await yn(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:U,serviceType:`service`,environment:e.environment,pid:e.pid,kill:!1,releasePort:!1});!t.success&&!t.error?.includes(`No matching process entry`)&&console.error(`${W} Failed to release process entry for port ${e.port}: ${t.error}`)}let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:U,serviceType:un,environment:e.environment,pid:e.pid});!t.success&&!t.error?.includes(`No matching registry entry`)&&console.error(`${W} 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===U)return{entry:t,browserCount:e.browserCount}}catch(e){console.error(`${W} 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===U)return{entry:t,browserCount:n.browserCount}}catch(t){console.error(`${W} 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(`${W} Health check threw for port ${n.port}, skipping cleanup: ${e instanceof Error?e.message:String(e)}`);continue}e.healthy&&e.serviceName===U||(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(`${W} ${r}`),t.push(r)}t.length>0&&console.error(`${W} ${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:U,serviceType:un,environment:this.environment,host:this.host,preferredPort:e,portRange:t.exactPort?{min:e,max:e}:void 0});if(!r.success||!r.port)throw new gn(`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 gn(`Requested port ${e} is unavailable; next available port is ${t}`)}async resolveCliCommand(e){let t=f.resolve(cn,mn),n=f.resolve(cn,`..`,hn),r=[`--port`,e.toString(),`--host`,this.host];if(await K(t))return[pn,[t,dn,...r]];if(await K(n))return[`bun`,[`run`,n,dn,...r]];let i=f.resolve(cn,`..`,`..`),a=f.join(i,`dist`,mn),o=f.join(i,`src`,hn);if(await K(a))return[pn,[a,dn,...r]];if(await K(o))return[`bun`,[`run`,o,dn,...r]];throw new G(`Cannot find CLI at ${t}, ${n}, ${a}, or ${o}`)}async startHttpServer(e){let[t,n]=await this.resolveCliCommand(e),r=(0,g.spawn)(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 G(`Failed to spawn HTTP server process: ${i.message}`,{cause:i});r.unref();let a=r.pid;if(!a)throw new G(`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)===fn){console.error(`${W} 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)===fn){console.error(`${W} 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(`${W} 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!==U)return await this.cleanupFailedSpawn(n,t),{success:!1,reason:`service_name_mismatch`,port:t,error:`Expected ${U}, got ${r.serviceName}`};let i=await this.findRegistrationByPort(t);return!i||i.pid!==n?(console.error(`${W} 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(`${W} Server self-registered on port ${t} (pid: ${i.pid})`),{success:!0,port:t,pid:n,browserCount:r.browserCount})}async ensureRunning(e=rn(),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 G(`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(`${W} Spawn attempt ${e+1} failed: ${t.reason} on port ${t.port}`),r=t.port+1}let i=new G(`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(`${W} ${r.message}`),t.push(r)}if(t.length>0){let n=t.map(e=>e.message);throw new _n(`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)}}};xn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.HttpServerHealthCheck)),M(1,(0,d.inject)(A.PortRegistryService)),M(2,(0,d.inject)(A.ProcessRegistryService)),j(`design:paramtypes`,[Object,Object,typeof(on=u.ProcessRegistryService!==void 0&&u.ProcessRegistryService)==`function`?on:Object])],xn);const Sn=900*1e3,Cn=1800*1e3,wn=`PLAYWRIGHT_BROWSER_IDLE_TIMEOUT_MINUTES`;function Tn(){let e=process.env[wn];if(!e)return Cn;let t=Number.parseFloat(e);return!Number.isFinite(t)||t<=0?Cn:Math.round(t*60*1e3)}let En=class{intervalId=null;browserIdleTimeoutMs=Tn();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: ${Sn/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<Sn)){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?Sn: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)}}}}};En=N([(0,d.injectable)(),M(0,(0,d.inject)(A.BrowserService)),M(1,(0,d.inject)(A.PageRegistry)),j(`design:paramtypes`,[Object,Object])],En);let q=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.DEFAULT_PORT_RANGE.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.DEFAULT_PORT_RANGE.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}}};q=N([(0,d.injectable)(),M(0,(0,d.inject)(A.PortRegistryService)),j(`design:paramtypes`,[Object])],q);let Dn=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()}};Dn=N([(0,d.injectable)()],Dn);let On=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)}};On=N([(0,d.injectable)()],On);let kn=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??at(),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:at(),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}};kn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.WebSocketHub)),M(0,(0,d.optional)()),j(`design:paramtypes`,[Object])],kn);let An=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)}};An=N([(0,d.injectable)()],An);let jn=class{profilesDir;constructor(){this.profilesDir=process.env.PLAYWRIGHT_PROFILES_DIR||(0,f.join)((0,h.homedir)(),`.browse-tool`,`profiles`)}getProfilesDir(){return this.profilesDir}getProfileDir(e){return(0,f.join)(this.getProfilesDir(),e)}getProfilePath(e){return(0,f.join)(this.getProfileDir(e),`profile.json`)}getStorageStatePath(e){return(0,f.join)(this.getProfileDir(e),`storage-state.json`)}async ensureProfilesDir(){let e=this.getProfilesDir();(0,p.existsSync)(e)||await(0,m.mkdir)(e,{recursive:!0})}async list(){await this.ensureProfilesDir();let e=await(0,m.readdir)(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(!(0,p.existsSync)(t))return null;let n=await(0,m.readFile)(t,`utf-8`);return JSON.parse(n)}async create(e){if(await this.get(e.name))throw Error(`Profile "${e.name}" already exists`);await(0,m.mkdir)(this.getProfileDir(e.name),{recursive:!0});let t=new Date().toISOString(),n={...e,createdAt:t,updatedAt:t};return await(0,m.writeFile)(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(0,m.writeFile)(this.getProfilePath(e),JSON.stringify(r,null,2)),r}async delete(e){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await(0,m.rm)(this.getProfileDir(e),{recursive:!0})}async saveStorageState(e,t){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await(0,m.writeFile)(this.getStorageStatePath(e),JSON.stringify(t,null,2))}async loadStorageState(e){let t=this.getStorageStatePath(e);if(!(0,p.existsSync)(t))return null;let n=await(0,m.readFile)(t,`utf-8`);return JSON.parse(n)}};jn=N([(0,d.injectable)(),j(`design:paramtypes`,[])],jn);const Mn=`session`,Nn=/\$\{([A-Z0-9_]+)\}/gi;function Pn(e){return e.PLAYWRIGHT_PROFILES_DIR||f.default.join((0,h.homedir)(),`.browse-tool`,`profiles`)}function Fn(e){let t=e.env??process.env,n=e.proxyConfigDir??t.BROWSE_TOOL_PROXY_CONFIG_DIR;if(n)return f.default.resolve(n);let r=e.profilesDir??Pn(t);return f.default.join(f.default.dirname(f.default.resolve(r)),`proxy-config`)}function In(e){if(!e||e.includes(`/`)||e.includes(`\\`)||e===`.`||e===`..`)throw Error(`Invalid proxy config name: ${e}`)}function Ln(e,t){In(t);let n=f.default.resolve(e),r=f.default.resolve(n,`${t}.yaml`),i=f.default.relative(n,r);if(i.startsWith(`..`)||f.default.isAbsolute(i))throw Error(`Proxy config path escapes configured directory: ${t}`);return r}function Rn(e,t,n){return e.replace(Nn,(e,r)=>{let i=t[r];if(i===void 0)throw Error(`Missing environment variable "${r}" referenced by proxy config ${n}`);return i})}function zn(e,t,n){let r=Ln(e,t);if(!(0,p.existsSync)(r))return;let i=(0,te.parse)((0,p.readFileSync)(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:Bn(a,`server`,r,n)},s=Vn(a,`username`,r,n),c=Vn(a,`password`,r,n),l=Vn(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 Bn(e,t,n,r){let i=Vn(e,t,n,r);if(!i)throw Error(`Proxy config ${n} must define a non-empty "${t}" string`);return i}function Vn(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 Rn(i,r,n)}}function Hn(e){return!!e&&e!==!0&&typeof e==`object`}let J=class{getProxyConfigDir(e={}){return Fn(e)}resolve(e){if(Hn(e.proxy))return e.proxy;let t=e.env??process.env,n=Fn(e);if(e.profileName){let r=zn(n,e.profileName,t);if(r)return r}if(e.proxy===!0){let e=zn(n,Mn,t);if(!e)throw Error(`Proxy requested but ${Ln(n,Mn)} was not found`);return e}return zn(n,`default`,t)}};J=N([(0,d.injectable)()],J);function Un(e){return new J().resolve(e)}let Wn=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(V(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}};Wn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.HttpBrowserClient)),j(`design:paramtypes`,[Object])],Wn);let Gn=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}};Gn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),j(`design:paramtypes`,[Object])],Gn);const Y=`[SpecBundler]`,Kn=RegExp(`^@playwright\\/test$`),qn=`__PLAYWRIGHT_MCP_STUB_PATH__`,X={OUTDIR_NAME:`browse-tool-bundles`,PLUGIN_NAME:`playwright-test-alias`,EXTERNALS:[`playwright`,`playwright/test`,`playwright-core`]},Jn={BUILD_FAILED:`SPEC_BUNDLER_BUILD_FAILED`,NO_OUTPUT:`SPEC_BUNDLER_NO_OUTPUT`,IO_FAILED:`SPEC_BUNDLER_IO_FAILED`},Z={UNEXPECTED_BUILD:`Unexpected build exception`,IO_MKDIR:`Failed to create output directory`,CLEANUP_FAILED:`Failed to clean up temporary file`};var Q=class extends Error{code=Jn.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 "${(0,f.basename)(e)}": ${t}`,n),this.specPath=e,this.buildErrors=t,this.name=`SpecBundlerBuildError`}},Yn=class extends Error{code=Jn.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 Xn=class{stubPath;nodeModulesPath;constructor(){let e=(0,f.dirname)(require(`url`).pathToFileURL(__filename).href.replace(`file://`,``));this.stubPath=(0,f.join)(e,`stubs`,`playwright-test.mjs`),this.nodeModulesPath=this.findNodeModules(e)}findNodeModules(e){let t=e,n=(0,f.resolve)(`/`);for(;t!==n;){let e=(0,f.join)(t,`node_modules`);if((0,p.existsSync)((0,f.join)(e,`playwright`)))return e;t=(0,f.dirname)(t)}for(t=e;t!==n;){let e=(0,f.join)(t,`node_modules`);if((0,p.existsSync)(e))return e;t=(0,f.dirname)(t)}return(0,f.join)(e,`node_modules`)}async bundle(e){let t=(0,f.join)((0,h.tmpdir)(),X.OUTDIR_NAME);try{await(0,m.mkdir)(t,{recursive:!0})}catch(e){throw console.error(`${Y} ${Z.IO_MKDIR} "${t}":`,e),new Yn(Z.IO_MKDIR,t,{cause:e})}let n=(0,f.join)(t,`node_modules`);if(!(0,p.existsSync)(n))try{await(0,m.symlink)(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleWithEsbuild(e,t)}async bundleWithEsbuild(e,t){let n=(0,f.basename)(e).replace(/\.[^.]+$/,``),r=this.stubPath;try{let i=await(0,ne.build)({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:[...X.EXTERNALS],define:{[qn]:JSON.stringify(r)},plugins:[{name:X.PLUGIN_NAME,setup(e){e.onResolve({filter:Kn},()=>({path:r}))}}],write:!0,logLevel:`silent`});if(i.errors.length>0){let t=i.errors.map(e=>e.text).join(`
48
+ `}};let At=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}};At=N([(0,d.injectable)()],At);let jt=class{constructor(e,t,n){this.bundler=e,this.pageProxy=t,this.sessionRegistry=n}async loadSpec(e){let t=await this.bundler.bundle(e),n=process,r=n.__pw_initiator__;try{return c.c(e),n.__pw_initiator__=void 0,await import(`${t.outputPath}?t=${Date.now()}`),c.s()}catch(t){throw c.p(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{n.__pw_initiator__=r,await t.cleanup()}}async executeSpec(e){let{specPath:t,sessionId:n,testFilter:r,specArgs:i,onHandoff:a}=e,o=Date.now(),s=[],l=0,u=0,d=!1,f=new Set,p=new Set;this.sessionRegistry.setActiveSpec(n,t);try{let e=await this.loadSpec(t),m=r?this.filterTests(e.allTests,r):e.allTests,h={page:this.pageProxy,requestHandoff:async e=>{if(!this.sessionRegistry.getSession(n))throw Error(`Session ${n} not found`);let t=await this.pageProxy.getSnapshot(),r=this.pageProxy.url();try{r=await this.pageProxy.currentUrlAsync()}catch{}this.sessionRegistry.requestHandoff({sessionId:n,reason:e,pageState:{url:r,snapshot:t}}),d=!0,a&&await a(n)},specArgs:i};for(let e of m){let{runtimeUse:t,unsupportedKeys:r,fixtureOverrides:i}=c.h(e.config.use);r.length>0&&f.add(`test.use() ignores unsupported fixture keys in extension mode: ${r.join(`, `)}`),Object.keys(t).length>0&&f.add(`test.use() runtime browser config is not applied in extension mode: ${Object.keys(t).join(`, `)}`);let a=e.serialScopeId,o=a!==void 0&&p.has(a);if(e.skip||o){s.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:0}),l++;continue}let d=this.sessionRegistry.getSession(n);if(d?.handoffRequested)break;let m=Date.now();try{await e.fn({...h,...i}),s.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);s.push({title:e.title,fullTitle:e.fullTitle,passed:!1,error:n,duration:Date.now()-m}),u++,a!==void 0&&p.add(a)}}return{specPath:t,totalTests:m.length,passed:l,failed:u,testResults:s,success:u===0,duration:Date.now()-o,handoffOccurred:d,sessionId:n,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()-o}],success:!1,duration:Date.now()-o,handoffOccurred:!1,sessionId:n,warnings:f.size>0?[...f]:void 0}}finally{this.sessionRegistry.setActiveSpec(n,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}};jt=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),M(1,(0,d.inject)(A.ExtensionPageProxy)),M(2,(0,d.inject)(A.ExtensionSessionRegistry)),j(`design:paramtypes`,[Object,Object,Object])],jt);const Mt=`browse-tool-http`,Nt=[`pnpm-workspace.yaml`,`nx.json`,`.git`],Pt=new Set([`127.0.0.1`,`localhost`,`::1`,`0.0.0.0`]);function Ft(e){return e===`1`||e===`true`}function It(e){return Array.isArray(e)?JSON.stringify(e):typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:JSON.stringify(e)}function Lt(e){if(!e)return;let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=It(r));return Object.keys(t).length>0?t:void 0}function Rt(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 zt(e){if(!e)return;let t=Number(e);return Number.isFinite(t)&&t>0?t:void 0}function Bt(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 Vt(e,t){return(t===`traces`?e.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:e.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)||(e.OTEL_EXPORTER_OTLP_ENDPOINT?Bt(e.OTEL_EXPORTER_OTLP_ENDPOINT,t):void 0)}function Ht(e){return Pt.has(e)}function Ut(e=process.cwd()){let t=f.default.resolve(e);for(;;){for(let e of Nt)if((0,p.existsSync)(f.default.join(t,e)))return t;let e=f.default.dirname(t);if(e===t)return process.cwd();t=e}}function Wt(e,t){try{let n=new URL(e),r=new URL(t);return!Ht(n.hostname)||Ht(r.hostname)||(n.hostname=r.hostname),n.toString()}catch{return e}}async function Gt(e,t,n){let r=await(0,w.resolveNodeTelemetryEndpoints)({env:e,workspaceRoot:Ut(),healthCheck:n?.healthCheck});return t===`traces`?r.tracesEndpoint:r.logsEndpoint}async function Kt(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 Wt(i,n);let a=e.BROWSE_TOOL_OTEL_BROWSER_ENDPOINT;if(a)return Wt(Bt(a,t),n);let o=await Gt(e,t,r);return o?Wt(o,n):void 0}async function qt(e,t,n){let r=e.BROWSE_TOOL_OTEL_BROWSER_SERVICE_NAME??`${e.OTEL_SERVICE_NAME??Mt}-extension`,[i,a]=await Promise.all([Kt(e,`traces`,t,n),Kt(e,`logs`,t,n)]);return{enabled:!!(i||a),tracesEndpoint:i,logsEndpoint:a,serviceName:r}}function Jt(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:Vt(e,t),headers:Rt(n??e.OTEL_EXPORTER_OTLP_HEADERS),timeoutMillis:zt(r??e.OTEL_EXPORTER_OTLP_TIMEOUT)}}function Yt(e){return e instanceof Error?e.message:String(e)}function Xt(e){if(e instanceof Error)return Lt({"exception.type":e.name,"exception.message":e.message,"exception.stacktrace":e.stack})}let z=class{contextStore=new x.AsyncLocalStorage;tracesEnabled;logsEnabled;tracerProvider;tracer;logSinkReady;delegateReady;delegateHandle=null;constructor(e={}){let t=e.env??process.env,n=Ft(t.OTEL_SDK_DISABLED),r=Ft(t.BROWSE_TOOL_OTEL_ENABLED),i=Jt(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??Mt,s=e.serviceVersion??t.npm_package_version,c={"service.name":o};s&&(c[`service.version`]=s);let l=(0,E.resourceFromAttributes)(c);if(this.tracesEnabled&&a){let t=e.traceExporter??new ee.OTLPTraceExporter(i);this.tracerProvider=new D.NodeTracerProvider({resource:l,spanProcessors:[e.traceExporter?new D.SimpleSpanProcessor(t):new D.BatchSpanProcessor(t)]})}this.tracer=this.tracerProvider?.getTracer(o,s)??T.trace.getTracer(o,s),this.delegateReady=!n&&!a?(0,w.createNodeTelemetry)({env:t,serviceName:o,serviceVersion:s,workspaceRoot:Ut(),enableTraces:!0,enableLogs:!0}).then(e=>(this.delegateHandle=e,e)).catch(()=>null):Promise.resolve(null),this.logSinkReady=this.logsEnabled&&a?(0,w.createNodeTelemetry)({env:t,serviceName:o,serviceVersion:s,workspaceRoot:Ut(),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=T.trace.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()??T.context.active(),a=this.tracer.startSpan(e,{kind:t.kind??T.SpanKind.INTERNAL,attributes:Lt(t.attributes)},i),o=T.trace.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(Yt(e))),a.setStatus({code:T.SpanStatusCode.ERROR,message:Yt(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()??T.context.active(),a=Xt(n.exception);r.logger[e](t,{attributes:Lt({...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()])}};z=N([(0,d.injectable)(),j(`design:paramtypes`,[Object])],z);let Zt=class{pendingTasks=new Map;taskQueue=[];lastPollAt;lastResultAt;maxQueueSize=100;defaultTimeoutMs=P;constructor(e,t=new z){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}}};Zt=N([(0,d.injectable)(),M(0,(0,d.inject)(A.WebSocketHub)),M(0,(0,d.optional)()),M(1,(0,d.inject)(A.TelemetryService)),M(1,(0,d.optional)()),j(`design:paramtypes`,[Object,Object])],Zt);const Qt=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(`.`)),$t=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 en=class{constructor(e){this.taskQueue=e}isToolSupported(e){return Qt.has(e)}isToolNotApplicable(e){return $t.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(Qt)}getNotApplicableTools(){return Array.from($t)}};en=N([(0,d.injectable)(),M(0,(0,d.inject)(A.ExtensionTaskQueue)),j(`design:paramtypes`,[Object])],en);const tn=l.DEFAULT_PORT_RANGE.min,nn=`PLAYWRIGHT_PORT`;function B(){return process.env.PLAYWRIGHT_HOST?.trim()||`localhost`}function rn(){let e=process.env[nn];if(!e)return tn;let t=Number.parseInt(e,10);if(Number.isNaN(t)||t<=0||t>65535)throw Error(`Invalid ${nn} value: ${e}`);return t}function V(e=void 0,t){let n=typeof e==`string`?e:e?.host??B(),r=e&&typeof e!=`string`?e.port:t??rn();return`http://${n===`0.0.0.0`?`127.0.0.1`:n===`::`?`::1`:n}:${r}`}let an=class{baseUrl;defaultTimeout=P;constructor(){this.baseUrl=V()}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}}};an=N([(0,d.injectable)(),j(`design:paramtypes`,[])],an);let H=class{defaultTimeout=5e3;async check(e,t=this.defaultTimeout){try{let n=`${V(B(),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)}}}};H=N([(0,d.injectable)()],H);var on;const sn=(0,_.fileURLToPath)(require(`url`).pathToFileURL(__filename).href),cn=f.dirname(sn),ln=[`pnpm-workspace.yaml`,`nx.json`,`.git`],U=`browse-tool-http`,un=`tool`,dn=`http-serve`,fn=`ESRCH`,pn=`node`,mn=`cli.mjs`,hn=`cli.ts`,W=`[HttpServerManager]`;var gn=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`}},G=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`}},_n=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 K(e){try{return await m.access(e),!0}catch(e){if(e instanceof Error&&`code`in e&&e.code===`ENOENT`)return!1;throw e}}const vn=(0,O.promisify)(g.execFile);async function yn(e){try{let{stdout:t}=await vn(`git`,[`rev-parse`,`--git-common-dir`],{cwd:e}),n=t.trim(),r=f.isAbsolute(n)?n:f.join(e,n),i=f.dirname(r);return i===e?null:i}catch{return null}}async function bn(e=process.cwd()){let t=f.resolve(e);for(;;){for(let e of ln)if(await K(f.join(t,e)))return t;let e=f.dirname(t);if(e===t)return process.cwd();t=e}}let xn=class{repositoryPath;environment;host;constructor(e,t,n=new u.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH)){this.healthCheck=e,this.portRegistry=t,this.processRegistry=n,this.environment=process.env.NODE_ENV||`development`,this.host=B()}async getRepositoryPath(){return this.repositoryPath||=await bn(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:U,serviceType:un,environment:this.environment},n=await this.portRegistry.listAllocations({...t,repositoryPath:e});if(n.length>0)return n;let r=await yn(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:U,serviceType:`service`,environment:e.environment,pid:e.pid,kill:!1,releasePort:!1});!t.success&&!t.error?.includes(`No matching process entry`)&&console.error(`${W} Failed to release process entry for port ${e.port}: ${t.error}`)}let t=await this.portRegistry.releasePort({repositoryPath:e.repositoryPath,serviceName:U,serviceType:un,environment:e.environment,pid:e.pid});!t.success&&!t.error?.includes(`No matching registry entry`)&&console.error(`${W} 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===U)return{entry:t,browserCount:e.browserCount}}catch(e){console.error(`${W} 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===U)return{entry:t,browserCount:n.browserCount}}catch(t){console.error(`${W} 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(`${W} Health check threw for port ${n.port}, skipping cleanup: ${e instanceof Error?e.message:String(e)}`);continue}e.healthy&&e.serviceName===U||(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(`${W} ${r}`),t.push(r)}t.length>0&&console.error(`${W} ${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:U,serviceType:un,environment:this.environment,host:this.host,preferredPort:e,portRange:t.exactPort?{min:e,max:e}:void 0});if(!r.success||!r.port)throw new gn(`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 gn(`Requested port ${e} is unavailable; next available port is ${t}`)}async resolveCliCommand(e){let t=f.resolve(cn,mn),n=f.resolve(cn,`..`,hn),r=[`--port`,e.toString(),`--host`,this.host];if(await K(t))return[pn,[t,dn,...r]];if(await K(n))return[`bun`,[`run`,n,dn,...r]];let i=f.resolve(cn,`..`,`..`),a=f.join(i,`dist`,mn),o=f.join(i,`src`,hn);if(await K(a))return[pn,[a,dn,...r]];if(await K(o))return[`bun`,[`run`,o,dn,...r]];throw new G(`Cannot find CLI at ${t}, ${n}, ${a}, or ${o}`)}async startHttpServer(e){let[t,n]=await this.resolveCliCommand(e),r=(0,g.spawn)(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 G(`Failed to spawn HTTP server process: ${i.message}`,{cause:i});r.unref();let a=r.pid;if(!a)throw new G(`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)===fn){console.error(`${W} 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)===fn){console.error(`${W} 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(`${W} 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!==U)return await this.cleanupFailedSpawn(n,t),{success:!1,reason:`service_name_mismatch`,port:t,error:`Expected ${U}, got ${r.serviceName}`};let i=await this.findRegistrationByPort(t);return!i||i.pid!==n?(console.error(`${W} 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(`${W} Server self-registered on port ${t} (pid: ${i.pid})`),{success:!0,port:t,pid:n,browserCount:r.browserCount})}async ensureRunning(e=rn(),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 G(`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(`${W} Spawn attempt ${e+1} failed: ${t.reason} on port ${t.port}`),r=t.port+1}let i=new G(`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(`${W} ${r.message}`),t.push(r)}if(t.length>0){let n=t.map(e=>e.message);throw new _n(`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)}}};xn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.HttpServerHealthCheck)),M(1,(0,d.inject)(A.PortRegistryService)),M(2,(0,d.inject)(A.ProcessRegistryService)),j(`design:paramtypes`,[Object,Object,typeof(on=u.ProcessRegistryService!==void 0&&u.ProcessRegistryService)==`function`?on:Object])],xn);const Sn=900*1e3,Cn=1800*1e3,wn=`PLAYWRIGHT_BROWSER_IDLE_TIMEOUT_MINUTES`;function Tn(){let e=process.env[wn];if(!e)return Cn;let t=Number.parseFloat(e);return!Number.isFinite(t)||t<=0?Cn:Math.round(t*60*1e3)}let En=class{intervalId=null;browserIdleTimeoutMs=Tn();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: ${Sn/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<Sn)){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?Sn: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)}}}}};En=N([(0,d.injectable)(),M(0,(0,d.inject)(A.BrowserService)),M(1,(0,d.inject)(A.PageRegistry)),j(`design:paramtypes`,[Object,Object])],En);let q=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.DEFAULT_PORT_RANGE.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.DEFAULT_PORT_RANGE.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}}};q=N([(0,d.injectable)(),M(0,(0,d.inject)(A.PortRegistryService)),j(`design:paramtypes`,[Object])],q);let Dn=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()}};Dn=N([(0,d.injectable)()],Dn);let On=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)}};On=N([(0,d.injectable)()],On);let kn=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??at(),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:at(),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}};kn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.WebSocketHub)),M(0,(0,d.optional)()),j(`design:paramtypes`,[Object])],kn);let An=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)}};An=N([(0,d.injectable)()],An);let jn=class{profilesDir;constructor(){this.profilesDir=process.env.PLAYWRIGHT_PROFILES_DIR||(0,f.join)((0,h.homedir)(),`.browse-tool`,`profiles`)}getProfilesDir(){return this.profilesDir}getProfileDir(e){return(0,f.join)(this.getProfilesDir(),e)}getProfilePath(e){return(0,f.join)(this.getProfileDir(e),`profile.json`)}getStorageStatePath(e){return(0,f.join)(this.getProfileDir(e),`storage-state.json`)}async ensureProfilesDir(){let e=this.getProfilesDir();(0,p.existsSync)(e)||await(0,m.mkdir)(e,{recursive:!0})}async list(){await this.ensureProfilesDir();let e=await(0,m.readdir)(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(!(0,p.existsSync)(t))return null;let n=await(0,m.readFile)(t,`utf-8`);return JSON.parse(n)}async create(e){if(await this.get(e.name))throw Error(`Profile "${e.name}" already exists`);await(0,m.mkdir)(this.getProfileDir(e.name),{recursive:!0});let t=new Date().toISOString(),n={...e,createdAt:t,updatedAt:t};return await(0,m.writeFile)(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(0,m.writeFile)(this.getProfilePath(e),JSON.stringify(r,null,2)),r}async delete(e){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await(0,m.rm)(this.getProfileDir(e),{recursive:!0})}async saveStorageState(e,t){if(!await this.get(e))throw Error(`Profile "${e}" not found`);await(0,m.writeFile)(this.getStorageStatePath(e),JSON.stringify(t,null,2))}async loadStorageState(e){let t=this.getStorageStatePath(e);if(!(0,p.existsSync)(t))return null;let n=await(0,m.readFile)(t,`utf-8`);return JSON.parse(n)}};jn=N([(0,d.injectable)(),j(`design:paramtypes`,[])],jn);const Mn=`session`,Nn=/\$\{([A-Z0-9_]+)\}/gi;function Pn(e){return e.PLAYWRIGHT_PROFILES_DIR||f.default.join((0,h.homedir)(),`.browse-tool`,`profiles`)}function Fn(e){let t=e.env??process.env,n=e.proxyConfigDir??t.BROWSE_TOOL_PROXY_CONFIG_DIR;if(n)return f.default.resolve(n);let r=e.profilesDir??Pn(t);return f.default.join(f.default.dirname(f.default.resolve(r)),`proxy-config`)}function In(e){if(!e||e.includes(`/`)||e.includes(`\\`)||e===`.`||e===`..`)throw Error(`Invalid proxy config name: ${e}`)}function Ln(e,t){In(t);let n=f.default.resolve(e),r=f.default.resolve(n,`${t}.yaml`),i=f.default.relative(n,r);if(i.startsWith(`..`)||f.default.isAbsolute(i))throw Error(`Proxy config path escapes configured directory: ${t}`);return r}function Rn(e,t,n){return e.replace(Nn,(e,r)=>{let i=t[r];if(i===void 0)throw Error(`Missing environment variable "${r}" referenced by proxy config ${n}`);return i})}function zn(e,t,n){let r=Ln(e,t);if(!(0,p.existsSync)(r))return;let i=(0,te.parse)((0,p.readFileSync)(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:Bn(a,`server`,r,n)},s=Vn(a,`username`,r,n),c=Vn(a,`password`,r,n),l=Vn(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 Bn(e,t,n,r){let i=Vn(e,t,n,r);if(!i)throw Error(`Proxy config ${n} must define a non-empty "${t}" string`);return i}function Vn(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 Rn(i,r,n)}}function Hn(e){return!!e&&e!==!0&&typeof e==`object`}let J=class{getProxyConfigDir(e={}){return Fn(e)}resolve(e){if(Hn(e.proxy))return e.proxy;let t=e.env??process.env,n=Fn(e);if(e.profileName){let r=zn(n,e.profileName,t);if(r)return r}if(e.proxy===!0){let e=zn(n,Mn,t);if(!e)throw Error(`Proxy requested but ${Ln(n,Mn)} was not found`);return e}return zn(n,`default`,t)}};J=N([(0,d.injectable)()],J);function Un(e){return new J().resolve(e)}let Wn=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(V(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}};Wn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.HttpBrowserClient)),j(`design:paramtypes`,[Object])],Wn);let Gn=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}};Gn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),j(`design:paramtypes`,[Object])],Gn);const Y=`[SpecBundler]`,Kn=RegExp(`^@playwright\\/test$`),qn=`__PLAYWRIGHT_MCP_STUB_PATH__`,X={OUTDIR_NAME:`browse-tool-bundles`,PLUGIN_NAME:`playwright-test-alias`,EXTERNALS:[`playwright`,`playwright/test`,`playwright-core`]},Jn={BUILD_FAILED:`SPEC_BUNDLER_BUILD_FAILED`,NO_OUTPUT:`SPEC_BUNDLER_NO_OUTPUT`,IO_FAILED:`SPEC_BUNDLER_IO_FAILED`},Z={UNEXPECTED_BUILD:`Unexpected build exception`,IO_MKDIR:`Failed to create output directory`,CLEANUP_FAILED:`Failed to clean up temporary file`};var Q=class extends Error{code=Jn.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 "${(0,f.basename)(e)}": ${t}`,n),this.specPath=e,this.buildErrors=t,this.name=`SpecBundlerBuildError`}},Yn=class extends Error{code=Jn.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 Xn=class{stubPath;nodeModulesPath;constructor(){let e=(0,f.dirname)(require(`url`).pathToFileURL(__filename).href.replace(`file://`,``));this.stubPath=(0,f.join)(e,`stubs`,`playwright-test.mjs`),this.nodeModulesPath=this.findNodeModules(e)}findNodeModules(e){let t=e,n=(0,f.resolve)(`/`);for(;t!==n;){let e=(0,f.join)(t,`node_modules`);if((0,p.existsSync)((0,f.join)(e,`playwright`)))return e;t=(0,f.dirname)(t)}for(t=e;t!==n;){let e=(0,f.join)(t,`node_modules`);if((0,p.existsSync)(e))return e;t=(0,f.dirname)(t)}return(0,f.join)(e,`node_modules`)}async bundle(e){let t=(0,f.join)((0,h.tmpdir)(),X.OUTDIR_NAME);try{await(0,m.mkdir)(t,{recursive:!0})}catch(e){throw console.error(`${Y} ${Z.IO_MKDIR} "${t}":`,e),new Yn(Z.IO_MKDIR,t,{cause:e})}let n=(0,f.join)(t,`node_modules`);if(!(0,p.existsSync)(n))try{await(0,m.symlink)(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleWithEsbuild(e,t)}async bundleWithEsbuild(e,t){let n=(0,f.basename)(e).replace(/\.[^.]+$/,``),r=this.stubPath;try{let i=await(0,ne.build)({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:[...X.EXTERNALS],define:{[qn]:JSON.stringify(r)},plugins:[{name:X.PLUGIN_NAME,setup(e){e.onResolve({filter:Kn},()=>({path:r}))}}],write:!0,logLevel:`silent`});if(i.errors.length>0){let t=i.errors.map(e=>e.text).join(`
49
49
  `);throw console.error(`${Y} Build failed for "${e}": ${t}`),new Q(e,t,{cause:Error(t)})}let a=(0,f.join)(t,`${n}.js`);return{outputPath:a,cleanup:()=>this.safeUnlink(a)}}catch(t){if(t instanceof Q)throw t;let n=t instanceof Error?t.message:Z.UNEXPECTED_BUILD;throw console.error(`${Y} Build failed for "${e}":`,t),new Q(e,n,{cause:t})}}async bundleWithHooks(e){let t=(0,f.join)((0,h.tmpdir)(),X.OUTDIR_NAME);try{await(0,m.mkdir)(t,{recursive:!0})}catch(e){throw console.error(`${Y} ${Z.IO_MKDIR} "${t}":`,e),new Yn(Z.IO_MKDIR,t,{cause:e})}let n=(0,f.join)(t,`node_modules`);if(!(0,p.existsSync)(n))try{await(0,m.symlink)(this.nodeModulesPath,n,`junction`)}catch{}return this.bundleSpecWithHooksEntry(e.specPath,e.hooksPath,t)}async bundleSpecWithHooksEntry(e,t,n){let r=(0,f.dirname)((0,f.resolve)(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
50
  `);try{let t=await(0,ne.build)({stdin:{contents:a,resolveDir:(0,f.dirname)(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:[...X.EXTERNALS],define:{[qn]:JSON.stringify(i)},plugins:[{name:X.PLUGIN_NAME,setup(e){e.onResolve({filter:Kn},()=>({path:i}))}}],write:!0,logLevel:`silent`});if(t.errors.length>0){let n=t.errors.map(e=>e.text).join(`
51
51
  `);throw console.error(`${Y} Build failed for "${e}" (with hooks): ${n}`),new Q(e,n,{cause:Error(n)})}let r=(0,f.join)(n,`stdin.js`);return{outputPath:r,cleanup:()=>this.safeUnlink(r)}}catch(t){if(t instanceof Q)throw t;let n=t instanceof Error?t.message:Z.UNEXPECTED_BUILD;throw console.error(`${Y} Build failed for "${e}" (with hooks):`,t),new Q(e,n,{cause:t})}}async safeUnlink(e){try{await(0,m.unlink)(e)}catch(t){console.warn(`${Y} ${Z.CLEANUP_FAILED} "${e}":`,t)}}};Xn=N([(0,d.injectable)(),j(`design:paramtypes`,[])],Xn);let Zn=class{async discoverProjects(e){let t=e||process.cwd(),n=[],r=await this.findPlaywrightConfigs(t);for(let e of r){let t=(0,f.dirname)(e),r={name:(0,f.basename)(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=(0,f.join)(e,`playwright.config.ts`);if(!(0,p.existsSync)(t))throw Error(`No playwright.config.ts found in ${e}`);let n=(0,f.join)(e,await this.getTestDirFromConfig(t));if(!(0,p.existsSync)(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=(0,f.resolve)(e);if(!(0,p.existsSync)(n))throw Error(`Spec file not found: ${e}`);let r=(0,f.basename)(n);return{name:r,path:n,relativePath:t?(0,f.relative)(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=(0,f.basename)(e);return!(t===`node_modules`||t.startsWith(`.`))}return(0,f.basename)(e)===`playwright.config.ts`&&t.push(e),!0}),t}async findSpecFiles(e){let t=[];return await this.walkDirectory(e,(e,n)=>n?(0,f.basename)(e)!==`node_modules`:(e.endsWith(`.spec.ts`)&&t.push(e),!0)),t}async walkDirectory(e,t){if(!(0,p.existsSync)(e))return;let n=await(0,m.readdir)(e,{withFileTypes:!0});for(let r of n){let n=(0,f.join)(e,r.name);r.isDirectory()?t(n,!0)&&await this.walkDirectory(n,t):t(n,!1)}}async getTestDirFromConfig(e){try{let t=(await(0,m.readFile)(e,`utf-8`)).match(/testDir:\s*['"`]([^'"`]+)['"`]/);if(t)return t[1].replace(/^\.\//,``)}catch{}return`tests`}async checkHasArgsSchema(e){try{let t=await(0,m.readFile)(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`)}};Zn=N([(0,d.injectable)()],Zn);let Qn=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(0,m.readFile)(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}};Qn=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),j(`design:paramtypes`,[Object])],Qn);function $n(e,t){let{runtimeUse:n,unsupportedKeys:r}=c.h(t);return{options:{...e?{baseURL:e}:{},...n},warnings:r.length>0?[`test.use() ignores unsupported fixture keys: ${r.join(`, `)}`]:[]}}let er=class{constructor(e){this.bundler=e}async loadSpec(e,t){let n=t?await this.bundler.bundleWithHooks({specPath:e,hooksPath:t}):await this.bundler.bundle(e),r=process,i=r.__pw_initiator__;try{return c.c(e),r.__pw_initiator__=void 0,await import(`${n.outputPath}?t=${Date.now()}`),c.s()}catch(t){throw c.p(),Error(`Failed to load spec "${e}": ${t instanceof Error?t.message:String(t)}`,{cause:t})}finally{r.__pw_initiator__=i,await n.cleanup()}}async executeSpec(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,hooksPath:i,baseURL:a,keepBrowserOpen:o}=e,s=Date.now(),l=[],u=0,d=0,f=new Set,p=new Set,m=[],h=await v.default[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}=$n(a,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 s=await h.newContext(e),g=await s.newPage(),_={page:g,context:s,browser:h,playwright:v.default},y=Date.now();try{await t.fn(_),l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-y}),u++}catch(e){if(e instanceof c.t||e instanceof Error&&`isSkip`in e)l.push({title:t.title,fullTitle:t.fullTitle,passed:!0,error:null,duration:Date.now()-y}),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()-y}),d++,r!==void 0&&p.add(r)}}finally{o?m.push({page:g,context:s}):await s.close().catch(()=>{})}}if(!o){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()-s,warnings:f.size>0?[...f]:void 0,browser:o?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()-s}],success:!1,duration:Date.now()-s,warnings:f.size>0?[...f]:void 0}}}async executeSpecEnhanced(e){let{specPath:t,browserType:n=`chromium`,headless:r=!0,testFilter:i,specArgs:a,hooksPath:o,baseURL:s,keepBrowserOpen:l}=e,u=Date.now(),d=[],f=0,p=0,m=new Set,h=new Set,g=[],_=await v.default[n].launch({channel:`chrome`,headless:r});try{let e=await this.loadSpec(t,o),n=i?this.filterTests(e.allTests,i):e.allTests;for(let e of n){let{options:t,warnings:n}=$n(s,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 o=await _.newContext(t),u=await o.newPage(),y={page:u,context:o,browser:_,playwright:v.default,specArgs:a},b=Date.now();try{await e.fn(y),d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-b}),f++}catch(t){if(t instanceof c.t||t instanceof Error&&`isSkip`in t)d.push({title:e.title,fullTitle:e.fullTitle,passed:!0,error:null,duration:Date.now()-b}),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()-b}),p++,r!==void 0&&h.add(r)}}finally{l?g.push({page:u,context:o}):await o.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}};er=N([(0,d.injectable)(),M(0,(0,d.inject)(A.SpecBundlerService)),j(`design:paramtypes`,[Object])],er);let tr=class{browsers=new Map;browserIdCounter=0;constructor(e,t,n){this.profileService=e,this.pageRegistry=t,this.lockManager=n}findChromePath(){let e=h.default.platform(),t={darwin:[`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,`/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary`,`${h.default.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(p.default.existsSync(e))return e;return null}resolveExtensionPath(e){if(e&&p.default.existsSync(f.default.join(e,`manifest.json`)))return e;let t=f.default.dirname(new URL(require(`url`).pathToFileURL(__filename).href).pathname),n=[f.default.resolve(t,`../dist/extension`),f.default.resolve(t,`../../dist/extension`)];for(let e of n)if(p.default.existsSync(f.default.join(e,`manifest.json`)))return e;let r=f.default.resolve(process.cwd(),`dist/extension`);if(p.default.existsSync(f.default.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=f.default.join(this.profileService.getProfilesDir(),e,`stealth-user-data`);return p.default.mkdirSync(t,{recursive:!0}),t}let t=f.default.join(h.default.tmpdir(),`stealth-chrome-${Date.now()}`);return p.default.mkdirSync(t,{recursive:!0}),t}async launch(e={}){let t=this.findChromePath();if(!t)throw Error(`Chrome not found. Please install Google Chrome.