@agimon-ai/browse-tool 0.10.5 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{playwright-test-D--wiuvm.cjs → playwright-test-DR8tmuiB.cjs} +2 -2
- package/dist/{playwright-test-DlT1Ad7l.mjs → playwright-test-DoPVFxH9.mjs} +1 -1
- package/dist/{streamable-http-CHbht6Hk.cjs → streamable-http-SGOVRHb7.cjs} +1 -1
- package/dist/{streamable-http-B4RiCNHp.mjs → streamable-http-ajTIeOoU.mjs} +1 -1
- package/dist/stubs/playwright-test.cjs +1 -1
- package/dist/stubs/playwright-test.mjs +1 -1
- package/package.json +5 -5
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./streamable-http-CHbht6Hk.cjs`);require(`reflect-metadata/lite`);let t=require(`@agimon-ai/foundation-port-registry`),n=require(`@agimon-ai/foundation-process-registry`),r=require(`inversify`),i=require(`node:path`);i=e.E(i,1);let a=require(`node:fs`),o=require(`node:fs/promises`),s=require(`node:os`),c=require(`node:module`),l=require(`zod`),u=require(`@opentelemetry/api`),d=require(`@modelcontextprotocol/sdk/server/index.js`),f=require(`@modelcontextprotocol/sdk/types.js`),p=require(`@modelcontextprotocol/sdk/server/stdio.js`),m=require(`commander`),h=require(`@hono/node-server`),g=require(`hono`),_=require(`hono/cors`),v=require(`@hono/node-ws`),y=require(`hono/html`),b=require(`hono/jsx`),x=require(`hono/jsx/jsx-runtime`);var S=`0.10.4`;const C=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`;function w(e,t){if(!C)return;let n=t?` ${JSON.stringify(t)}`:``;console.log(`[ExtensionRecordingDebug] ${e}${n}`)}function T(t){let n=new g.Hono,r;try{r=t.get(e.T.TelemetryService)}catch{r=new e.g}return n.get(`/telemetry-config`,t=>{try{let n=new URL(t.req.url).origin;return t.json(e._(process.env,n))}catch(e){return t.json({enabled:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/tasks`,n=>{try{let r=t.get(e.T.ExtensionTaskQueue).getNextTask();return r?n.json({task:{id:r.id,tool:r.tool,arguments:r.arguments,telemetry:r.telemetry}}):n.json({})}catch(e){return n.json({error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/result`,async n=>{try{let r=await n.req.json();if(!r.taskId)return n.json({success:!1,error:`Missing taskId in request body`},400);let i=t.get(e.T.ExtensionTaskQueue),a=t.get(e.T.BrowserService),o={taskId:r.taskId,success:r.success,result:r.result,error:r.error},s=i.submitResult(o);return s?(s.browserId&&a.recordBrowserActivity(s.browserId,s.pageId),n.json({success:!0})):n.json({success:!1,error:`Task ${r.taskId} not found or already completed`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/status`,n=>{try{let r=t.get(e.T.ExtensionTaskQueue),i=r.getConnectionStatus(),a={connected:i.connected,lastPollAt:i.lastPollAt?.toISOString(),lastResultAt:i.lastResultAt?.toISOString(),pendingTasks:i.pendingTasks,queueSize:r.queueSize};return n.json(a)}catch(e){return n.json({connected:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/register`,async n=>{try{let r=await n.req.json();if(!r.browserId)return n.json({success:!1,error:`Missing browserId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).register(r);return n.json({success:!0,session:{id:i.id,browserId:i.browserId,controlMode:i.controlMode,createdAt:i.createdAt.toISOString()}})}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/heartbeat`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).heartbeat(r);return i?n.json({success:!0,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested,lastHeartbeatAt:i.lastHeartbeatAt.toISOString()}}):n.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/tab-mapped`,async n=>{try{let r=await n.req.json();if(!r.pageId||typeof r.tabId!=`number`)return n.json({success:!1,error:`Missing pageId or tabId in request body`},400);let i=t.get(e.T.PageRegistry),a=t.get(e.T.BrowserService),o=i.get(r.pageId);return o?(o.extensionTabId=r.tabId,a.recordBrowserActivity(o.browserId,r.pageId),n.json({success:!0})):n.json({success:!1,error:`Page ${r.pageId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/recording`,async n=>{try{let r=await n.req.json();if(!r.browserId)return n.json({success:!1,error:`Missing browserId in request body`},400);let i=t.get(e.T.BrowserService),a=await i.persistExtensionRecordingArtifact(r.browserId,r.videoBase64);return w(`artifact received`,{browserId:r.browserId,videoBase64Size:r.videoBase64?.length??0,persisted:a}),a?(i.recordBrowserActivity(r.browserId),n.json({success:!0})):n.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/recording/chunk`,async n=>{try{let i=await n.req.json();if(!i.browserId||!i.chunkBase64)return n.json({success:!1,error:`Missing browserId or chunkBase64 in request body`},400);let a=t.get(e.T.BrowserService),o=await a.persistExtensionRecordingChunk(i.browserId,i.chunkBase64);return w(`chunk received`,{browserId:i.browserId,chunkIndex:i.chunkIndex,mimeType:i.mimeType,persisted:!!o,chunkBase64Size:i.chunkBase64.length}),o?(r.log(`debug`,`extension recording chunk received`,{attributes:{"browse_tool.extension.recording.chunk_received":!0,"browse_tool.browser.id":i.browserId,"browse_tool.recording.chunk_bytes":o.chunkBytes,"browse_tool.recording.total_bytes":o.totalBytes,"browse_tool.recording.chunk_count":o.chunkCount,...typeof i.chunkIndex==`number`?{"browse_tool.recording.chunk_index":i.chunkIndex}:{},...typeof i.mimeType==`string`?{"browse_tool.recording.mime_type":i.mimeType}:{}}}),a.recordBrowserActivity(i.browserId),n.json({success:!0})):n.json({success:!1,error:`Browser "${i.browserId}" has no active recording target`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/browser-log`,async e=>{try{let t=await e.req.json();return!t.message||typeof t.message!=`string`?e.json({success:!1,error:`Missing message in request body`},400):(r.log(t.level??`info`,t.message,{attributes:{"browse_tool.extension.log_relay":!0,...typeof t.attributes==`object`&&t.attributes!==null?t.attributes:{}}}),w(`browser log relayed`,{level:t.level??`info`,message:t.message,attributes:t.attributes}),e.json({success:!0}))}catch(t){return e.json({success:!1,error:t instanceof Error?t.message:String(t)},500)}}),n.post(`/handoff`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).requestHandoff(r);return i?n.json({success:!0,message:`Handoff requested. AI will take control when ready.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):n.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/handoff/acknowledge`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).acknowledgeHandoff(r.sessionId);return i?n.json({success:!0,message:`Handoff acknowledged. AI now has control.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):n.json({success:!1,error:`Session ${r.sessionId} not found or no handoff pending`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/sessions`,n=>{try{let r=t.get(e.T.ExtensionSessionRegistry).listSessions();return n.json({sessions:r.map(e=>({id:e.id,browserId:e.browserId,tabId:e.tabId,currentUrl:e.currentUrl,controlMode:e.controlMode,activeSpecPath:e.activeSpecPath,handoffRequested:e.handoffRequested,createdAt:e.createdAt.toISOString(),lastHeartbeatAt:e.lastHeartbeatAt.toISOString()}))})}catch(e){return n.json({error:e instanceof Error?e.message:String(e)},500)}}),n}function E(e){if(e==null||e===``)return;let t=typeof e==`number`?e:typeof e==`string`?Number.parseInt(e,10):NaN;if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`Invalid port: ${e}`);return t}const D=[`pnpm-workspace.yaml`,`nx.json`,`.git`],O=`browse-tool-chrome`,k=`tool`,ee=process.env.NODE_ENV||`development`,te=`127.0.0.1`;function ne(e=process.cwd()){let t=i.default.resolve(e);for(;;){for(let e of D)if((0,a.existsSync)(i.default.join(t,e)))return t;let e=i.default.dirname(t);if(e===t)return process.cwd();t=e}}function re(){return new t.PortRegistryService(process.env.PORT_REGISTRY_PATH)}async function ie(e,t,n){let r=re(),i=await r.reservePort({repositoryPath:e,serviceName:O,serviceType:k,environment:ee,preferredPort:t,portRange:n,pid:process.pid,host:te,force:!0,metadata:{transport:`stdio`,mode:`chrome-serve`}});if(!i.success||!i.record)throw Error(i.error||`Failed to reserve port ${t}`);let a=!1;return{port:i.record.port,release:async()=>{if(a)return;a=!0;let n=await r.releasePort({repositoryPath:e,serviceName:O,serviceType:k,environment:ee,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port ${t}`)}}}function ae(){return new r.ContainerModule(t=>{t.bind(e.T.ExtensionTaskQueue).to(e.h).inSingletonScope(),t.bind(e.T.ExtensionToolDelegator).to(e.m).inSingletonScope()})}function oe(e){let t=new d.Server({name:`browse-tool-chrome`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e.getSupportedTools().map(e=>({name:e,description:`Browser automation tool (Chrome extension mode): ${e}`,inputSchema:{type:`object`,properties:{},additionalProperties:!0}}));return t.setRequestHandler(f.ListToolsRequestSchema,async()=>({tools:n})),t.setRequestHandler(f.CallToolRequestSchema,async t=>{let{name:n,arguments:r}=t.params;return await e.executeTool(n,r||{})}),t}const se=new m.Command(`chrome-serve`).description(`[DEPRECATED] Start MCP server with Chrome extension HTTP polling for bot-detection-free browser automation`).option(`-p, --port <port>`,`HTTP server port for extension polling`).option(`-v, --verbose`,`Enable verbose output`,!1).option(`--wait-for-extension`,`Wait for extension to connect before accepting MCP requests`,!1).action(async i=>{console.error(``),console.error(`╔════════════════════════════════════════════════════════════════╗`),console.error(`║ DEPRECATED: chrome-serve is deprecated. ║`),console.error(`║ Use mcp-serve instead - extension routes are now ║`),console.error(`║ automatically available in the HTTP server at /extension/*. ║`),console.error(`╚════════════════════════════════════════════════════════════════╝`),console.error(``);let a,o;try{let s=E(i.port),c=ne(process.cwd()),l=process.env.PORT_REGISTRY_PATH;l&&(process.env.PROCESS_REGISTRY_PATH=(0,n.resolveSiblingRegistryPath)(l,`processes.json`)),a=await ie(c,s??t.DEFAULT_PORT_RANGE.min,s?{min:s,max:s}:t.DEFAULT_PORT_RANGE);let u=a.port;o=await(0,n.createProcessLease)({repositoryPath:c,serviceName:O,serviceType:k,environment:ee,pid:process.pid,port:u,host:te,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`chrome-serve`,waitForExtension:i.waitForExtension}}),i.verbose&&(console.error(`Chrome Extension MCP Server starting...`),console.error(` HTTP Port: ${u}`),console.error(` Wait for extension: ${i.waitForExtension}`));let d=new r.Container({defaultScope:`Singleton`});d.load(ae());let f=d.get(e.T.ExtensionTaskQueue),m=d.get(e.T.ExtensionToolDelegator),v=new g.Hono;v.use(`*`,(0,_.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`]}));let y=T(d);v.route(`/extension`,y),v.get(`/health`,e=>{let t=f.getConnectionStatus();return e.json({status:`healthy`,service:`browse-tool-chrome`,extension:t})});let b=(0,h.serve)({fetch:v.fetch,port:u});b.on(`error`,e=>{e.code===`EADDRINUSE`?(console.error(`Error [PORT_IN_USE]: Port ${u} is already in use.`),console.error(`Recovery: Try a different port with --port <port>`)):console.error(`Error [SERVER_ERROR]: HTTP server error: ${e.message}`),process.exit(1)}),console.error(`HTTP server started on port ${u}`),console.error(`Waiting for Chrome extension to connect...`),console.error(`Extension should poll: http://localhost:${u}/extension/tasks`),i.waitForExtension&&(console.error(`Waiting for extension connection before starting MCP...`),await new Promise(e=>{let t=setInterval(()=>{f.getConnectionStatus().connected&&(clearInterval(t),console.error(`Extension connected!`),e())},500)}));let x=oe(m),S=new p.StdioServerTransport;await x.connect(S),console.error(`Chrome extension MCP server started on stdio`);let C=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`);try{f.clearAllTasks(`Server shutting down`),await S.close(),b.close(),await o.release({kill:!1}),await a.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>C(`SIGINT`)),process.on(`SIGTERM`,()=>C(`SIGTERM`))}catch(e){if(o||a)try{await o?.release(),await a?.release()}catch{}let t=e instanceof Error?e.message:String(e);console.error(`Error [SERVER_ERROR]: Failed to start Chrome extension MCP server: ${t}`),console.error(`Recovery: Check that the port is available and try again.`),process.exit(1)}}),ce=`BROWSE_TOOL_CONFIG`,le=`.browse-tool`,ue=`config.json`,de=[`json`,`text`,`quiet`],fe={commands:{},tools:{}},pe=l.z.record(l.z.string(),l.z.unknown()),me=l.z.object({mcpServe:l.z.object({type:l.z.string().optional(),browser:l.z.string().optional(),headless:l.z.boolean().optional(),profile:l.z.string().optional(),mode:l.z.string().optional(),host:l.z.string().optional(),port:l.z.coerce.number().int().positive().optional(),httpPort:l.z.coerce.number().int().positive().optional(),tags:l.z.string().optional(),exclude:l.z.string().optional(),customTools:l.z.string().optional(),chromeForTestingPath:l.z.string().optional(),snippetsDir:l.z.string().optional(),registryPath:l.z.string().optional(),registryDir:l.z.string().optional(),pidsDir:l.z.string().optional(),profilesDir:l.z.string().optional(),proxyConfigDir:l.z.string().optional()}).partial().optional(),httpServe:l.z.object({port:l.z.coerce.number().int().positive().optional(),headless:l.z.boolean().optional(),idleTimeout:l.z.coerce.number().positive().optional(),host:l.z.string().optional(),registryDir:l.z.string().optional(),registryPath:l.z.string().optional(),pidsDir:l.z.string().optional(),profilesDir:l.z.string().optional(),snippetsDir:l.z.string().optional(),proxyConfigDir:l.z.string().optional()}).partial().optional(),exec:l.z.object({format:l.z.enum(de).optional(),color:l.z.boolean().optional(),port:l.z.coerce.number().int().positive().optional()}).partial().optional(),tools:l.z.object({format:l.z.enum(de).optional(),color:l.z.boolean().optional(),port:l.z.coerce.number().int().positive().optional()}).partial().optional(),status:l.z.record(l.z.string(),l.z.unknown()).optional(),stop:l.z.record(l.z.string(),l.z.unknown()).optional()}),he=l.z.object({commands:me.default({}),tools:l.z.record(l.z.string(),pe).default({})});let A={config:fe};function ge(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n===`--config`)return e[t+1];if(n.startsWith(`--config=`))return n.slice(9)}}function _e(e){let t=i.default.resolve(e);if(!(0,a.existsSync)(t))throw Error(`Config path not found: ${t}`);return(0,a.statSync)(t).isDirectory()?i.default.join(t,ue):t}function ve(e,t){let n=t.env??process.env,r=t.cwd??process.cwd(),o=t.homeDir??(0,s.homedir)(),c=ge(e);if(c)return _e(c);if(n[ce])return _e(n[ce]);let l=i.default.join(r,le,ue);if((0,a.existsSync)(l))return l;let u=i.default.join(o,le,ue);if((0,a.existsSync)(u))return u}function ye(e){let t=(0,a.readFileSync)(e,`utf8`),n=JSON.parse(t);return he.parse(n)}function be(e,t={}){let n=ve(e,t);return n?{configPath:n,config:ye(n)}:{config:fe}}function xe(e,t={}){return A=be(e,t),A}function j(e){return(A.config.commands??{})[e]??{}}function Se(e){return A.config.tools?.[e]??{}}function M(e,t,n,r,i){let a=e.getOptionValueSourceWithGlobals(t);return a!==void 0&&a!==`default`&&a!==`implied`?n:i===void 0?r===void 0?n:r:i}var Ce=class{port;exactPort;timeout;serverPort=null;constructor(t={}){this.port=t.port??e.d,this.exactPort=t.exactPort??!1,this.timeout=t.timeout??6e4}async ensureServer(){if(this.serverPort!==null)return this.serverPort;let t=await e.o().get(e.T.HttpServerManager).ensureRunning(this.port,{exactPort:this.exactPort});if(!t.running||!t.port)throw Error(t.error??`Failed to start HTTP server. Try running "browse-tool http-serve" manually.`);return this.serverPort=t.port,t.port}async getBaseUrl(){return`http://localhost:${await this.ensureServer()}`}async listTools(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/tools`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async listCustomTools(e){let t=await this.getBaseUrl(),n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let r=new URL(`/custom-tools`,t);r.searchParams.set(`dir`,e);let i=await fetch(r,{method:`GET`,headers:{Accept:`application/json`},signal:n.signal});if(!i.ok)throw Error(`HTTP ${i.status}: ${i.statusText}`);let a=await i.json();if(a.error)throw Error(a.error);return a.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(r)}}async listBrowsers(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/browsers`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.browsers}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async execute(e,t){let n=await this.getBaseUrl(),r=e===`run_spec`?void 0:this.timeout,i=new AbortController,a=r===void 0?void 0:setTimeout(()=>i.abort(),r);try{let a=await fetch(`${n}/execute`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:e,arguments:t}),signal:r===void 0?void 0:i.signal});if(!a.ok){let e=await a.text();throw Error(`HTTP ${a.status}: ${e||a.statusText}`)}let o=await a.json();return o.success?o.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:o.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{a&&clearTimeout(a)}}async executeCustomTool(e,t,n){let r=await this.getBaseUrl(),i=new AbortController,a=setTimeout(()=>i.abort(),this.timeout);try{let a=new URL(`/custom-tools`,r);a.searchParams.set(`dir`,e);let o=await fetch(a,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:t,arguments:n}),signal:i.signal});if(!o.ok){let e=await o.text();throw Error(`HTTP ${o.status}: ${e||o.statusText}`)}let s=await o.json();return s.success?s.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:s.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(a)}}wrapConnectionError(e){return e instanceof Error?e.message.includes(`ECONNREFUSED`)||e.message.includes(`fetch failed`)?Error(`Cannot connect to browse-tool HTTP server.\n\nTry one of the following:\n 1. Start the server: browse-tool http-serve\n 2. Check if another process is using port ${this.port}\n 3. Run with a different port: browse-tool --port 3201 <command>\n\nOriginal error: ${e.message}`,{cause:e}):e:Error(String(e),{cause:e})}};function N(e){return new Ce(e)}const we={reset:`\x1B[0m`,red:`\x1B[31m`,green:`\x1B[32m`,yellow:`\x1B[33m`,blue:`\x1B[34m`,magenta:`\x1B[35m`,cyan:`\x1B[36m`,gray:`\x1B[90m`,bold:`\x1B[1m`};function P(e,t,n){return n?`${we[t]}${e}${we.reset}`:e}function F(e,t){let{format:n,color:r}=t;return n===`quiet`?``:n===`json`?Te(e):Ee(e,r)}function Te(e){return JSON.stringify(e,null,2)}function Ee(e,t){let n=[];e.isError&&n.push(P(`Error:`,`red`,t));for(let r of e.content)r.type===`text`?n.push(De(r,t)):r.type===`image`?n.push(Ae(r,t)):n.push(P(`[Unknown content type: ${r.type}]`,`yellow`,t));return n.join(`
|
|
2
|
+
const e=require(`./streamable-http-SGOVRHb7.cjs`);require(`reflect-metadata/lite`);let t=require(`@agimon-ai/foundation-port-registry`),n=require(`@agimon-ai/foundation-process-registry`),r=require(`inversify`),i=require(`node:path`);i=e.E(i,1);let a=require(`node:fs`),o=require(`node:fs/promises`),s=require(`node:os`),c=require(`node:module`),l=require(`zod`),u=require(`@opentelemetry/api`),d=require(`@modelcontextprotocol/sdk/server/index.js`),f=require(`@modelcontextprotocol/sdk/types.js`),p=require(`@modelcontextprotocol/sdk/server/stdio.js`),m=require(`commander`),h=require(`@hono/node-server`),g=require(`hono`),_=require(`hono/cors`),v=require(`@hono/node-ws`),y=require(`hono/html`),b=require(`hono/jsx`),x=require(`hono/jsx/jsx-runtime`);var S=`0.11.0`;const C=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`;function w(e,t){if(!C)return;let n=t?` ${JSON.stringify(t)}`:``;console.log(`[ExtensionRecordingDebug] ${e}${n}`)}function T(t){let n=new g.Hono,r;try{r=t.get(e.T.TelemetryService)}catch{r=new e.g}return n.get(`/telemetry-config`,t=>{try{let n=new URL(t.req.url).origin;return t.json(e._(process.env,n))}catch(e){return t.json({enabled:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/tasks`,n=>{try{let r=t.get(e.T.ExtensionTaskQueue).getNextTask();return r?n.json({task:{id:r.id,tool:r.tool,arguments:r.arguments,telemetry:r.telemetry}}):n.json({})}catch(e){return n.json({error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/result`,async n=>{try{let r=await n.req.json();if(!r.taskId)return n.json({success:!1,error:`Missing taskId in request body`},400);let i=t.get(e.T.ExtensionTaskQueue),a=t.get(e.T.BrowserService),o={taskId:r.taskId,success:r.success,result:r.result,error:r.error},s=i.submitResult(o);return s?(s.browserId&&a.recordBrowserActivity(s.browserId,s.pageId),n.json({success:!0})):n.json({success:!1,error:`Task ${r.taskId} not found or already completed`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/status`,n=>{try{let r=t.get(e.T.ExtensionTaskQueue),i=r.getConnectionStatus(),a={connected:i.connected,lastPollAt:i.lastPollAt?.toISOString(),lastResultAt:i.lastResultAt?.toISOString(),pendingTasks:i.pendingTasks,queueSize:r.queueSize};return n.json(a)}catch(e){return n.json({connected:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/register`,async n=>{try{let r=await n.req.json();if(!r.browserId)return n.json({success:!1,error:`Missing browserId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).register(r);return n.json({success:!0,session:{id:i.id,browserId:i.browserId,controlMode:i.controlMode,createdAt:i.createdAt.toISOString()}})}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/heartbeat`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).heartbeat(r);return i?n.json({success:!0,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested,lastHeartbeatAt:i.lastHeartbeatAt.toISOString()}}):n.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/tab-mapped`,async n=>{try{let r=await n.req.json();if(!r.pageId||typeof r.tabId!=`number`)return n.json({success:!1,error:`Missing pageId or tabId in request body`},400);let i=t.get(e.T.PageRegistry),a=t.get(e.T.BrowserService),o=i.get(r.pageId);return o?(o.extensionTabId=r.tabId,a.recordBrowserActivity(o.browserId,r.pageId),n.json({success:!0})):n.json({success:!1,error:`Page ${r.pageId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/recording`,async n=>{try{let r=await n.req.json();if(!r.browserId)return n.json({success:!1,error:`Missing browserId in request body`},400);let i=t.get(e.T.BrowserService),a=await i.persistExtensionRecordingArtifact(r.browserId,r.videoBase64);return w(`artifact received`,{browserId:r.browserId,videoBase64Size:r.videoBase64?.length??0,persisted:a}),a?(i.recordBrowserActivity(r.browserId),n.json({success:!0})):n.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/recording/chunk`,async n=>{try{let i=await n.req.json();if(!i.browserId||!i.chunkBase64)return n.json({success:!1,error:`Missing browserId or chunkBase64 in request body`},400);let a=t.get(e.T.BrowserService),o=await a.persistExtensionRecordingChunk(i.browserId,i.chunkBase64);return w(`chunk received`,{browserId:i.browserId,chunkIndex:i.chunkIndex,mimeType:i.mimeType,persisted:!!o,chunkBase64Size:i.chunkBase64.length}),o?(r.log(`debug`,`extension recording chunk received`,{attributes:{"browse_tool.extension.recording.chunk_received":!0,"browse_tool.browser.id":i.browserId,"browse_tool.recording.chunk_bytes":o.chunkBytes,"browse_tool.recording.total_bytes":o.totalBytes,"browse_tool.recording.chunk_count":o.chunkCount,...typeof i.chunkIndex==`number`?{"browse_tool.recording.chunk_index":i.chunkIndex}:{},...typeof i.mimeType==`string`?{"browse_tool.recording.mime_type":i.mimeType}:{}}}),a.recordBrowserActivity(i.browserId),n.json({success:!0})):n.json({success:!1,error:`Browser "${i.browserId}" has no active recording target`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/browser-log`,async e=>{try{let t=await e.req.json();return!t.message||typeof t.message!=`string`?e.json({success:!1,error:`Missing message in request body`},400):(r.log(t.level??`info`,t.message,{attributes:{"browse_tool.extension.log_relay":!0,...typeof t.attributes==`object`&&t.attributes!==null?t.attributes:{}}}),w(`browser log relayed`,{level:t.level??`info`,message:t.message,attributes:t.attributes}),e.json({success:!0}))}catch(t){return e.json({success:!1,error:t instanceof Error?t.message:String(t)},500)}}),n.post(`/handoff`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).requestHandoff(r);return i?n.json({success:!0,message:`Handoff requested. AI will take control when ready.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):n.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.post(`/handoff/acknowledge`,async n=>{try{let r=await n.req.json();if(!r.sessionId)return n.json({success:!1,error:`Missing sessionId in request body`},400);let i=t.get(e.T.ExtensionSessionRegistry).acknowledgeHandoff(r.sessionId);return i?n.json({success:!0,message:`Handoff acknowledged. AI now has control.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):n.json({success:!1,error:`Session ${r.sessionId} not found or no handoff pending`},404)}catch(e){return n.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),n.get(`/sessions`,n=>{try{let r=t.get(e.T.ExtensionSessionRegistry).listSessions();return n.json({sessions:r.map(e=>({id:e.id,browserId:e.browserId,tabId:e.tabId,currentUrl:e.currentUrl,controlMode:e.controlMode,activeSpecPath:e.activeSpecPath,handoffRequested:e.handoffRequested,createdAt:e.createdAt.toISOString(),lastHeartbeatAt:e.lastHeartbeatAt.toISOString()}))})}catch(e){return n.json({error:e instanceof Error?e.message:String(e)},500)}}),n}function E(e){if(e==null||e===``)return;let t=typeof e==`number`?e:typeof e==`string`?Number.parseInt(e,10):NaN;if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`Invalid port: ${e}`);return t}const D=[`pnpm-workspace.yaml`,`nx.json`,`.git`],O=`browse-tool-chrome`,k=`tool`,ee=process.env.NODE_ENV||`development`,te=`127.0.0.1`;function ne(e=process.cwd()){let t=i.default.resolve(e);for(;;){for(let e of D)if((0,a.existsSync)(i.default.join(t,e)))return t;let e=i.default.dirname(t);if(e===t)return process.cwd();t=e}}function re(){return new t.PortRegistryService(process.env.PORT_REGISTRY_PATH)}async function ie(e,t,n){let r=re(),i=await r.reservePort({repositoryPath:e,serviceName:O,serviceType:k,environment:ee,preferredPort:t,portRange:n,pid:process.pid,host:te,force:!0,metadata:{transport:`stdio`,mode:`chrome-serve`}});if(!i.success||!i.record)throw Error(i.error||`Failed to reserve port ${t}`);let a=!1;return{port:i.record.port,release:async()=>{if(a)return;a=!0;let n=await r.releasePort({repositoryPath:e,serviceName:O,serviceType:k,environment:ee,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port ${t}`)}}}function ae(){return new r.ContainerModule(t=>{t.bind(e.T.ExtensionTaskQueue).to(e.h).inSingletonScope(),t.bind(e.T.ExtensionToolDelegator).to(e.m).inSingletonScope()})}function oe(e){let t=new d.Server({name:`browse-tool-chrome`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e.getSupportedTools().map(e=>({name:e,description:`Browser automation tool (Chrome extension mode): ${e}`,inputSchema:{type:`object`,properties:{},additionalProperties:!0}}));return t.setRequestHandler(f.ListToolsRequestSchema,async()=>({tools:n})),t.setRequestHandler(f.CallToolRequestSchema,async t=>{let{name:n,arguments:r}=t.params;return await e.executeTool(n,r||{})}),t}const se=new m.Command(`chrome-serve`).description(`[DEPRECATED] Start MCP server with Chrome extension HTTP polling for bot-detection-free browser automation`).option(`-p, --port <port>`,`HTTP server port for extension polling`).option(`-v, --verbose`,`Enable verbose output`,!1).option(`--wait-for-extension`,`Wait for extension to connect before accepting MCP requests`,!1).action(async i=>{console.error(``),console.error(`╔════════════════════════════════════════════════════════════════╗`),console.error(`║ DEPRECATED: chrome-serve is deprecated. ║`),console.error(`║ Use mcp-serve instead - extension routes are now ║`),console.error(`║ automatically available in the HTTP server at /extension/*. ║`),console.error(`╚════════════════════════════════════════════════════════════════╝`),console.error(``);let a,o;try{let s=E(i.port),c=ne(process.cwd()),l=process.env.PORT_REGISTRY_PATH;l&&(process.env.PROCESS_REGISTRY_PATH=(0,n.resolveSiblingRegistryPath)(l,`processes.json`)),a=await ie(c,s??t.DEFAULT_PORT_RANGE.min,s?{min:s,max:s}:t.DEFAULT_PORT_RANGE);let u=a.port;o=await(0,n.createProcessLease)({repositoryPath:c,serviceName:O,serviceType:k,environment:ee,pid:process.pid,port:u,host:te,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`chrome-serve`,waitForExtension:i.waitForExtension}}),i.verbose&&(console.error(`Chrome Extension MCP Server starting...`),console.error(` HTTP Port: ${u}`),console.error(` Wait for extension: ${i.waitForExtension}`));let d=new r.Container({defaultScope:`Singleton`});d.load(ae());let f=d.get(e.T.ExtensionTaskQueue),m=d.get(e.T.ExtensionToolDelegator),v=new g.Hono;v.use(`*`,(0,_.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`]}));let y=T(d);v.route(`/extension`,y),v.get(`/health`,e=>{let t=f.getConnectionStatus();return e.json({status:`healthy`,service:`browse-tool-chrome`,extension:t})});let b=(0,h.serve)({fetch:v.fetch,port:u});b.on(`error`,e=>{e.code===`EADDRINUSE`?(console.error(`Error [PORT_IN_USE]: Port ${u} is already in use.`),console.error(`Recovery: Try a different port with --port <port>`)):console.error(`Error [SERVER_ERROR]: HTTP server error: ${e.message}`),process.exit(1)}),console.error(`HTTP server started on port ${u}`),console.error(`Waiting for Chrome extension to connect...`),console.error(`Extension should poll: http://localhost:${u}/extension/tasks`),i.waitForExtension&&(console.error(`Waiting for extension connection before starting MCP...`),await new Promise(e=>{let t=setInterval(()=>{f.getConnectionStatus().connected&&(clearInterval(t),console.error(`Extension connected!`),e())},500)}));let x=oe(m),S=new p.StdioServerTransport;await x.connect(S),console.error(`Chrome extension MCP server started on stdio`);let C=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`);try{f.clearAllTasks(`Server shutting down`),await S.close(),b.close(),await o.release({kill:!1}),await a.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>C(`SIGINT`)),process.on(`SIGTERM`,()=>C(`SIGTERM`))}catch(e){if(o||a)try{await o?.release(),await a?.release()}catch{}let t=e instanceof Error?e.message:String(e);console.error(`Error [SERVER_ERROR]: Failed to start Chrome extension MCP server: ${t}`),console.error(`Recovery: Check that the port is available and try again.`),process.exit(1)}}),ce=`BROWSE_TOOL_CONFIG`,le=`.browse-tool`,ue=`config.json`,de=[`json`,`text`,`quiet`],fe={commands:{},tools:{}},pe=l.z.record(l.z.string(),l.z.unknown()),me=l.z.object({mcpServe:l.z.object({type:l.z.string().optional(),browser:l.z.string().optional(),headless:l.z.boolean().optional(),profile:l.z.string().optional(),mode:l.z.string().optional(),host:l.z.string().optional(),port:l.z.coerce.number().int().positive().optional(),httpPort:l.z.coerce.number().int().positive().optional(),tags:l.z.string().optional(),exclude:l.z.string().optional(),customTools:l.z.string().optional(),chromeForTestingPath:l.z.string().optional(),snippetsDir:l.z.string().optional(),registryPath:l.z.string().optional(),registryDir:l.z.string().optional(),pidsDir:l.z.string().optional(),profilesDir:l.z.string().optional(),proxyConfigDir:l.z.string().optional()}).partial().optional(),httpServe:l.z.object({port:l.z.coerce.number().int().positive().optional(),headless:l.z.boolean().optional(),idleTimeout:l.z.coerce.number().positive().optional(),host:l.z.string().optional(),registryDir:l.z.string().optional(),registryPath:l.z.string().optional(),pidsDir:l.z.string().optional(),profilesDir:l.z.string().optional(),snippetsDir:l.z.string().optional(),proxyConfigDir:l.z.string().optional()}).partial().optional(),exec:l.z.object({format:l.z.enum(de).optional(),color:l.z.boolean().optional(),port:l.z.coerce.number().int().positive().optional()}).partial().optional(),tools:l.z.object({format:l.z.enum(de).optional(),color:l.z.boolean().optional(),port:l.z.coerce.number().int().positive().optional()}).partial().optional(),status:l.z.record(l.z.string(),l.z.unknown()).optional(),stop:l.z.record(l.z.string(),l.z.unknown()).optional()}),he=l.z.object({commands:me.default({}),tools:l.z.record(l.z.string(),pe).default({})});let A={config:fe};function ge(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n===`--config`)return e[t+1];if(n.startsWith(`--config=`))return n.slice(9)}}function _e(e){let t=i.default.resolve(e);if(!(0,a.existsSync)(t))throw Error(`Config path not found: ${t}`);return(0,a.statSync)(t).isDirectory()?i.default.join(t,ue):t}function ve(e,t){let n=t.env??process.env,r=t.cwd??process.cwd(),o=t.homeDir??(0,s.homedir)(),c=ge(e);if(c)return _e(c);if(n[ce])return _e(n[ce]);let l=i.default.join(r,le,ue);if((0,a.existsSync)(l))return l;let u=i.default.join(o,le,ue);if((0,a.existsSync)(u))return u}function ye(e){let t=(0,a.readFileSync)(e,`utf8`),n=JSON.parse(t);return he.parse(n)}function be(e,t={}){let n=ve(e,t);return n?{configPath:n,config:ye(n)}:{config:fe}}function xe(e,t={}){return A=be(e,t),A}function j(e){return(A.config.commands??{})[e]??{}}function Se(e){return A.config.tools?.[e]??{}}function M(e,t,n,r,i){let a=e.getOptionValueSourceWithGlobals(t);return a!==void 0&&a!==`default`&&a!==`implied`?n:i===void 0?r===void 0?n:r:i}var Ce=class{port;exactPort;timeout;serverPort=null;constructor(t={}){this.port=t.port??e.d,this.exactPort=t.exactPort??!1,this.timeout=t.timeout??6e4}async ensureServer(){if(this.serverPort!==null)return this.serverPort;let t=await e.o().get(e.T.HttpServerManager).ensureRunning(this.port,{exactPort:this.exactPort});if(!t.running||!t.port)throw Error(t.error??`Failed to start HTTP server. Try running "browse-tool http-serve" manually.`);return this.serverPort=t.port,t.port}async getBaseUrl(){return`http://localhost:${await this.ensureServer()}`}async listTools(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/tools`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async listCustomTools(e){let t=await this.getBaseUrl(),n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let r=new URL(`/custom-tools`,t);r.searchParams.set(`dir`,e);let i=await fetch(r,{method:`GET`,headers:{Accept:`application/json`},signal:n.signal});if(!i.ok)throw Error(`HTTP ${i.status}: ${i.statusText}`);let a=await i.json();if(a.error)throw Error(a.error);return a.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(r)}}async listBrowsers(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/browsers`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.browsers}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async execute(e,t){let n=await this.getBaseUrl(),r=e===`run_spec`?void 0:this.timeout,i=new AbortController,a=r===void 0?void 0:setTimeout(()=>i.abort(),r);try{let a=await fetch(`${n}/execute`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:e,arguments:t}),signal:r===void 0?void 0:i.signal});if(!a.ok){let e=await a.text();throw Error(`HTTP ${a.status}: ${e||a.statusText}`)}let o=await a.json();return o.success?o.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:o.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{a&&clearTimeout(a)}}async executeCustomTool(e,t,n){let r=await this.getBaseUrl(),i=new AbortController,a=setTimeout(()=>i.abort(),this.timeout);try{let a=new URL(`/custom-tools`,r);a.searchParams.set(`dir`,e);let o=await fetch(a,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:t,arguments:n}),signal:i.signal});if(!o.ok){let e=await o.text();throw Error(`HTTP ${o.status}: ${e||o.statusText}`)}let s=await o.json();return s.success?s.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:s.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(a)}}wrapConnectionError(e){return e instanceof Error?e.message.includes(`ECONNREFUSED`)||e.message.includes(`fetch failed`)?Error(`Cannot connect to browse-tool HTTP server.\n\nTry one of the following:\n 1. Start the server: browse-tool http-serve\n 2. Check if another process is using port ${this.port}\n 3. Run with a different port: browse-tool --port 3201 <command>\n\nOriginal error: ${e.message}`,{cause:e}):e:Error(String(e),{cause:e})}};function N(e){return new Ce(e)}const we={reset:`\x1B[0m`,red:`\x1B[31m`,green:`\x1B[32m`,yellow:`\x1B[33m`,blue:`\x1B[34m`,magenta:`\x1B[35m`,cyan:`\x1B[36m`,gray:`\x1B[90m`,bold:`\x1B[1m`};function P(e,t,n){return n?`${we[t]}${e}${we.reset}`:e}function F(e,t){let{format:n,color:r}=t;return n===`quiet`?``:n===`json`?Te(e):Ee(e,r)}function Te(e){return JSON.stringify(e,null,2)}function Ee(e,t){let n=[];e.isError&&n.push(P(`Error:`,`red`,t));for(let r of e.content)r.type===`text`?n.push(De(r,t)):r.type===`image`?n.push(Ae(r,t)):n.push(P(`[Unknown content type: ${r.type}]`,`yellow`,t));return n.join(`
|
|
3
3
|
`)}function De(e,t){let n=e.text;try{return Oe(JSON.parse(n),t)}catch{return n}}function Oe(e,t){if(typeof e!=`object`||!e)return String(e);let n=[];for(let[r,i]of Object.entries(e)){let e=P(r,`cyan`,t),a=ke(i,t);n.push(`${e}: ${a}`)}return n.join(`
|
|
4
4
|
`)}function ke(e,t){return e===null?P(`null`,`gray`,t):e===void 0?P(`undefined`,`gray`,t):typeof e==`boolean`?P(String(e),e?`green`:`red`,t):typeof e==`number`?P(String(e),`yellow`,t):typeof e==`string`?e.startsWith(`http://`)||e.startsWith(`https://`)?P(e,`blue`,t):e:Array.isArray(e)?e.length===0?P(`[]`,`gray`,t):JSON.stringify(e,null,2):typeof e==`object`?JSON.stringify(e,null,2):String(e)}function Ae(e,t){let{mimeType:n,data:r}=e;return P(`[Image: ${n}, ~${Math.round(r.length*3/4/1024)}KB base64 data]`,`magenta`,t)}function I(e,t){return P(`Error: ${e instanceof Error?e.message:e}`,`red`,t)}function je(e,t){let n=Math.max(...e.map(e=>e.name.length)),r=[];for(let i of e){let e=P(i.name.padEnd(n),`cyan`,t);r.push(` ${e} ${i.description}`)}return r.join(`
|
|
5
5
|
`)}function Me(e,t){let n=j(`tools`),r=M(e,`format`,t.format,n.format),i=M(e,`color`,t.color,n.color);return{port:M(e,`port`,t.port,n.port===void 0?void 0:String(n.port),process.env.PLAYWRIGHT_PORT),formatterOptions:{format:r,color:i}}}const Ne=new m.Command(`list-custom-tools`).description(`List custom tools from a tools directory`).argument(`<dir>`,`Path to the tools directory containing tools.yaml`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(e.d)).action(async function(e,t){let{port:n,formatterOptions:r}=Me(this,t);try{let t=await N({port:Number.parseInt(n,10)}).listCustomTools(e),i=r.format===`text`?je(t,r.color):r.format===`quiet`?``:JSON.stringify(t,null,2);i&&console.log(i)}catch(e){console.error(I(e instanceof Error?e:String(e),r.color)),process.exit(1)}}),Pe=new m.Command(`exec-custom-tool`).description(`Execute a custom tool from a tools directory`).argument(`<dir>`,`Path to the tools directory containing tools.yaml`).argument(`<tool>`,`Custom tool name to execute`).argument(`[args]`,`JSON arguments for the tool`,`{}`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(e.d)).action(async function(e,t,n,r){let{port:i,formatterOptions:a}=Me(this,r);try{let r;try{r=JSON.parse(n)}catch{console.error(I(`Invalid JSON arguments: ${n}`,a.color)),process.exit(1);return}let o=await N({port:Number.parseInt(i,10)}).executeCustomTool(e,t,r),s=F(o,a);s&&console.log(s),o.isError&&process.exit(1)}catch(e){console.error(I(e instanceof Error?e:String(e),a.color)),process.exit(1)}});new m.Command(`custom-tools`).description(`List and execute custom tools through the HTTP server`).addCommand(Ne).addCommand(Pe);const Fe=new m.Command(`docker-build-cft`).description(`Build the Chrome for Testing Docker image used by vm mode`).option(`--cft-version <version>`,`Chrome for Testing version to build`,e.w).option(`--image <image>`,`Docker image tag to produce`,e.S).option(`--platform <platform>`,`Docker target platform`,e.C).action(async t=>{try{let n=await e.y({version:t.cftVersion,image:t.image,platform:t.platform,stdio:`inherit`});console.log(`Built Docker image ${n.image}`),console.log(` Version: ${n.version}`),console.log(` Platform: ${n.platform}`),console.log(` Archive: ${e.b(n.platform)}`)}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(1)}}),Ie=new m.Command(`exec`).description(`Execute a tool directly with JSON arguments`).argument(`<tool>`,`Tool name to execute (e.g., browser_launch)`).argument(`[args]`,`JSON arguments for the tool`,`{}`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(e.d)).action(async function(e,t,n){let r=j(`exec`),i=M(this,`format`,n.format,r.format),a=M(this,`color`,n.color,r.color),o=M(this,`port`,n.port,r.port===void 0?void 0:String(r.port),process.env.PLAYWRIGHT_PORT),s=this.getOptionValueSourceWithGlobals(`port`),c=s!==void 0&&s!==`default`&&s!==`implied`,l={format:i,color:a};try{let n;try{n=JSON.parse(t)}catch{console.error(I(`Invalid JSON arguments: ${t}`,l.color)),process.exit(1)}let r=await N({port:Number.parseInt(o,10),exactPort:c}).execute(e,n),i=F(r,l);i&&console.log(i),r.isError&&process.exit(1)}catch(e){console.error(I(e instanceof Error?e:String(e),l.color)),process.exit(1)}}),L=`pageId`,R=`browserId`,Le=l.z.record(l.z.string(),l.z.unknown()),Re=l.z.object({type:l.z.literal(`object`),properties:l.z.record(l.z.string(),l.z.unknown()).optional(),required:l.z.array(l.z.string()).optional(),additionalProperties:l.z.boolean().optional()}).passthrough(),ze=l.z.object({name:l.z.string().min(1),description:l.z.string().min(1),script:l.z.string().min(1),suggestionActions:l.z.string().min(1).optional(),capabilities:Le,inputSchema:Re}),Be=l.z.object({tools:l.z.array(ze)}),Ve=process.env.BROWSE_TOOL_DEBUG_CUSTOM_TOOLS===`1`;function He(e){return JSON.stringify(e,(e,t)=>typeof t==`string`&&t.length>240?`${t.slice(0,240)}...<trimmed>`:t)}function z(e,t){if(Ve){if(t){console.error(`[CustomToolService] ${e}`,t);return}console.error(`[CustomToolService] ${e}`)}}function B(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ue(e){return B(e)&&Array.isArray(e.content)}function V(e){let t=e.trim();return t===``?``:t.startsWith(`"`)&&t.endsWith(`"`)||t.startsWith(`[`)&&t.endsWith(`]`)||t.startsWith(`{`)&&t.endsWith(`}`)?JSON.parse(t):t.startsWith(`'`)&&t.endsWith(`'`)?t.slice(1,-1):t===`true`?!0:t===`false`?!1:t===`null`?null:/^-?\d+(\.\d+)?$/.test(t)?Number(t):t}function We(e){if(!e||Object.keys(e).length===0)return;let t={};for(let[n,r]of Object.entries(e))if(r!=null){if(typeof r==`string`||typeof r==`number`||typeof r==`boolean`){t[n]=r;continue}t[n]=JSON.stringify(r)}return Object.keys(t).length>0?t:void 0}function Ge(e){return e.replace(/^\uFEFF/,``).split(/\r?\n/).map(e=>{if(e.includes(` `))throw Error(`Tab indentation is not supported in tools.yaml`);let t=e.replace(/\s+#.*$/,``);return t.trim().length===0?null:{indent:t.match(/^ */)?.[0].length??0,text:t.trim()}}).filter(e=>e!==null)}function H(e,t,n){let r=e[t];if(!r||r.indent!==n)throw Error(`Invalid indentation in tools.yaml at line ${t+1}`);return r.text.startsWith(`-`)?Ke(e,t,n):qe(e,t,n)}function Ke(e,t,n){let r=[],i=t;for(;i<e.length;){let t=e[i];if(t.indent<n||t.indent!==n||!t.text.startsWith(`-`))break;let a=t.text.slice(1).trim();if(a===``){let t=e[i+1];if(!t||t.indent<=n){r.push(null),i+=1;continue}let[a,o]=H(e,i+1,t.indent);r.push(a),i=o;continue}if(a.includes(`:`)){let[t,o]=U(a),s={};if(o===void 0){let r=e[i+1];if(!r||r.indent<=n)throw Error(`Expected nested value for "${t}" in tools.yaml`);let[a,o]=H(e,i+1,r.indent);s[t]=a,i=o}else s[t]=V(o),i+=1;for(;i<e.length&&e[i].indent>n;){let t=e[i];if(t.indent!==n+2||t.text.startsWith(`-`)){let[n,r]=H(e,i,t.indent);if(!B(n))throw Error(`Expected object entry in tools.yaml at line ${i+1}`);Object.assign(s,n),i=r;continue}let[r,a]=U(t.text);if(a===void 0){let n=e[i+1];if(!n||n.indent<=t.indent)throw Error(`Expected nested value for "${r}" in tools.yaml`);let[a,o]=H(e,i+1,n.indent);s[r]=a,i=o;continue}s[r]=V(a),i+=1}r.push(s);continue}r.push(V(a)),i+=1}return[r,i]}function qe(e,t,n){let r={},i=t;for(;i<e.length;){let t=e[i];if(t.indent<n||t.indent!==n||t.text.startsWith(`-`))break;let[a,o]=U(t.text);if(o===void 0){let t=e[i+1];if(!t||t.indent<=n){r[a]=null,i+=1;continue}let[o,s]=H(e,i+1,t.indent);r[a]=o,i=s;continue}r[a]=V(o),i+=1}return[r,i]}function U(e){let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid tools.yaml entry: "${e}"`);let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();return[n,r===``?void 0:r]}function Je(e){let t=Ge(e);if(t.length===0)return{};let[n]=H(t,0,t[0].indent);return n}function Ye(e){e.inputSchema.properties===void 0&&(e.inputSchema.properties={});let t=e.inputSchema.properties;if(!B(t))throw Error(`Custom tool "${e.name}" inputSchema.properties must be an object`);let n=t[L],r=t[R];if(n!==void 0&&!(B(n)&&n.type===`string`))throw Error(`Custom tool "${e.name}" must define inputSchema.properties.pageId as type "string"`);if(r!==void 0&&!(B(r)&&r.type===`string`))throw Error(`Custom tool "${e.name}" must define inputSchema.properties.browserId as type "string"`);n===void 0&&(t[L]={type:`string`,description:`Browse-tool page ID to run this custom tool against. Either pageId or browserId is required at call time.`}),r===void 0&&(t[R]={type:`string`,description:`Browse-tool browser ID to run this custom tool against when no pageId is supplied. The tool will use the browser's current page or open a new one.`})}async function Xe(e){let t=(0,c.stripTypeScriptTypes)(await(0,o.readFile)(e,`utf8`),{mode:`strip`});return await import(`data:text/javascript;base64,${Buffer.from(t,`utf8`).toString(`base64`)}`)}function Ze(e,t){let n=typeof t.run==`function`?t.run:void 0;if(!n)throw Error(`Custom tool script "${e}" must export a "run" function`);return{execute:n}}function Qe(e,t){return typeof e.suggestionActions==`string`&&e.suggestionActions.trim().length>0?e:{...e,suggestionActions:t}}function $e(e,t){let n=e.content[0];if(n?.type===`text`&&typeof n.text==`string`)try{let r=JSON.parse(n.text);if(B(r)){let i=Qe(r,t);return{...e,content:[{...n,text:JSON.stringify(i,null,2)},...e.content.slice(1)]}}}catch{}return{...e,content:[...e.content,{type:`text`,text:`suggestionActions: ${t}`}]}}function et(e,t,n){if(Ue(t))return n?$e(t,n):t;if(typeof t==`string`)return n?{content:[{type:`text`,text:JSON.stringify({result:t,suggestionActions:n},null,2)}]}:{content:[{type:`text`,text:t}]};if(t===void 0)return n?{content:[{type:`text`,text:JSON.stringify({result:`Custom tool "${e}" completed successfully`,suggestionActions:n},null,2)}]}:{content:[{type:`text`,text:`Custom tool "${e}" completed successfully`}]};let r=B(t)&&n?Qe(t,n):t;return{content:[{type:`text`,text:JSON.stringify(r,null,2)}]}}var tt=class{constructor(t,n,r=new e.g,i){this.pageRegistry=t,this.extensionTaskQueue=n,this.telemetry=r,this.browserService=i}resolveToolPage(t,n,r){if(r.page)return r.page;if(r.mode===`extension`&&this.extensionTaskQueue){let t=new e.v(this.extensionTaskQueue);return t.setTarget(n,r.browserId),t}throw Error(`Custom tool "${t}" requires a supported page context`)}toPageSummary(e,t){return{pageId:t.id,url:t.url,title:t.title,active:e===t.id}}async createPageForBrowser(e,t,n){if(!this.browserService)throw Error(`Custom tool "${e}" requires browser service support to create a page`);let r=this.browserService.getBrowser(t);if(!r)throw Error(`Browser "${t}" not found`);let i=n?.setAsCurrent!==!1;if(r.mode===`extension`||r.mode===`vm`){if(!this.extensionTaskQueue)throw Error(`Custom tool "${e}" requires extension task support to create a page`);let a=this.pageRegistry.registerExtensionPage(t,void 0,n?.url,!1);try{let o=await this.extensionTaskQueue.queueTask(`browser_new_page`,{browserId:t,pageId:a,url:n?.url,setAsCurrent:i},1e4,t);if(!o.success)throw Error(o.error??`Custom tool "${e}" failed to create a page`);let s=o.result?.content[0]?.type===`text`?o.result.content[0].text:void 0,c={};if(typeof s==`string`&&s.length>0)try{c=JSON.parse(s)}catch{c={}}let l=this.pageRegistry.get(a);if(!l)throw Error(`Page "${a}" was not registered`);l.url=c.url??l.url,l.title=c.title??l.title,l.extensionTabId=c.tabId??l.extensionTabId;let u=await this.waitForResolvedPageMetadata(a);return r.pageIds.add(a),(i||!r.currentPageId)&&this.browserService.setCurrentPage(t,a),this.browserService.recordBrowserActivity(t,a),{...this.toPageSummary(r.currentPageId,u),page:this.resolveToolPage(e,a,u)}}catch(e){throw this.pageRegistry.remove(a),e}}let{pageId:a,page:o}=await this.browserService.newPage(t);n?.url&&(await o.goto(n.url),await this.pageRegistry.updateMetadata(a)),i&&this.browserService.setCurrentPage(t,a),this.browserService.recordBrowserActivity(t,a);let s=this.pageRegistry.get(a);if(!s)throw Error(`Page "${a}" was not registered`);return{...this.toPageSummary(r.currentPageId,s),page:this.resolveToolPage(e,a,s)}}async waitForResolvedPageMetadata(e){let t=Date.now(),n=this.pageRegistry.get(e);for(;n&&Date.now()-t<1500;){let t=typeof n.url==`string`&&n.url.length>0,r=typeof n.title==`string`&&n.title.length>0&&n.title!==`Extension Tab`;if(t&&r)return n;await new Promise(e=>setTimeout(e,50)),n=this.pageRegistry.get(e)}if(!n)throw Error(`Page "${e}" was not registered`);return n}async resolveExecutionContext(e,t){let n=typeof t[L]==`string`&&t[L].length>0?t[L]:void 0,r=typeof t[R]==`string`&&t[R].length>0?t[R]:void 0;if(!n&&!r)throw Error(`Custom tool "${e}" requires a string pageId or browserId`);if(n){let t=this.pageRegistry.get(n);if(!t)throw Error(`Page "${n}" not found`);if(r&&t.browserId!==r)throw Error(`Custom tool "${e}" received pageId "${n}" for browser "${t.browserId}", not "${r}"`);let i=this.createBrowserHelper(e,t.browserId);return{pageId:n,pageEntry:t,page:this.resolveToolPage(e,n,t),browser:i}}let i=this.createBrowserHelper(e,r);try{let e=await i.getCurrentPage(),t=this.pageRegistry.get(e.pageId);if(!t)throw Error(`Page "${e.pageId}" not found`);return{pageId:e.pageId,pageEntry:t,page:e.page,browser:i}}catch(e){if(!(e instanceof Error?e.message:String(e)).includes(`has no pages`))throw e;let t=await i.newPage(),n=this.pageRegistry.get(t.pageId);if(!n)throw Error(`Page "${t.pageId}" not found`,{cause:e});return{pageId:t.pageId,pageEntry:n,page:t.page,browser:i}}}createBrowserHelper(e,t){return{browserId:t,mode:this.browserService?.getBrowser(t)?.mode??`extension`,listPages:async()=>{let e=this.browserService?.getBrowser(t);if(!e)throw Error(`Browser "${t}" not found`);return this.pageRegistry.findByBrowser(t).map(t=>this.toPageSummary(e.currentPageId,t))},getPage:async n=>{let r=this.pageRegistry.get(n);if(!r||r.browserId!==t)throw Error(`Page "${n}" not found in browser "${t}"`);let i=this.browserService?.getBrowser(t);return{...this.toPageSummary(i?.currentPageId??null,r),page:this.resolveToolPage(e,n,r)}},getCurrentPage:async()=>{let n=this.browserService?.getBrowser(t);if(!n)throw Error(`Browser "${t}" not found`);let r=n.currentPageId??this.pageRegistry.findByBrowser(t)[0]?.id??void 0;if(!r)throw Error(`Browser "${t}" has no pages`);let i=this.pageRegistry.get(r);if(!i)throw Error(`Page "${r}" not found`);return{...this.toPageSummary(n.currentPageId,i),page:this.resolveToolPage(e,r,i)}},newPage:async n=>this.createPageForBrowser(e,t,n)}}createToolLogger(e,t,n){let r={"browse_tool.tool.name":e,"browse_tool.page.id":t,"browse_tool.browser.id":n.browserId,"browse_tool.execution.mode":n.mode},i=(e,t,n)=>{this.telemetry.log(e,t,{attributes:We({...r,...n?.attributes??{}}),exception:n?.exception})};return{getTraceContext:()=>this.telemetry.getActiveTraceContext(),trace:(e,t)=>i(`trace`,e,t),debug:(e,t)=>i(`debug`,e,t),info:(e,t)=>i(`info`,e,t),warn:(e,t)=>i(`warn`,e,t),error:(e,t)=>i(`error`,e,t),fatal:(e,t)=>i(`fatal`,e,t)}}async listTools(e){return(await this.loadTools(e)).map(({name:e,description:t,suggestionActions:n,inputSchema:r,capabilities:i})=>({name:e,description:t,suggestionActions:n,inputSchema:r,capabilities:i}))}async executeTool(e,t,n){let r=typeof n[L]==`string`?n[L]:void 0,a=typeof n[R]==`string`?n[R]:void 0;return this.telemetry.runInSpan(`browse_tool.custom_tool.execute`,{attributes:{"browse_tool.tool.name":t,"browse_tool.custom_tools.directory":i.default.resolve(e),"browse_tool.page.id":r,"browse_tool.browser.id":a}},async r=>{let a=(await this.loadTools(e)).find(e=>e.name===t);if(!a)throw r?.setStatus({code:u.SpanStatusCode.ERROR,message:`Custom tool "${t}" not found`}),Error(`Custom tool "${t}" not found`);let{pageId:o,pageEntry:s,page:c,browser:l}=await this.resolveExecutionContext(t,n),d=this.createToolLogger(t,o,s);r?.setAttributes({"browse_tool.browser.id":s.browserId,"browse_tool.execution.mode":s.mode,"browse_tool.page.id":o}),z(`Executing custom tool`,{toolName:t,directory:i.default.resolve(e),pageId:o,browserId:s.browserId,mode:s.mode,input:He(n),scriptPath:a.scriptPath});let f;try{f=await a.execute?.({page:c,browser:l,input:n,logger:d})}catch(e){let n=e instanceof Error?e.message:String(e);throw z(`Custom tool execution failed`,{toolName:t,pageId:o,browserId:s.browserId,mode:s.mode,error:n,stack:e instanceof Error?e.stack:void 0}),Error(`Custom tool "${t}" failed on page "${o}": ${n}`,{cause:e})}z(`Custom tool execution completed`,{toolName:t,pageId:o,browserId:s.browserId,mode:s.mode,result:He(f)});let p=et(t,f,a.suggestionActions),m=p.isError?p.content[0]?.text:void 0;return m&&r?.setStatus({code:u.SpanStatusCode.ERROR,message:m}),p})}async loadTools(e){let t=i.default.resolve(e),n=i.default.join(t,`tools.yaml`),r=Je(await(0,o.readFile)(n,`utf8`).catch(e=>{throw Error(`Failed to read custom tool manifest at "${n}": ${e instanceof Error?e.message:String(e)}`)})),a=Be.parse(r);return Promise.all(a.tools.map(async e=>{Ye(e);let n=i.default.resolve(t,e.script);await(0,o.stat)(n).catch(t=>{throw Error(`Custom tool "${e.name}" script not found at "${n}": ${t instanceof Error?t.message:String(t)}`)});let r=Ze(n,await Xe(n));return{name:e.name,description:e.description,suggestionActions:e.suggestionActions,inputSchema:e.inputSchema,capabilities:e.capabilities,scriptPath:n,...r}}))}};function nt(t){let n=new g.Hono;return n.get(`/browsers`,async n=>{try{let r=t.get(e.T.BrowserService),i=t.get(e.T.PageRegistry),a=r.listBrowsers(),o=i.list(),s=a.map(e=>{let t=o.filter(t=>t.browserId===e.id);return{id:e.id,profileName:e.profileName,currentPageId:e.currentPageId,createdAt:e.createdAt.toISOString(),pages:t.map(e=>({id:e.id,url:e.url,title:e.title,createdAt:e.createdAt.toISOString()}))}}),c={totalBrowsers:a.length,totalPages:o.length};return n.json({browsers:s,stats:c})}catch(e){return console.error(`Failed to list browsers:`,e),n.json({error:`Failed to list browsers`,message:e instanceof Error?e.message:String(e)},500)}}),n.delete(`/browsers`,async n=>{try{return await t.get(e.T.BrowserService).closeAll(),n.json({success:!0,message:`All browsers closed`})}catch(e){return console.error(`Failed to close all browsers:`,e),n.json({error:`Failed to close all browsers`,message:e instanceof Error?e.message:String(e)},500)}}),n.delete(`/browsers/:id`,async n=>{try{let r=n.req.param(`id`),i=t.get(e.T.BrowserService);return i.getBrowser(r)?(await i.closeBrowser(r),n.json({success:!0,message:`Browser "${r}" closed`})):n.json({error:`Browser "${r}" not found`},404)}catch(e){return console.error(`Failed to close browser:`,e),n.json({error:`Failed to close browser`,message:e instanceof Error?e.message:String(e)},500)}}),n.delete(`/pages/:id`,async n=>{try{let r=n.req.param(`id`),i=t.get(e.T.PageRegistry).get(r);return i?i.page?(await i.page.close(),n.json({success:!0,message:`Page "${r}" closed`})):n.json({error:`Page "${r}" is in extension mode and cannot be closed via API`},400):n.json({error:`Page "${r}" not found`},404)}catch(e){return console.error(`Failed to close page:`,e),n.json({error:`Failed to close page`,message:e instanceof Error?e.message:String(e)},500)}}),n}function rt(e){return e.toLocaleString(`en-US`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}function it(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),r=Math.floor(n/60);return r>0?`${r}h ${n%60}m`:n>0?`${n}m ${t%60}s`:`${t}s`}function at(e){return it(new Date().getTime()-e.getTime())}const ot=`table-container`,st=`stat-card`,ct=`browser-row`,lt=`page-row`,W=`status-active`,ut=`url-cell`,G=`timestamp-cell`,K=`actions-cell`,q=`kill-btn`,dt=`empty-state`;function ft({browsers:e}){return e.length===0?(0,x.jsx)(`div`,{class:ot,children:(0,x.jsxs)(`div`,{class:dt,children:[(0,x.jsx)(`h3`,{children:`No Active Browsers`}),(0,x.jsx)(`p`,{children:`Launch a browser using MCP tools to see it here.`})]})}):(0,x.jsx)(`div`,{class:ot,children:(0,x.jsxs)(`table`,{class:`browser-table`,children:[(0,x.jsx)(`thead`,{children:(0,x.jsxs)(`tr`,{children:[(0,x.jsx)(`th`,{children:`ID`}),(0,x.jsx)(`th`,{children:`Status`}),(0,x.jsx)(`th`,{children:`URL / Title`}),(0,x.jsx)(`th`,{children:`Created`}),(0,x.jsx)(`th`,{children:`Age`}),(0,x.jsx)(`th`,{children:`Actions`})]})}),(0,x.jsx)(`tbody`,{id:`browser-table-body`,children:e.map(e=>(0,x.jsxs)(b.Fragment,{children:[(0,x.jsxs)(`tr`,{class:ct,children:[(0,x.jsx)(`td`,{children:e.id}),(0,x.jsx)(`td`,{children:(0,x.jsxs)(`span`,{class:W,children:[e.pages.length,` page`,e.pages.length===1?``:`s`]})}),(0,x.jsx)(`td`,{children:e.profileName||`Default Profile`}),(0,x.jsx)(`td`,{class:G,children:rt(e.createdAt)}),(0,x.jsx)(`td`,{class:G,children:at(e.createdAt)}),(0,x.jsx)(`td`,{class:K,children:(0,x.jsx)(`button`,{type:`button`,class:q,onclick:`dashboard.killBrowser('${e.id}')`,children:`Kill`})})]}),e.pages.map(t=>(0,x.jsxs)(`tr`,{class:lt,children:[(0,x.jsxs)(`td`,{children:[t.id,e.currentPageId===t.id&&` (active)`]}),(0,x.jsx)(`td`,{children:(0,x.jsx)(`span`,{class:W,children:`Open`})}),(0,x.jsx)(`td`,{class:ut,title:t.url,children:t.title||t.url||`about:blank`}),(0,x.jsx)(`td`,{class:G,children:rt(t.createdAt)}),(0,x.jsx)(`td`,{class:G,children:at(t.createdAt)}),(0,x.jsx)(`td`,{class:K,children:(0,x.jsx)(`button`,{type:`button`,class:q,onclick:`dashboard.killPage('${t.id}')`,children:`Close`})})]},t.id))]},e.id))})]})})}function pt({stats:e}){return(0,x.jsxs)(`div`,{class:`stats-container`,children:[(0,x.jsxs)(`div`,{class:st,children:[(0,x.jsx)(`h3`,{children:`Active Browsers`}),(0,x.jsx)(`div`,{class:`value`,children:e.totalBrowsers})]}),(0,x.jsxs)(`div`,{class:st,children:[(0,x.jsx)(`h3`,{children:`Active Pages`}),(0,x.jsx)(`div`,{class:`value`,children:e.totalPages})]})]})}function mt({browsers:e,stats:t}){return(0,x.jsxs)(`div`,{class:`dashboard-container`,children:[(0,x.jsxs)(`div`,{class:`dashboard-header`,children:[(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`h1`,{children:`Playwright MCP Dashboard`}),(0,x.jsx)(`p`,{children:`Browser automation management (auto-refresh every 3 seconds)`})]}),(0,x.jsxs)(`div`,{children:[(0,x.jsx)(`button`,{type:`button`,id:`refresh-btn`,class:`btn refresh-btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`}),(0,x.jsx)(`button`,{type:`button`,id:`kill-all-btn`,class:`btn btn-danger`,onclick:`dashboard.killAllBrowsers()`,children:`Kill All Browsers`})]})]}),(0,x.jsx)(pt,{stats:t}),(0,x.jsx)(ft,{browsers:e}),(0,x.jsx)(`script`,{children:(0,y.raw)(`
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,S as t,T as n,_ as r,a as i,b as a,c as o,d as s,f as c,g as l,h as u,i as d,l as f,m as p,n as m,o as h,p as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./streamable-http-B4RiCNHp.mjs";import{stripTypeScriptTypes as w}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as T,PortRegistryService as E}from"@agimon-ai/foundation-port-registry";import{createProcessLease as ee,resolveSiblingRegistryPath as te}from"@agimon-ai/foundation-process-registry";import{Container as D,ContainerModule as O}from"inversify";import k from"node:path";import{existsSync as A,readFileSync as j,statSync as ne}from"node:fs";import{readFile as M,stat as re}from"node:fs/promises";import{homedir as ie}from"node:os";import{z as N}from"zod";import{SpanStatusCode as P}from"@opentelemetry/api";import{Server as ae}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as oe,ListToolsRequestSchema as se}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as ce}from"@modelcontextprotocol/sdk/server/stdio.js";import{Command as F,Option as le}from"commander";import{serve as ue}from"@hono/node-server";import{Hono as I}from"hono";import{cors as de}from"hono/cors";import{createNodeWebSocket as fe}from"@hono/node-ws";import{raw as pe}from"hono/html";import{Fragment as me}from"hono/jsx";import{jsx as L,jsxs as R}from"hono/jsx/jsx-runtime";var he=`0.10.4`;const ge=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`;function _e(e,t){if(!ge)return;let n=t?` ${JSON.stringify(t)}`:``;console.log(`[ExtensionRecordingDebug] ${e}${n}`)}function ve(e){let t=new I,i;try{i=e.get(n.TelemetryService)}catch{i=new l}return t.get(`/telemetry-config`,e=>{try{let t=new URL(e.req.url).origin;return e.json(r(process.env,t))}catch(t){return e.json({enabled:!1,error:t instanceof Error?t.message:String(t)},500)}}),t.get(`/tasks`,t=>{try{let r=e.get(n.ExtensionTaskQueue).getNextTask();return r?t.json({task:{id:r.id,tool:r.tool,arguments:r.arguments,telemetry:r.telemetry}}):t.json({})}catch(e){return t.json({error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/result`,async t=>{try{let r=await t.req.json();if(!r.taskId)return t.json({success:!1,error:`Missing taskId in request body`},400);let i=e.get(n.ExtensionTaskQueue),a=e.get(n.BrowserService),o={taskId:r.taskId,success:r.success,result:r.result,error:r.error},s=i.submitResult(o);return s?(s.browserId&&a.recordBrowserActivity(s.browserId,s.pageId),t.json({success:!0})):t.json({success:!1,error:`Task ${r.taskId} not found or already completed`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.get(`/status`,t=>{try{let r=e.get(n.ExtensionTaskQueue),i=r.getConnectionStatus(),a={connected:i.connected,lastPollAt:i.lastPollAt?.toISOString(),lastResultAt:i.lastResultAt?.toISOString(),pendingTasks:i.pendingTasks,queueSize:r.queueSize};return t.json(a)}catch(e){return t.json({connected:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/register`,async t=>{try{let r=await t.req.json();if(!r.browserId)return t.json({success:!1,error:`Missing browserId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).register(r);return t.json({success:!0,session:{id:i.id,browserId:i.browserId,controlMode:i.controlMode,createdAt:i.createdAt.toISOString()}})}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/heartbeat`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).heartbeat(r);return i?t.json({success:!0,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested,lastHeartbeatAt:i.lastHeartbeatAt.toISOString()}}):t.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/tab-mapped`,async t=>{try{let r=await t.req.json();if(!r.pageId||typeof r.tabId!=`number`)return t.json({success:!1,error:`Missing pageId or tabId in request body`},400);let i=e.get(n.PageRegistry),a=e.get(n.BrowserService),o=i.get(r.pageId);return o?(o.extensionTabId=r.tabId,a.recordBrowserActivity(o.browserId,r.pageId),t.json({success:!0})):t.json({success:!1,error:`Page ${r.pageId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/recording`,async t=>{try{let r=await t.req.json();if(!r.browserId)return t.json({success:!1,error:`Missing browserId in request body`},400);let i=e.get(n.BrowserService),a=await i.persistExtensionRecordingArtifact(r.browserId,r.videoBase64);return _e(`artifact received`,{browserId:r.browserId,videoBase64Size:r.videoBase64?.length??0,persisted:a}),a?(i.recordBrowserActivity(r.browserId),t.json({success:!0})):t.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/recording/chunk`,async t=>{try{let r=await t.req.json();if(!r.browserId||!r.chunkBase64)return t.json({success:!1,error:`Missing browserId or chunkBase64 in request body`},400);let a=e.get(n.BrowserService),o=await a.persistExtensionRecordingChunk(r.browserId,r.chunkBase64);return _e(`chunk received`,{browserId:r.browserId,chunkIndex:r.chunkIndex,mimeType:r.mimeType,persisted:!!o,chunkBase64Size:r.chunkBase64.length}),o?(i.log(`debug`,`extension recording chunk received`,{attributes:{"browse_tool.extension.recording.chunk_received":!0,"browse_tool.browser.id":r.browserId,"browse_tool.recording.chunk_bytes":o.chunkBytes,"browse_tool.recording.total_bytes":o.totalBytes,"browse_tool.recording.chunk_count":o.chunkCount,...typeof r.chunkIndex==`number`?{"browse_tool.recording.chunk_index":r.chunkIndex}:{},...typeof r.mimeType==`string`?{"browse_tool.recording.mime_type":r.mimeType}:{}}}),a.recordBrowserActivity(r.browserId),t.json({success:!0})):t.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/browser-log`,async e=>{try{let t=await e.req.json();return!t.message||typeof t.message!=`string`?e.json({success:!1,error:`Missing message in request body`},400):(i.log(t.level??`info`,t.message,{attributes:{"browse_tool.extension.log_relay":!0,...typeof t.attributes==`object`&&t.attributes!==null?t.attributes:{}}}),_e(`browser log relayed`,{level:t.level??`info`,message:t.message,attributes:t.attributes}),e.json({success:!0}))}catch(t){return e.json({success:!1,error:t instanceof Error?t.message:String(t)},500)}}),t.post(`/handoff`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).requestHandoff(r);return i?t.json({success:!0,message:`Handoff requested. AI will take control when ready.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):t.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/handoff/acknowledge`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).acknowledgeHandoff(r.sessionId);return i?t.json({success:!0,message:`Handoff acknowledged. AI now has control.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):t.json({success:!1,error:`Session ${r.sessionId} not found or no handoff pending`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.get(`/sessions`,t=>{try{let r=e.get(n.ExtensionSessionRegistry).listSessions();return t.json({sessions:r.map(e=>({id:e.id,browserId:e.browserId,tabId:e.tabId,currentUrl:e.currentUrl,controlMode:e.controlMode,activeSpecPath:e.activeSpecPath,handoffRequested:e.handoffRequested,createdAt:e.createdAt.toISOString(),lastHeartbeatAt:e.lastHeartbeatAt.toISOString()}))})}catch(e){return t.json({error:e instanceof Error?e.message:String(e)},500)}}),t}function ye(e){if(e==null||e===``)return;let t=typeof e==`number`?e:typeof e==`string`?Number.parseInt(e,10):NaN;if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`Invalid port: ${e}`);return t}const be=[`pnpm-workspace.yaml`,`nx.json`,`.git`],xe=`browse-tool-chrome`,Se=`tool`,Ce=process.env.NODE_ENV||`development`,we=`127.0.0.1`;function Te(e=process.cwd()){let t=k.resolve(e);for(;;){for(let e of be)if(A(k.join(t,e)))return t;let e=k.dirname(t);if(e===t)return process.cwd();t=e}}function Ee(){return new E(process.env.PORT_REGISTRY_PATH)}async function De(e,t,n){let r=Ee(),i=await r.reservePort({repositoryPath:e,serviceName:xe,serviceType:Se,environment:Ce,preferredPort:t,portRange:n,pid:process.pid,host:we,force:!0,metadata:{transport:`stdio`,mode:`chrome-serve`}});if(!i.success||!i.record)throw Error(i.error||`Failed to reserve port ${t}`);let a=!1;return{port:i.record.port,release:async()=>{if(a)return;a=!0;let n=await r.releasePort({repositoryPath:e,serviceName:xe,serviceType:Se,environment:Ce,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port ${t}`)}}}function Oe(){return new O(e=>{e.bind(n.ExtensionTaskQueue).to(u).inSingletonScope(),e.bind(n.ExtensionToolDelegator).to(p).inSingletonScope()})}function ke(e){let t=new ae({name:`browse-tool-chrome`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e.getSupportedTools().map(e=>({name:e,description:`Browser automation tool (Chrome extension mode): ${e}`,inputSchema:{type:`object`,properties:{},additionalProperties:!0}}));return t.setRequestHandler(se,async()=>({tools:n})),t.setRequestHandler(oe,async t=>{let{name:n,arguments:r}=t.params;return await e.executeTool(n,r||{})}),t}const Ae=new F(`chrome-serve`).description(`[DEPRECATED] Start MCP server with Chrome extension HTTP polling for bot-detection-free browser automation`).option(`-p, --port <port>`,`HTTP server port for extension polling`).option(`-v, --verbose`,`Enable verbose output`,!1).option(`--wait-for-extension`,`Wait for extension to connect before accepting MCP requests`,!1).action(async e=>{console.error(``),console.error(`╔════════════════════════════════════════════════════════════════╗`),console.error(`║ DEPRECATED: chrome-serve is deprecated. ║`),console.error(`║ Use mcp-serve instead - extension routes are now ║`),console.error(`║ automatically available in the HTTP server at /extension/*. ║`),console.error(`╚════════════════════════════════════════════════════════════════╝`),console.error(``);let t,r;try{let i=ye(e.port),a=Te(process.cwd()),o=process.env.PORT_REGISTRY_PATH;o&&(process.env.PROCESS_REGISTRY_PATH=te(o,`processes.json`)),t=await De(a,i??T.min,i?{min:i,max:i}:T);let s=t.port;r=await ee({repositoryPath:a,serviceName:xe,serviceType:Se,environment:Ce,pid:process.pid,port:s,host:we,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`chrome-serve`,waitForExtension:e.waitForExtension}}),e.verbose&&(console.error(`Chrome Extension MCP Server starting...`),console.error(` HTTP Port: ${s}`),console.error(` Wait for extension: ${e.waitForExtension}`));let c=new D({defaultScope:`Singleton`});c.load(Oe());let l=c.get(n.ExtensionTaskQueue),u=c.get(n.ExtensionToolDelegator),d=new I;d.use(`*`,de({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`]}));let f=ve(c);d.route(`/extension`,f),d.get(`/health`,e=>{let t=l.getConnectionStatus();return e.json({status:`healthy`,service:`browse-tool-chrome`,extension:t})});let p=ue({fetch:d.fetch,port:s});p.on(`error`,e=>{e.code===`EADDRINUSE`?(console.error(`Error [PORT_IN_USE]: Port ${s} is already in use.`),console.error(`Recovery: Try a different port with --port <port>`)):console.error(`Error [SERVER_ERROR]: HTTP server error: ${e.message}`),process.exit(1)}),console.error(`HTTP server started on port ${s}`),console.error(`Waiting for Chrome extension to connect...`),console.error(`Extension should poll: http://localhost:${s}/extension/tasks`),e.waitForExtension&&(console.error(`Waiting for extension connection before starting MCP...`),await new Promise(e=>{let t=setInterval(()=>{l.getConnectionStatus().connected&&(clearInterval(t),console.error(`Extension connected!`),e())},500)}));let m=ke(u),h=new ce;await m.connect(h),console.error(`Chrome extension MCP server started on stdio`);let g=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`);try{l.clearAllTasks(`Server shutting down`),await h.close(),p.close(),await r.release({kill:!1}),await t.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>g(`SIGINT`)),process.on(`SIGTERM`,()=>g(`SIGTERM`))}catch(e){if(r||t)try{await r?.release(),await t?.release()}catch{}let n=e instanceof Error?e.message:String(e);console.error(`Error [SERVER_ERROR]: Failed to start Chrome extension MCP server: ${n}`),console.error(`Recovery: Check that the port is available and try again.`),process.exit(1)}}),je=`BROWSE_TOOL_CONFIG`,Me=`.browse-tool`,Ne=`config.json`,Pe=[`json`,`text`,`quiet`],Fe={commands:{},tools:{}},Ie=N.record(N.string(),N.unknown()),Le=N.object({mcpServe:N.object({type:N.string().optional(),browser:N.string().optional(),headless:N.boolean().optional(),profile:N.string().optional(),mode:N.string().optional(),host:N.string().optional(),port:N.coerce.number().int().positive().optional(),httpPort:N.coerce.number().int().positive().optional(),tags:N.string().optional(),exclude:N.string().optional(),customTools:N.string().optional(),chromeForTestingPath:N.string().optional(),snippetsDir:N.string().optional(),registryPath:N.string().optional(),registryDir:N.string().optional(),pidsDir:N.string().optional(),profilesDir:N.string().optional(),proxyConfigDir:N.string().optional()}).partial().optional(),httpServe:N.object({port:N.coerce.number().int().positive().optional(),headless:N.boolean().optional(),idleTimeout:N.coerce.number().positive().optional(),host:N.string().optional(),registryDir:N.string().optional(),registryPath:N.string().optional(),pidsDir:N.string().optional(),profilesDir:N.string().optional(),snippetsDir:N.string().optional(),proxyConfigDir:N.string().optional()}).partial().optional(),exec:N.object({format:N.enum(Pe).optional(),color:N.boolean().optional(),port:N.coerce.number().int().positive().optional()}).partial().optional(),tools:N.object({format:N.enum(Pe).optional(),color:N.boolean().optional(),port:N.coerce.number().int().positive().optional()}).partial().optional(),status:N.record(N.string(),N.unknown()).optional(),stop:N.record(N.string(),N.unknown()).optional()}),Re=N.object({commands:Le.default({}),tools:N.record(N.string(),Ie).default({})});let z={config:Fe};function ze(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n===`--config`)return e[t+1];if(n.startsWith(`--config=`))return n.slice(9)}}function Be(e){let t=k.resolve(e);if(!A(t))throw Error(`Config path not found: ${t}`);return ne(t).isDirectory()?k.join(t,Ne):t}function Ve(e,t){let n=t.env??process.env,r=t.cwd??process.cwd(),i=t.homeDir??ie(),a=ze(e);if(a)return Be(a);if(n[je])return Be(n[je]);let o=k.join(r,Me,Ne);if(A(o))return o;let s=k.join(i,Me,Ne);if(A(s))return s}function He(e){let t=j(e,`utf8`),n=JSON.parse(t);return Re.parse(n)}function Ue(e,t={}){let n=Ve(e,t);return n?{configPath:n,config:He(n)}:{config:Fe}}function We(e,t={}){return z=Ue(e,t),z}function B(e){return(z.config.commands??{})[e]??{}}function Ge(e){return z.config.tools?.[e]??{}}function V(e,t,n,r,i){let a=e.getOptionValueSourceWithGlobals(t);return a!==void 0&&a!==`default`&&a!==`implied`?n:i===void 0?r===void 0?n:r:i}var Ke=class{port;exactPort;timeout;serverPort=null;constructor(e={}){this.port=e.port??s,this.exactPort=e.exactPort??!1,this.timeout=e.timeout??6e4}async ensureServer(){if(this.serverPort!==null)return this.serverPort;let e=await h().get(n.HttpServerManager).ensureRunning(this.port,{exactPort:this.exactPort});if(!e.running||!e.port)throw Error(e.error??`Failed to start HTTP server. Try running "browse-tool http-serve" manually.`);return this.serverPort=e.port,e.port}async getBaseUrl(){return`http://localhost:${await this.ensureServer()}`}async listTools(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/tools`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async listCustomTools(e){let t=await this.getBaseUrl(),n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let r=new URL(`/custom-tools`,t);r.searchParams.set(`dir`,e);let i=await fetch(r,{method:`GET`,headers:{Accept:`application/json`},signal:n.signal});if(!i.ok)throw Error(`HTTP ${i.status}: ${i.statusText}`);let a=await i.json();if(a.error)throw Error(a.error);return a.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(r)}}async listBrowsers(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/browsers`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.browsers}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async execute(e,t){let n=await this.getBaseUrl(),r=e===`run_spec`?void 0:this.timeout,i=new AbortController,a=r===void 0?void 0:setTimeout(()=>i.abort(),r);try{let a=await fetch(`${n}/execute`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:e,arguments:t}),signal:r===void 0?void 0:i.signal});if(!a.ok){let e=await a.text();throw Error(`HTTP ${a.status}: ${e||a.statusText}`)}let o=await a.json();return o.success?o.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:o.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{a&&clearTimeout(a)}}async executeCustomTool(e,t,n){let r=await this.getBaseUrl(),i=new AbortController,a=setTimeout(()=>i.abort(),this.timeout);try{let a=new URL(`/custom-tools`,r);a.searchParams.set(`dir`,e);let o=await fetch(a,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:t,arguments:n}),signal:i.signal});if(!o.ok){let e=await o.text();throw Error(`HTTP ${o.status}: ${e||o.statusText}`)}let s=await o.json();return s.success?s.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:s.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(a)}}wrapConnectionError(e){return e instanceof Error?e.message.includes(`ECONNREFUSED`)||e.message.includes(`fetch failed`)?Error(`Cannot connect to browse-tool HTTP server.\n\nTry one of the following:\n 1. Start the server: browse-tool http-serve\n 2. Check if another process is using port ${this.port}\n 3. Run with a different port: browse-tool --port 3201 <command>\n\nOriginal error: ${e.message}`,{cause:e}):e:Error(String(e),{cause:e})}};function H(e){return new Ke(e)}const qe={reset:`\x1B[0m`,red:`\x1B[31m`,green:`\x1B[32m`,yellow:`\x1B[33m`,blue:`\x1B[34m`,magenta:`\x1B[35m`,cyan:`\x1B[36m`,gray:`\x1B[90m`,bold:`\x1B[1m`};function U(e,t,n){return n?`${qe[t]}${e}${qe.reset}`:e}function Je(e,t){let{format:n,color:r}=t;return n===`quiet`?``:n===`json`?Ye(e):Xe(e,r)}function Ye(e){return JSON.stringify(e,null,2)}function Xe(e,t){let n=[];e.isError&&n.push(U(`Error:`,`red`,t));for(let r of e.content)r.type===`text`?n.push(Ze(r,t)):r.type===`image`?n.push(et(r,t)):n.push(U(`[Unknown content type: ${r.type}]`,`yellow`,t));return n.join(`
|
|
2
|
+
import{C as e,S as t,T as n,_ as r,a as i,b as a,c as o,d as s,f as c,g as l,h as u,i as d,l as f,m as p,n as m,o as h,p as g,s as _,t as v,u as y,v as b,w as x,x as S,y as C}from"./streamable-http-ajTIeOoU.mjs";import{stripTypeScriptTypes as w}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as T,PortRegistryService as E}from"@agimon-ai/foundation-port-registry";import{createProcessLease as ee,resolveSiblingRegistryPath as te}from"@agimon-ai/foundation-process-registry";import{Container as D,ContainerModule as O}from"inversify";import k from"node:path";import{existsSync as A,readFileSync as j,statSync as ne}from"node:fs";import{readFile as M,stat as re}from"node:fs/promises";import{homedir as ie}from"node:os";import{z as N}from"zod";import{SpanStatusCode as P}from"@opentelemetry/api";import{Server as ae}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as oe,ListToolsRequestSchema as se}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as ce}from"@modelcontextprotocol/sdk/server/stdio.js";import{Command as F,Option as le}from"commander";import{serve as ue}from"@hono/node-server";import{Hono as I}from"hono";import{cors as de}from"hono/cors";import{createNodeWebSocket as fe}from"@hono/node-ws";import{raw as pe}from"hono/html";import{Fragment as me}from"hono/jsx";import{jsx as L,jsxs as R}from"hono/jsx/jsx-runtime";var he=`0.11.0`;const ge=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`;function _e(e,t){if(!ge)return;let n=t?` ${JSON.stringify(t)}`:``;console.log(`[ExtensionRecordingDebug] ${e}${n}`)}function ve(e){let t=new I,i;try{i=e.get(n.TelemetryService)}catch{i=new l}return t.get(`/telemetry-config`,e=>{try{let t=new URL(e.req.url).origin;return e.json(r(process.env,t))}catch(t){return e.json({enabled:!1,error:t instanceof Error?t.message:String(t)},500)}}),t.get(`/tasks`,t=>{try{let r=e.get(n.ExtensionTaskQueue).getNextTask();return r?t.json({task:{id:r.id,tool:r.tool,arguments:r.arguments,telemetry:r.telemetry}}):t.json({})}catch(e){return t.json({error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/result`,async t=>{try{let r=await t.req.json();if(!r.taskId)return t.json({success:!1,error:`Missing taskId in request body`},400);let i=e.get(n.ExtensionTaskQueue),a=e.get(n.BrowserService),o={taskId:r.taskId,success:r.success,result:r.result,error:r.error},s=i.submitResult(o);return s?(s.browserId&&a.recordBrowserActivity(s.browserId,s.pageId),t.json({success:!0})):t.json({success:!1,error:`Task ${r.taskId} not found or already completed`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.get(`/status`,t=>{try{let r=e.get(n.ExtensionTaskQueue),i=r.getConnectionStatus(),a={connected:i.connected,lastPollAt:i.lastPollAt?.toISOString(),lastResultAt:i.lastResultAt?.toISOString(),pendingTasks:i.pendingTasks,queueSize:r.queueSize};return t.json(a)}catch(e){return t.json({connected:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/register`,async t=>{try{let r=await t.req.json();if(!r.browserId)return t.json({success:!1,error:`Missing browserId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).register(r);return t.json({success:!0,session:{id:i.id,browserId:i.browserId,controlMode:i.controlMode,createdAt:i.createdAt.toISOString()}})}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/heartbeat`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).heartbeat(r);return i?t.json({success:!0,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested,lastHeartbeatAt:i.lastHeartbeatAt.toISOString()}}):t.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/tab-mapped`,async t=>{try{let r=await t.req.json();if(!r.pageId||typeof r.tabId!=`number`)return t.json({success:!1,error:`Missing pageId or tabId in request body`},400);let i=e.get(n.PageRegistry),a=e.get(n.BrowserService),o=i.get(r.pageId);return o?(o.extensionTabId=r.tabId,a.recordBrowserActivity(o.browserId,r.pageId),t.json({success:!0})):t.json({success:!1,error:`Page ${r.pageId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/recording`,async t=>{try{let r=await t.req.json();if(!r.browserId)return t.json({success:!1,error:`Missing browserId in request body`},400);let i=e.get(n.BrowserService),a=await i.persistExtensionRecordingArtifact(r.browserId,r.videoBase64);return _e(`artifact received`,{browserId:r.browserId,videoBase64Size:r.videoBase64?.length??0,persisted:a}),a?(i.recordBrowserActivity(r.browserId),t.json({success:!0})):t.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/recording/chunk`,async t=>{try{let r=await t.req.json();if(!r.browserId||!r.chunkBase64)return t.json({success:!1,error:`Missing browserId or chunkBase64 in request body`},400);let a=e.get(n.BrowserService),o=await a.persistExtensionRecordingChunk(r.browserId,r.chunkBase64);return _e(`chunk received`,{browserId:r.browserId,chunkIndex:r.chunkIndex,mimeType:r.mimeType,persisted:!!o,chunkBase64Size:r.chunkBase64.length}),o?(i.log(`debug`,`extension recording chunk received`,{attributes:{"browse_tool.extension.recording.chunk_received":!0,"browse_tool.browser.id":r.browserId,"browse_tool.recording.chunk_bytes":o.chunkBytes,"browse_tool.recording.total_bytes":o.totalBytes,"browse_tool.recording.chunk_count":o.chunkCount,...typeof r.chunkIndex==`number`?{"browse_tool.recording.chunk_index":r.chunkIndex}:{},...typeof r.mimeType==`string`?{"browse_tool.recording.mime_type":r.mimeType}:{}}}),a.recordBrowserActivity(r.browserId),t.json({success:!0})):t.json({success:!1,error:`Browser "${r.browserId}" has no active recording target`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/browser-log`,async e=>{try{let t=await e.req.json();return!t.message||typeof t.message!=`string`?e.json({success:!1,error:`Missing message in request body`},400):(i.log(t.level??`info`,t.message,{attributes:{"browse_tool.extension.log_relay":!0,...typeof t.attributes==`object`&&t.attributes!==null?t.attributes:{}}}),_e(`browser log relayed`,{level:t.level??`info`,message:t.message,attributes:t.attributes}),e.json({success:!0}))}catch(t){return e.json({success:!1,error:t instanceof Error?t.message:String(t)},500)}}),t.post(`/handoff`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).requestHandoff(r);return i?t.json({success:!0,message:`Handoff requested. AI will take control when ready.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):t.json({success:!1,error:`Session ${r.sessionId} not found`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.post(`/handoff/acknowledge`,async t=>{try{let r=await t.req.json();if(!r.sessionId)return t.json({success:!1,error:`Missing sessionId in request body`},400);let i=e.get(n.ExtensionSessionRegistry).acknowledgeHandoff(r.sessionId);return i?t.json({success:!0,message:`Handoff acknowledged. AI now has control.`,session:{id:i.id,controlMode:i.controlMode,handoffRequested:i.handoffRequested}}):t.json({success:!1,error:`Session ${r.sessionId} not found or no handoff pending`},404)}catch(e){return t.json({success:!1,error:e instanceof Error?e.message:String(e)},500)}}),t.get(`/sessions`,t=>{try{let r=e.get(n.ExtensionSessionRegistry).listSessions();return t.json({sessions:r.map(e=>({id:e.id,browserId:e.browserId,tabId:e.tabId,currentUrl:e.currentUrl,controlMode:e.controlMode,activeSpecPath:e.activeSpecPath,handoffRequested:e.handoffRequested,createdAt:e.createdAt.toISOString(),lastHeartbeatAt:e.lastHeartbeatAt.toISOString()}))})}catch(e){return t.json({error:e instanceof Error?e.message:String(e)},500)}}),t}function ye(e){if(e==null||e===``)return;let t=typeof e==`number`?e:typeof e==`string`?Number.parseInt(e,10):NaN;if(!Number.isInteger(t)||t<=0||t>65535)throw Error(`Invalid port: ${e}`);return t}const be=[`pnpm-workspace.yaml`,`nx.json`,`.git`],xe=`browse-tool-chrome`,Se=`tool`,Ce=process.env.NODE_ENV||`development`,we=`127.0.0.1`;function Te(e=process.cwd()){let t=k.resolve(e);for(;;){for(let e of be)if(A(k.join(t,e)))return t;let e=k.dirname(t);if(e===t)return process.cwd();t=e}}function Ee(){return new E(process.env.PORT_REGISTRY_PATH)}async function De(e,t,n){let r=Ee(),i=await r.reservePort({repositoryPath:e,serviceName:xe,serviceType:Se,environment:Ce,preferredPort:t,portRange:n,pid:process.pid,host:we,force:!0,metadata:{transport:`stdio`,mode:`chrome-serve`}});if(!i.success||!i.record)throw Error(i.error||`Failed to reserve port ${t}`);let a=!1;return{port:i.record.port,release:async()=>{if(a)return;a=!0;let n=await r.releasePort({repositoryPath:e,serviceName:xe,serviceType:Se,environment:Ce,pid:process.pid});if(!n.success&&!n.error?.includes(`No matching registry entry`))throw Error(n.error||`Failed to release port ${t}`)}}}function Oe(){return new O(e=>{e.bind(n.ExtensionTaskQueue).to(u).inSingletonScope(),e.bind(n.ExtensionToolDelegator).to(p).inSingletonScope()})}function ke(e){let t=new ae({name:`browse-tool-chrome`,version:`0.1.0`},{capabilities:{tools:{}}}),n=e.getSupportedTools().map(e=>({name:e,description:`Browser automation tool (Chrome extension mode): ${e}`,inputSchema:{type:`object`,properties:{},additionalProperties:!0}}));return t.setRequestHandler(se,async()=>({tools:n})),t.setRequestHandler(oe,async t=>{let{name:n,arguments:r}=t.params;return await e.executeTool(n,r||{})}),t}const Ae=new F(`chrome-serve`).description(`[DEPRECATED] Start MCP server with Chrome extension HTTP polling for bot-detection-free browser automation`).option(`-p, --port <port>`,`HTTP server port for extension polling`).option(`-v, --verbose`,`Enable verbose output`,!1).option(`--wait-for-extension`,`Wait for extension to connect before accepting MCP requests`,!1).action(async e=>{console.error(``),console.error(`╔════════════════════════════════════════════════════════════════╗`),console.error(`║ DEPRECATED: chrome-serve is deprecated. ║`),console.error(`║ Use mcp-serve instead - extension routes are now ║`),console.error(`║ automatically available in the HTTP server at /extension/*. ║`),console.error(`╚════════════════════════════════════════════════════════════════╝`),console.error(``);let t,r;try{let i=ye(e.port),a=Te(process.cwd()),o=process.env.PORT_REGISTRY_PATH;o&&(process.env.PROCESS_REGISTRY_PATH=te(o,`processes.json`)),t=await De(a,i??T.min,i?{min:i,max:i}:T);let s=t.port;r=await ee({repositoryPath:a,serviceName:xe,serviceType:Se,environment:Ce,pid:process.pid,port:s,host:we,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`chrome-serve`,waitForExtension:e.waitForExtension}}),e.verbose&&(console.error(`Chrome Extension MCP Server starting...`),console.error(` HTTP Port: ${s}`),console.error(` Wait for extension: ${e.waitForExtension}`));let c=new D({defaultScope:`Singleton`});c.load(Oe());let l=c.get(n.ExtensionTaskQueue),u=c.get(n.ExtensionToolDelegator),d=new I;d.use(`*`,de({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`]}));let f=ve(c);d.route(`/extension`,f),d.get(`/health`,e=>{let t=l.getConnectionStatus();return e.json({status:`healthy`,service:`browse-tool-chrome`,extension:t})});let p=ue({fetch:d.fetch,port:s});p.on(`error`,e=>{e.code===`EADDRINUSE`?(console.error(`Error [PORT_IN_USE]: Port ${s} is already in use.`),console.error(`Recovery: Try a different port with --port <port>`)):console.error(`Error [SERVER_ERROR]: HTTP server error: ${e.message}`),process.exit(1)}),console.error(`HTTP server started on port ${s}`),console.error(`Waiting for Chrome extension to connect...`),console.error(`Extension should poll: http://localhost:${s}/extension/tasks`),e.waitForExtension&&(console.error(`Waiting for extension connection before starting MCP...`),await new Promise(e=>{let t=setInterval(()=>{l.getConnectionStatus().connected&&(clearInterval(t),console.error(`Extension connected!`),e())},500)}));let m=ke(u),h=new ce;await m.connect(h),console.error(`Chrome extension MCP server started on stdio`);let g=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`);try{l.clearAllTasks(`Server shutting down`),await h.close(),p.close(),await r.release({kill:!1}),await t.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>g(`SIGINT`)),process.on(`SIGTERM`,()=>g(`SIGTERM`))}catch(e){if(r||t)try{await r?.release(),await t?.release()}catch{}let n=e instanceof Error?e.message:String(e);console.error(`Error [SERVER_ERROR]: Failed to start Chrome extension MCP server: ${n}`),console.error(`Recovery: Check that the port is available and try again.`),process.exit(1)}}),je=`BROWSE_TOOL_CONFIG`,Me=`.browse-tool`,Ne=`config.json`,Pe=[`json`,`text`,`quiet`],Fe={commands:{},tools:{}},Ie=N.record(N.string(),N.unknown()),Le=N.object({mcpServe:N.object({type:N.string().optional(),browser:N.string().optional(),headless:N.boolean().optional(),profile:N.string().optional(),mode:N.string().optional(),host:N.string().optional(),port:N.coerce.number().int().positive().optional(),httpPort:N.coerce.number().int().positive().optional(),tags:N.string().optional(),exclude:N.string().optional(),customTools:N.string().optional(),chromeForTestingPath:N.string().optional(),snippetsDir:N.string().optional(),registryPath:N.string().optional(),registryDir:N.string().optional(),pidsDir:N.string().optional(),profilesDir:N.string().optional(),proxyConfigDir:N.string().optional()}).partial().optional(),httpServe:N.object({port:N.coerce.number().int().positive().optional(),headless:N.boolean().optional(),idleTimeout:N.coerce.number().positive().optional(),host:N.string().optional(),registryDir:N.string().optional(),registryPath:N.string().optional(),pidsDir:N.string().optional(),profilesDir:N.string().optional(),snippetsDir:N.string().optional(),proxyConfigDir:N.string().optional()}).partial().optional(),exec:N.object({format:N.enum(Pe).optional(),color:N.boolean().optional(),port:N.coerce.number().int().positive().optional()}).partial().optional(),tools:N.object({format:N.enum(Pe).optional(),color:N.boolean().optional(),port:N.coerce.number().int().positive().optional()}).partial().optional(),status:N.record(N.string(),N.unknown()).optional(),stop:N.record(N.string(),N.unknown()).optional()}),Re=N.object({commands:Le.default({}),tools:N.record(N.string(),Ie).default({})});let z={config:Fe};function ze(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n===`--config`)return e[t+1];if(n.startsWith(`--config=`))return n.slice(9)}}function Be(e){let t=k.resolve(e);if(!A(t))throw Error(`Config path not found: ${t}`);return ne(t).isDirectory()?k.join(t,Ne):t}function Ve(e,t){let n=t.env??process.env,r=t.cwd??process.cwd(),i=t.homeDir??ie(),a=ze(e);if(a)return Be(a);if(n[je])return Be(n[je]);let o=k.join(r,Me,Ne);if(A(o))return o;let s=k.join(i,Me,Ne);if(A(s))return s}function He(e){let t=j(e,`utf8`),n=JSON.parse(t);return Re.parse(n)}function Ue(e,t={}){let n=Ve(e,t);return n?{configPath:n,config:He(n)}:{config:Fe}}function We(e,t={}){return z=Ue(e,t),z}function B(e){return(z.config.commands??{})[e]??{}}function Ge(e){return z.config.tools?.[e]??{}}function V(e,t,n,r,i){let a=e.getOptionValueSourceWithGlobals(t);return a!==void 0&&a!==`default`&&a!==`implied`?n:i===void 0?r===void 0?n:r:i}var Ke=class{port;exactPort;timeout;serverPort=null;constructor(e={}){this.port=e.port??s,this.exactPort=e.exactPort??!1,this.timeout=e.timeout??6e4}async ensureServer(){if(this.serverPort!==null)return this.serverPort;let e=await h().get(n.HttpServerManager).ensureRunning(this.port,{exactPort:this.exactPort});if(!e.running||!e.port)throw Error(e.error??`Failed to start HTTP server. Try running "browse-tool http-serve" manually.`);return this.serverPort=e.port,e.port}async getBaseUrl(){return`http://localhost:${await this.ensureServer()}`}async listTools(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/tools`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async listCustomTools(e){let t=await this.getBaseUrl(),n=new AbortController,r=setTimeout(()=>n.abort(),this.timeout);try{let r=new URL(`/custom-tools`,t);r.searchParams.set(`dir`,e);let i=await fetch(r,{method:`GET`,headers:{Accept:`application/json`},signal:n.signal});if(!i.ok)throw Error(`HTTP ${i.status}: ${i.statusText}`);let a=await i.json();if(a.error)throw Error(a.error);return a.tools}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(r)}}async listBrowsers(){let e=await this.getBaseUrl(),t=new AbortController,n=setTimeout(()=>t.abort(),this.timeout);try{let n=await fetch(`${e}/browsers`,{method:`GET`,headers:{Accept:`application/json`},signal:t.signal});if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json();if(r.error)throw Error(r.error);return r.browsers}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(n)}}async execute(e,t){let n=await this.getBaseUrl(),r=e===`run_spec`?void 0:this.timeout,i=new AbortController,a=r===void 0?void 0:setTimeout(()=>i.abort(),r);try{let a=await fetch(`${n}/execute`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:e,arguments:t}),signal:r===void 0?void 0:i.signal});if(!a.ok){let e=await a.text();throw Error(`HTTP ${a.status}: ${e||a.statusText}`)}let o=await a.json();return o.success?o.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:o.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{a&&clearTimeout(a)}}async executeCustomTool(e,t,n){let r=await this.getBaseUrl(),i=new AbortController,a=setTimeout(()=>i.abort(),this.timeout);try{let a=new URL(`/custom-tools`,r);a.searchParams.set(`dir`,e);let o=await fetch(a,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({tool:t,arguments:n}),signal:i.signal});if(!o.ok){let e=await o.text();throw Error(`HTTP ${o.status}: ${e||o.statusText}`)}let s=await o.json();return s.success?s.result??{content:[{type:`text`,text:`No result returned`}]}:{content:[{type:`text`,text:s.error??`Unknown error`}],isError:!0}}catch(e){throw e instanceof Error&&e.name===`AbortError`?Error(`Request timeout after ${this.timeout}ms`,{cause:e}):this.wrapConnectionError(e)}finally{clearTimeout(a)}}wrapConnectionError(e){return e instanceof Error?e.message.includes(`ECONNREFUSED`)||e.message.includes(`fetch failed`)?Error(`Cannot connect to browse-tool HTTP server.\n\nTry one of the following:\n 1. Start the server: browse-tool http-serve\n 2. Check if another process is using port ${this.port}\n 3. Run with a different port: browse-tool --port 3201 <command>\n\nOriginal error: ${e.message}`,{cause:e}):e:Error(String(e),{cause:e})}};function H(e){return new Ke(e)}const qe={reset:`\x1B[0m`,red:`\x1B[31m`,green:`\x1B[32m`,yellow:`\x1B[33m`,blue:`\x1B[34m`,magenta:`\x1B[35m`,cyan:`\x1B[36m`,gray:`\x1B[90m`,bold:`\x1B[1m`};function U(e,t,n){return n?`${qe[t]}${e}${qe.reset}`:e}function Je(e,t){let{format:n,color:r}=t;return n===`quiet`?``:n===`json`?Ye(e):Xe(e,r)}function Ye(e){return JSON.stringify(e,null,2)}function Xe(e,t){let n=[];e.isError&&n.push(U(`Error:`,`red`,t));for(let r of e.content)r.type===`text`?n.push(Ze(r,t)):r.type===`image`?n.push(et(r,t)):n.push(U(`[Unknown content type: ${r.type}]`,`yellow`,t));return n.join(`
|
|
3
3
|
`)}function Ze(e,t){let n=e.text;try{return Qe(JSON.parse(n),t)}catch{return n}}function Qe(e,t){if(typeof e!=`object`||!e)return String(e);let n=[];for(let[r,i]of Object.entries(e)){let e=U(r,`cyan`,t),a=$e(i,t);n.push(`${e}: ${a}`)}return n.join(`
|
|
4
4
|
`)}function $e(e,t){return e===null?U(`null`,`gray`,t):e===void 0?U(`undefined`,`gray`,t):typeof e==`boolean`?U(String(e),e?`green`:`red`,t):typeof e==`number`?U(String(e),`yellow`,t):typeof e==`string`?e.startsWith(`http://`)||e.startsWith(`https://`)?U(e,`blue`,t):e:Array.isArray(e)?e.length===0?U(`[]`,`gray`,t):JSON.stringify(e,null,2):typeof e==`object`?JSON.stringify(e,null,2):String(e)}function et(e,t){let{mimeType:n,data:r}=e;return U(`[Image: ${n}, ~${Math.round(r.length*3/4/1024)}KB base64 data]`,`magenta`,t)}function W(e,t){return U(`Error: ${e instanceof Error?e.message:e}`,`red`,t)}function tt(e,t){let n=Math.max(...e.map(e=>e.name.length)),r=[];for(let i of e){let e=U(i.name.padEnd(n),`cyan`,t);r.push(` ${e} ${i.description}`)}return r.join(`
|
|
5
5
|
`)}function nt(e,t){let n=B(`tools`),r=V(e,`format`,t.format,n.format),i=V(e,`color`,t.color,n.color);return{port:V(e,`port`,t.port,n.port===void 0?void 0:String(n.port),process.env.PLAYWRIGHT_PORT),formatterOptions:{format:r,color:i}}}const rt=new F(`list-custom-tools`).description(`List custom tools from a tools directory`).argument(`<dir>`,`Path to the tools directory containing tools.yaml`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(s)).action(async function(e,t){let{port:n,formatterOptions:r}=nt(this,t);try{let t=await H({port:Number.parseInt(n,10)}).listCustomTools(e),i=r.format===`text`?tt(t,r.color):r.format===`quiet`?``:JSON.stringify(t,null,2);i&&console.log(i)}catch(e){console.error(W(e instanceof Error?e:String(e),r.color)),process.exit(1)}}),it=new F(`exec-custom-tool`).description(`Execute a custom tool from a tools directory`).argument(`<dir>`,`Path to the tools directory containing tools.yaml`).argument(`<tool>`,`Custom tool name to execute`).argument(`[args]`,`JSON arguments for the tool`,`{}`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(s)).action(async function(e,t,n,r){let{port:i,formatterOptions:a}=nt(this,r);try{let r;try{r=JSON.parse(n)}catch{console.error(W(`Invalid JSON arguments: ${n}`,a.color)),process.exit(1);return}let o=await H({port:Number.parseInt(i,10)}).executeCustomTool(e,t,r),s=Je(o,a);s&&console.log(s),o.isError&&process.exit(1)}catch(e){console.error(W(e instanceof Error?e:String(e),a.color)),process.exit(1)}});new F(`custom-tools`).description(`List and execute custom tools through the HTTP server`).addCommand(rt).addCommand(it);const at=new F(`docker-build-cft`).description(`Build the Chrome for Testing Docker image used by vm mode`).option(`--cft-version <version>`,`Chrome for Testing version to build`,x).option(`--image <image>`,`Docker image tag to produce`,t).option(`--platform <platform>`,`Docker target platform`,e).action(async e=>{try{let t=await C({version:e.cftVersion,image:e.image,platform:e.platform,stdio:`inherit`});console.log(`Built Docker image ${t.image}`),console.log(` Version: ${t.version}`),console.log(` Platform: ${t.platform}`),console.log(` Archive: ${a(t.platform)}`)}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exit(1)}}),ot=new F(`exec`).description(`Execute a tool directly with JSON arguments`).argument(`<tool>`,`Tool name to execute (e.g., browser_launch)`).argument(`[args]`,`JSON arguments for the tool`,`{}`).option(`-f, --format <format>`,`Output format: json, text, quiet`,`json`).option(`--no-color`,`Disable colored output`).option(`-p, --port <port>`,`HTTP server port`,String(s)).action(async function(e,t,n){let r=B(`exec`),i=V(this,`format`,n.format,r.format),a=V(this,`color`,n.color,r.color),o=V(this,`port`,n.port,r.port===void 0?void 0:String(r.port),process.env.PLAYWRIGHT_PORT),s=this.getOptionValueSourceWithGlobals(`port`),c=s!==void 0&&s!==`default`&&s!==`implied`,l={format:i,color:a};try{let n;try{n=JSON.parse(t)}catch{console.error(W(`Invalid JSON arguments: ${t}`,l.color)),process.exit(1)}let r=await H({port:Number.parseInt(o,10),exactPort:c}).execute(e,n),i=Je(r,l);i&&console.log(i),r.isError&&process.exit(1)}catch(e){console.error(W(e instanceof Error?e:String(e),l.color)),process.exit(1)}}),G=`pageId`,K=`browserId`,st=N.record(N.string(),N.unknown()),ct=N.object({type:N.literal(`object`),properties:N.record(N.string(),N.unknown()).optional(),required:N.array(N.string()).optional(),additionalProperties:N.boolean().optional()}).passthrough(),lt=N.object({name:N.string().min(1),description:N.string().min(1),script:N.string().min(1),suggestionActions:N.string().min(1).optional(),capabilities:st,inputSchema:ct}),ut=N.object({tools:N.array(lt)}),dt=process.env.BROWSE_TOOL_DEBUG_CUSTOM_TOOLS===`1`;function ft(e){return JSON.stringify(e,(e,t)=>typeof t==`string`&&t.length>240?`${t.slice(0,240)}...<trimmed>`:t)}function pt(e,t){if(dt){if(t){console.error(`[CustomToolService] ${e}`,t);return}console.error(`[CustomToolService] ${e}`)}}function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function mt(e){return q(e)&&Array.isArray(e.content)}function J(e){let t=e.trim();return t===``?``:t.startsWith(`"`)&&t.endsWith(`"`)||t.startsWith(`[`)&&t.endsWith(`]`)||t.startsWith(`{`)&&t.endsWith(`}`)?JSON.parse(t):t.startsWith(`'`)&&t.endsWith(`'`)?t.slice(1,-1):t===`true`?!0:t===`false`?!1:t===`null`?null:/^-?\d+(\.\d+)?$/.test(t)?Number(t):t}function ht(e){if(!e||Object.keys(e).length===0)return;let t={};for(let[n,r]of Object.entries(e))if(r!=null){if(typeof r==`string`||typeof r==`number`||typeof r==`boolean`){t[n]=r;continue}t[n]=JSON.stringify(r)}return Object.keys(t).length>0?t:void 0}function gt(e){return e.replace(/^\uFEFF/,``).split(/\r?\n/).map(e=>{if(e.includes(` `))throw Error(`Tab indentation is not supported in tools.yaml`);let t=e.replace(/\s+#.*$/,``);return t.trim().length===0?null:{indent:t.match(/^ */)?.[0].length??0,text:t.trim()}}).filter(e=>e!==null)}function Y(e,t,n){let r=e[t];if(!r||r.indent!==n)throw Error(`Invalid indentation in tools.yaml at line ${t+1}`);return r.text.startsWith(`-`)?_t(e,t,n):vt(e,t,n)}function _t(e,t,n){let r=[],i=t;for(;i<e.length;){let t=e[i];if(t.indent<n||t.indent!==n||!t.text.startsWith(`-`))break;let a=t.text.slice(1).trim();if(a===``){let t=e[i+1];if(!t||t.indent<=n){r.push(null),i+=1;continue}let[a,o]=Y(e,i+1,t.indent);r.push(a),i=o;continue}if(a.includes(`:`)){let[t,o]=yt(a),s={};if(o===void 0){let r=e[i+1];if(!r||r.indent<=n)throw Error(`Expected nested value for "${t}" in tools.yaml`);let[a,o]=Y(e,i+1,r.indent);s[t]=a,i=o}else s[t]=J(o),i+=1;for(;i<e.length&&e[i].indent>n;){let t=e[i];if(t.indent!==n+2||t.text.startsWith(`-`)){let[n,r]=Y(e,i,t.indent);if(!q(n))throw Error(`Expected object entry in tools.yaml at line ${i+1}`);Object.assign(s,n),i=r;continue}let[r,a]=yt(t.text);if(a===void 0){let n=e[i+1];if(!n||n.indent<=t.indent)throw Error(`Expected nested value for "${r}" in tools.yaml`);let[a,o]=Y(e,i+1,n.indent);s[r]=a,i=o;continue}s[r]=J(a),i+=1}r.push(s);continue}r.push(J(a)),i+=1}return[r,i]}function vt(e,t,n){let r={},i=t;for(;i<e.length;){let t=e[i];if(t.indent<n||t.indent!==n||t.text.startsWith(`-`))break;let[a,o]=yt(t.text);if(o===void 0){let t=e[i+1];if(!t||t.indent<=n){r[a]=null,i+=1;continue}let[o,s]=Y(e,i+1,t.indent);r[a]=o,i=s;continue}r[a]=J(o),i+=1}return[r,i]}function yt(e){let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid tools.yaml entry: "${e}"`);let n=e.slice(0,t).trim(),r=e.slice(t+1).trim();return[n,r===``?void 0:r]}function bt(e){let t=gt(e);if(t.length===0)return{};let[n]=Y(t,0,t[0].indent);return n}function xt(e){e.inputSchema.properties===void 0&&(e.inputSchema.properties={});let t=e.inputSchema.properties;if(!q(t))throw Error(`Custom tool "${e.name}" inputSchema.properties must be an object`);let n=t[G],r=t[K];if(n!==void 0&&!(q(n)&&n.type===`string`))throw Error(`Custom tool "${e.name}" must define inputSchema.properties.pageId as type "string"`);if(r!==void 0&&!(q(r)&&r.type===`string`))throw Error(`Custom tool "${e.name}" must define inputSchema.properties.browserId as type "string"`);n===void 0&&(t[G]={type:`string`,description:`Browse-tool page ID to run this custom tool against. Either pageId or browserId is required at call time.`}),r===void 0&&(t[K]={type:`string`,description:`Browse-tool browser ID to run this custom tool against when no pageId is supplied. The tool will use the browser's current page or open a new one.`})}async function St(e){let t=w(await M(e,`utf8`),{mode:`strip`});return await import(`data:text/javascript;base64,${Buffer.from(t,`utf8`).toString(`base64`)}`)}function Ct(e,t){let n=typeof t.run==`function`?t.run:void 0;if(!n)throw Error(`Custom tool script "${e}" must export a "run" function`);return{execute:n}}function wt(e,t){return typeof e.suggestionActions==`string`&&e.suggestionActions.trim().length>0?e:{...e,suggestionActions:t}}function Tt(e,t){let n=e.content[0];if(n?.type===`text`&&typeof n.text==`string`)try{let r=JSON.parse(n.text);if(q(r)){let i=wt(r,t);return{...e,content:[{...n,text:JSON.stringify(i,null,2)},...e.content.slice(1)]}}}catch{}return{...e,content:[...e.content,{type:`text`,text:`suggestionActions: ${t}`}]}}function Et(e,t,n){if(mt(t))return n?Tt(t,n):t;if(typeof t==`string`)return n?{content:[{type:`text`,text:JSON.stringify({result:t,suggestionActions:n},null,2)}]}:{content:[{type:`text`,text:t}]};if(t===void 0)return n?{content:[{type:`text`,text:JSON.stringify({result:`Custom tool "${e}" completed successfully`,suggestionActions:n},null,2)}]}:{content:[{type:`text`,text:`Custom tool "${e}" completed successfully`}]};let r=q(t)&&n?wt(t,n):t;return{content:[{type:`text`,text:JSON.stringify(r,null,2)}]}}var Dt=class{constructor(e,t,n=new l,r){this.pageRegistry=e,this.extensionTaskQueue=t,this.telemetry=n,this.browserService=r}resolveToolPage(e,t,n){if(n.page)return n.page;if(n.mode===`extension`&&this.extensionTaskQueue){let e=new b(this.extensionTaskQueue);return e.setTarget(t,n.browserId),e}throw Error(`Custom tool "${e}" requires a supported page context`)}toPageSummary(e,t){return{pageId:t.id,url:t.url,title:t.title,active:e===t.id}}async createPageForBrowser(e,t,n){if(!this.browserService)throw Error(`Custom tool "${e}" requires browser service support to create a page`);let r=this.browserService.getBrowser(t);if(!r)throw Error(`Browser "${t}" not found`);let i=n?.setAsCurrent!==!1;if(r.mode===`extension`||r.mode===`vm`){if(!this.extensionTaskQueue)throw Error(`Custom tool "${e}" requires extension task support to create a page`);let a=this.pageRegistry.registerExtensionPage(t,void 0,n?.url,!1);try{let o=await this.extensionTaskQueue.queueTask(`browser_new_page`,{browserId:t,pageId:a,url:n?.url,setAsCurrent:i},1e4,t);if(!o.success)throw Error(o.error??`Custom tool "${e}" failed to create a page`);let s=o.result?.content[0]?.type===`text`?o.result.content[0].text:void 0,c={};if(typeof s==`string`&&s.length>0)try{c=JSON.parse(s)}catch{c={}}let l=this.pageRegistry.get(a);if(!l)throw Error(`Page "${a}" was not registered`);l.url=c.url??l.url,l.title=c.title??l.title,l.extensionTabId=c.tabId??l.extensionTabId;let u=await this.waitForResolvedPageMetadata(a);return r.pageIds.add(a),(i||!r.currentPageId)&&this.browserService.setCurrentPage(t,a),this.browserService.recordBrowserActivity(t,a),{...this.toPageSummary(r.currentPageId,u),page:this.resolveToolPage(e,a,u)}}catch(e){throw this.pageRegistry.remove(a),e}}let{pageId:a,page:o}=await this.browserService.newPage(t);n?.url&&(await o.goto(n.url),await this.pageRegistry.updateMetadata(a)),i&&this.browserService.setCurrentPage(t,a),this.browserService.recordBrowserActivity(t,a);let s=this.pageRegistry.get(a);if(!s)throw Error(`Page "${a}" was not registered`);return{...this.toPageSummary(r.currentPageId,s),page:this.resolveToolPage(e,a,s)}}async waitForResolvedPageMetadata(e){let t=Date.now(),n=this.pageRegistry.get(e);for(;n&&Date.now()-t<1500;){let t=typeof n.url==`string`&&n.url.length>0,r=typeof n.title==`string`&&n.title.length>0&&n.title!==`Extension Tab`;if(t&&r)return n;await new Promise(e=>setTimeout(e,50)),n=this.pageRegistry.get(e)}if(!n)throw Error(`Page "${e}" was not registered`);return n}async resolveExecutionContext(e,t){let n=typeof t[G]==`string`&&t[G].length>0?t[G]:void 0,r=typeof t[K]==`string`&&t[K].length>0?t[K]:void 0;if(!n&&!r)throw Error(`Custom tool "${e}" requires a string pageId or browserId`);if(n){let t=this.pageRegistry.get(n);if(!t)throw Error(`Page "${n}" not found`);if(r&&t.browserId!==r)throw Error(`Custom tool "${e}" received pageId "${n}" for browser "${t.browserId}", not "${r}"`);let i=this.createBrowserHelper(e,t.browserId);return{pageId:n,pageEntry:t,page:this.resolveToolPage(e,n,t),browser:i}}let i=this.createBrowserHelper(e,r);try{let e=await i.getCurrentPage(),t=this.pageRegistry.get(e.pageId);if(!t)throw Error(`Page "${e.pageId}" not found`);return{pageId:e.pageId,pageEntry:t,page:e.page,browser:i}}catch(e){if(!(e instanceof Error?e.message:String(e)).includes(`has no pages`))throw e;let t=await i.newPage(),n=this.pageRegistry.get(t.pageId);if(!n)throw Error(`Page "${t.pageId}" not found`,{cause:e});return{pageId:t.pageId,pageEntry:n,page:t.page,browser:i}}}createBrowserHelper(e,t){return{browserId:t,mode:this.browserService?.getBrowser(t)?.mode??`extension`,listPages:async()=>{let e=this.browserService?.getBrowser(t);if(!e)throw Error(`Browser "${t}" not found`);return this.pageRegistry.findByBrowser(t).map(t=>this.toPageSummary(e.currentPageId,t))},getPage:async n=>{let r=this.pageRegistry.get(n);if(!r||r.browserId!==t)throw Error(`Page "${n}" not found in browser "${t}"`);let i=this.browserService?.getBrowser(t);return{...this.toPageSummary(i?.currentPageId??null,r),page:this.resolveToolPage(e,n,r)}},getCurrentPage:async()=>{let n=this.browserService?.getBrowser(t);if(!n)throw Error(`Browser "${t}" not found`);let r=n.currentPageId??this.pageRegistry.findByBrowser(t)[0]?.id??void 0;if(!r)throw Error(`Browser "${t}" has no pages`);let i=this.pageRegistry.get(r);if(!i)throw Error(`Page "${r}" not found`);return{...this.toPageSummary(n.currentPageId,i),page:this.resolveToolPage(e,r,i)}},newPage:async n=>this.createPageForBrowser(e,t,n)}}createToolLogger(e,t,n){let r={"browse_tool.tool.name":e,"browse_tool.page.id":t,"browse_tool.browser.id":n.browserId,"browse_tool.execution.mode":n.mode},i=(e,t,n)=>{this.telemetry.log(e,t,{attributes:ht({...r,...n?.attributes??{}}),exception:n?.exception})};return{getTraceContext:()=>this.telemetry.getActiveTraceContext(),trace:(e,t)=>i(`trace`,e,t),debug:(e,t)=>i(`debug`,e,t),info:(e,t)=>i(`info`,e,t),warn:(e,t)=>i(`warn`,e,t),error:(e,t)=>i(`error`,e,t),fatal:(e,t)=>i(`fatal`,e,t)}}async listTools(e){return(await this.loadTools(e)).map(({name:e,description:t,suggestionActions:n,inputSchema:r,capabilities:i})=>({name:e,description:t,suggestionActions:n,inputSchema:r,capabilities:i}))}async executeTool(e,t,n){let r=typeof n[G]==`string`?n[G]:void 0,i=typeof n[K]==`string`?n[K]:void 0;return this.telemetry.runInSpan(`browse_tool.custom_tool.execute`,{attributes:{"browse_tool.tool.name":t,"browse_tool.custom_tools.directory":k.resolve(e),"browse_tool.page.id":r,"browse_tool.browser.id":i}},async r=>{let i=(await this.loadTools(e)).find(e=>e.name===t);if(!i)throw r?.setStatus({code:P.ERROR,message:`Custom tool "${t}" not found`}),Error(`Custom tool "${t}" not found`);let{pageId:a,pageEntry:o,page:s,browser:c}=await this.resolveExecutionContext(t,n),l=this.createToolLogger(t,a,o);r?.setAttributes({"browse_tool.browser.id":o.browserId,"browse_tool.execution.mode":o.mode,"browse_tool.page.id":a}),pt(`Executing custom tool`,{toolName:t,directory:k.resolve(e),pageId:a,browserId:o.browserId,mode:o.mode,input:ft(n),scriptPath:i.scriptPath});let u;try{u=await i.execute?.({page:s,browser:c,input:n,logger:l})}catch(e){let n=e instanceof Error?e.message:String(e);throw pt(`Custom tool execution failed`,{toolName:t,pageId:a,browserId:o.browserId,mode:o.mode,error:n,stack:e instanceof Error?e.stack:void 0}),Error(`Custom tool "${t}" failed on page "${a}": ${n}`,{cause:e})}pt(`Custom tool execution completed`,{toolName:t,pageId:a,browserId:o.browserId,mode:o.mode,result:ft(u)});let d=Et(t,u,i.suggestionActions),f=d.isError?d.content[0]?.text:void 0;return f&&r?.setStatus({code:P.ERROR,message:f}),d})}async loadTools(e){let t=k.resolve(e),n=k.join(t,`tools.yaml`),r=bt(await M(n,`utf8`).catch(e=>{throw Error(`Failed to read custom tool manifest at "${n}": ${e instanceof Error?e.message:String(e)}`)})),i=ut.parse(r);return Promise.all(i.tools.map(async e=>{xt(e);let n=k.resolve(t,e.script);await re(n).catch(t=>{throw Error(`Custom tool "${e.name}" script not found at "${n}": ${t instanceof Error?t.message:String(t)}`)});let r=Ct(n,await St(n));return{name:e.name,description:e.description,suggestionActions:e.suggestionActions,inputSchema:e.inputSchema,capabilities:e.capabilities,scriptPath:n,...r}}))}};function Ot(e){let t=new I;return t.get(`/browsers`,async t=>{try{let r=e.get(n.BrowserService),i=e.get(n.PageRegistry),a=r.listBrowsers(),o=i.list(),s=a.map(e=>{let t=o.filter(t=>t.browserId===e.id);return{id:e.id,profileName:e.profileName,currentPageId:e.currentPageId,createdAt:e.createdAt.toISOString(),pages:t.map(e=>({id:e.id,url:e.url,title:e.title,createdAt:e.createdAt.toISOString()}))}}),c={totalBrowsers:a.length,totalPages:o.length};return t.json({browsers:s,stats:c})}catch(e){return console.error(`Failed to list browsers:`,e),t.json({error:`Failed to list browsers`,message:e instanceof Error?e.message:String(e)},500)}}),t.delete(`/browsers`,async t=>{try{return await e.get(n.BrowserService).closeAll(),t.json({success:!0,message:`All browsers closed`})}catch(e){return console.error(`Failed to close all browsers:`,e),t.json({error:`Failed to close all browsers`,message:e instanceof Error?e.message:String(e)},500)}}),t.delete(`/browsers/:id`,async t=>{try{let r=t.req.param(`id`),i=e.get(n.BrowserService);return i.getBrowser(r)?(await i.closeBrowser(r),t.json({success:!0,message:`Browser "${r}" closed`})):t.json({error:`Browser "${r}" not found`},404)}catch(e){return console.error(`Failed to close browser:`,e),t.json({error:`Failed to close browser`,message:e instanceof Error?e.message:String(e)},500)}}),t.delete(`/pages/:id`,async t=>{try{let r=t.req.param(`id`),i=e.get(n.PageRegistry).get(r);return i?i.page?(await i.page.close(),t.json({success:!0,message:`Page "${r}" closed`})):t.json({error:`Page "${r}" is in extension mode and cannot be closed via API`},400):t.json({error:`Page "${r}" not found`},404)}catch(e){return console.error(`Failed to close page:`,e),t.json({error:`Failed to close page`,message:e instanceof Error?e.message:String(e)},500)}}),t}function kt(e){return e.toLocaleString(`en-US`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}function At(e){let t=Math.floor(e/1e3),n=Math.floor(t/60),r=Math.floor(n/60);return r>0?`${r}h ${n%60}m`:n>0?`${n}m ${t%60}s`:`${t}s`}function jt(e){return At(new Date().getTime()-e.getTime())}const Mt=`table-container`,Nt=`stat-card`,Pt=`browser-row`,Ft=`page-row`,X=`status-active`,It=`url-cell`,Z=`timestamp-cell`,Lt=`actions-cell`,Rt=`kill-btn`,zt=`empty-state`;function Bt({browsers:e}){return e.length===0?L(`div`,{class:Mt,children:R(`div`,{class:zt,children:[L(`h3`,{children:`No Active Browsers`}),L(`p`,{children:`Launch a browser using MCP tools to see it here.`})]})}):L(`div`,{class:Mt,children:R(`table`,{class:`browser-table`,children:[L(`thead`,{children:R(`tr`,{children:[L(`th`,{children:`ID`}),L(`th`,{children:`Status`}),L(`th`,{children:`URL / Title`}),L(`th`,{children:`Created`}),L(`th`,{children:`Age`}),L(`th`,{children:`Actions`})]})}),L(`tbody`,{id:`browser-table-body`,children:e.map(e=>R(me,{children:[R(`tr`,{class:Pt,children:[L(`td`,{children:e.id}),L(`td`,{children:R(`span`,{class:X,children:[e.pages.length,` page`,e.pages.length===1?``:`s`]})}),L(`td`,{children:e.profileName||`Default Profile`}),L(`td`,{class:Z,children:kt(e.createdAt)}),L(`td`,{class:Z,children:jt(e.createdAt)}),L(`td`,{class:Lt,children:L(`button`,{type:`button`,class:Rt,onclick:`dashboard.killBrowser('${e.id}')`,children:`Kill`})})]}),e.pages.map(t=>R(`tr`,{class:Ft,children:[R(`td`,{children:[t.id,e.currentPageId===t.id&&` (active)`]}),L(`td`,{children:L(`span`,{class:X,children:`Open`})}),L(`td`,{class:It,title:t.url,children:t.title||t.url||`about:blank`}),L(`td`,{class:Z,children:kt(t.createdAt)}),L(`td`,{class:Z,children:jt(t.createdAt)}),L(`td`,{class:Lt,children:L(`button`,{type:`button`,class:Rt,onclick:`dashboard.killPage('${t.id}')`,children:`Close`})})]},t.id))]},e.id))})]})})}function Vt({stats:e}){return R(`div`,{class:`stats-container`,children:[R(`div`,{class:Nt,children:[L(`h3`,{children:`Active Browsers`}),L(`div`,{class:`value`,children:e.totalBrowsers})]}),R(`div`,{class:Nt,children:[L(`h3`,{children:`Active Pages`}),L(`div`,{class:`value`,children:e.totalPages})]})]})}function Ht({browsers:e,stats:t}){return R(`div`,{class:`dashboard-container`,children:[R(`div`,{class:`dashboard-header`,children:[R(`div`,{children:[L(`h1`,{children:`Playwright MCP Dashboard`}),L(`p`,{children:`Browser automation management (auto-refresh every 3 seconds)`})]}),R(`div`,{children:[L(`button`,{type:`button`,id:`refresh-btn`,class:`btn refresh-btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`}),L(`button`,{type:`button`,id:`kill-all-btn`,class:`btn btn-danger`,onclick:`dashboard.killAllBrowsers()`,children:`Kill All Browsers`})]})]}),L(Vt,{stats:t}),L(Bt,{browsers:e}),L(`script`,{children:pe(`
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./streamable-http-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./streamable-http-SGOVRHb7.cjs`);let t=require(`zod`),n=require(`@agimon-ai/foundation-validator`),r=require(`@modelcontextprotocol/sdk/server/index.js`),i=require(`@modelcontextprotocol/sdk/types.js`),a=require(`liquidjs`);function o(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:JSON.stringify(e)}function s(e){if(!e)return;let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=o(r));return Object.keys(t).length>0?t:void 0}function c(){let t=new e.g({serviceName:`browse-tool-mcp`});return{debug(e,n){t.log(`debug`,e,{attributes:s(n)})},info(e,n){t.log(`info`,e,{attributes:s(n)})},warn(e,n){t.log(`warn`,e,{attributes:s(n)})},error(e,n){t.log(`error`,e,{attributes:s(n)})}}}var l=class extends Error{code=`UNKNOWN_TOOL`;recovery=`Use ListTools to see available tools.`;availableTools;constructor(e,t,n){super(`Unknown tool: ${e}. Available tools: ${t.slice(0,5).join(`, `)}${t.length>5?`, ... (${t.length} total)`:``}. Use ListTools to see all available tools.`,n),this.name=`UnknownToolError`,this.availableTools=t}},u=class extends Error{code=`TOOL_EXECUTION_ERROR`;recovery;toolName;constructor(e,t,n){super(`Tool execution failed for '${e}': ${t}`,n),this.name=`ToolExecutionError`,this.toolName=e,this.recovery=n?.recovery??`Check tool inputs and try again.`}};function d(e,t){let n=e instanceof Error?e.message:`Unknown error occurred`,r={error:{code:e instanceof Error&&`code`in e?e.code:`TOOL_EXECUTION_ERROR`,message:n,toolName:t,recovery:e instanceof Error&&`recovery`in e?e.recovery:`Check tool inputs and try again.`}};return{content:[{type:`text`,text:JSON.stringify(r,null,2)}],isError:!0}}function f(a){let o=a?.container??e.r,s=a?.logger??c(),f=new r.Server({name:`browse-tool`,version:`0.1.0`},{capabilities:{tools:{}}}),p=o.getAll(e.T.Tool),m=new Map;for(let e of p){let t=e.getDefinition();m.set(t.name,e)}return s.info(`MCP server initialized`,{toolCount:p.length}),f.setRequestHandler(i.ListToolsRequestSchema,async()=>(s.debug(`ListTools request received`),{tools:p.map(e=>e.getDefinition())})),f.setRequestHandler(i.CallToolRequestSchema,async e=>{let{name:r,arguments:i}=e.params;s.debug(`Tool call received`,{toolName:r,timestamp:new Date().toISOString()});let a=m.get(r);if(!a){let e=Array.from(m.keys());throw s.warn(`Unknown tool requested`,{toolName:r,availableTools:e,timestamp:new Date().toISOString()}),new l(r,e)}let o=(0,n.coerceArgs)(i??{},a.getInputSchema());try{let e=a.getInputSchema().parse(o);return await a.execute(e)}catch(e){if(e instanceof t.z.ZodError)return s.warn(`Tool input validation failed`,{toolName:r,timestamp:new Date().toISOString()}),{content:[{type:`text`,text:(0,n.formatZodError)(e,{schemaName:r,schema:a.getInputSchema()})}],isError:!0};let i=e instanceof u?e:new u(r,e instanceof Error?e.message:String(e),{cause:e});return s.error(`Tool execution failed`,{toolName:r,error:i.message,code:i.code,timestamp:new Date().toISOString()}),d(i,r)}}),f}var p='Create a browse-tool custom tool folder for use with `browse-tool mcp-serve --custom-tools <dir>`.\n\nGoal: {{ toolGoal }}\nPreferred tool name: {{ preferredToolName }}\nPage context: {{ pageContext }}\n\nRequirements:\n- Produce a `tools.yaml` file with a top-level `tools` array.\n- Each tool entry must include `name`, `description`, `script`, `capabilities`, and `inputSchema`.\n- Each tool entry may optionally include `suggestionActions` as a short text string describing the recommended next step after the tool succeeds.\n- `inputSchema` must be JSON Schema with `type: object`.\n- `inputSchema` must define at least one execution target field: `pageId` and/or `browserId`, each as `type: string` when present.\n- The script file must be `.ts` and export a named `run` function with the shape `export const run = async ({ page, browser, input, logger }) => { ... }`.\n- The `run` export runs in Node.js, not inside the page.\n- Use the provided `page` object for browser interaction. It will be a Playwright page in playwright mode or an extension page proxy in extension mode.\n- Use the provided `browser` helper when the tool needs to list pages, open a new page, or work from `browserId` instead of an existing `pageId`.\n- `browser` is not a raw Playwright browser. It exposes `browserId`, `mode`, `listPages()`, `getPage(pageId)`, `getCurrentPage()`, and `newPage({ url?, setAsCurrent? })`.\n- Use the provided `logger` object for instrumentation. It exposes `trace`, `debug`, `info`, `warn`, `error`, and `fatal`, and logs automatically attach to the active custom-tool telemetry context.\n- The logger also exposes `getTraceContext()` so a tool can return the active `traceId` and `spanId` when needed for downstream log inspection.\n- If you need DOM access, call `page.evaluate(...)` from the `run` export instead of assuming browser globals are available at module scope.\n- Keep the script self-contained and avoid relative imports.\n- Return either a plain object, a string, or an MCP-style `CallToolResult`.\n- Prefer read-only behavior unless the goal explicitly requires mutation.\n\nOutput format:\n- Show the full `tools.yaml` content.\n- Show the full `{{ preferredToolName }}.ts` content.\n- If the tool needs additional inputs beyond `pageId` or `browserId`, define them in `inputSchema` and read them from `input`.\n- Filesystem access is allowed because the script runs in Node.js.\n\nExample `tools.yaml` shape:\n```yaml\ntools:\n - name: get_post\n description: Get the current post from the open feed page\n script: get_post.ts\n suggestionActions: Open the author profile and review whether the post is worth engaging with\n capabilities:\n readOnlyHint: true\n openWorldHint: false\n inputSchema:\n type: object\n properties:\n pageId:\n type: string\n browserId:\n type: string\n selector:\n type: string\n required:\n - pageId\n```\n\nExample script shape:\n```ts\nexport const run = async ({ page, browser, input, logger }) => {\n const traceContext = logger.getTraceContext();\n logger.info("reading post", { attributes: { selector: input.selector ?? "body", browserId: browser.browserId } });\n const selector = input.selector ?? "body";\n const text = await page.evaluate((currentSelector) => {\n return document.querySelector(currentSelector)?.textContent ?? "";\n }, selector);\n return {\n pageUrl: page.url(),\n traceContext,\n text,\n };\n};\n```\n';const m={name:`custom_script_authoring`,description:`Generate a browse-tool custom tool folder with tools.yaml and TypeScript scripts.`,arguments:[{name:`toolGoal`,description:`What the custom tool should do on the page`,required:!0},{name:`toolName`,description:`Preferred snake_case tool name`,required:!1},{name:`pageContext`,description:`Optional context about the target page or workflow`,required:!1}]},h=new a.Liquid;function g(e){let t=e.toolName?.trim()||`custom_page_tool`,n=e.pageContext?.trim()||`No extra page context provided.`;return[{role:`user`,content:{type:`text`,text:h.parseAndRenderSync(p,{toolGoal:e.toolGoal,preferredToolName:t,pageContext:n})}}]}exports.PLAYWRIGHT_TYPES=e.T,exports.StdioTransportHandler=e.n,exports.StreamableHttpTransportHandler=e.t,exports.ToolExecutionError=u,exports.UnknownToolError=l,exports.container=e.r,exports.createContainer=e.i,exports.createServer=f,exports.customScriptAuthoringPrompt=m,exports.generateCustomScriptAuthoringPrompt=g;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{T as e,g as t,i as n,n as r,r as i,t as a}from"./streamable-http-
|
|
1
|
+
import{T as e,g as t,i as n,n as r,r as i,t as a}from"./streamable-http-ajTIeOoU.mjs";import{z as o}from"zod";import{coerceArgs as s,formatZodError as c}from"@agimon-ai/foundation-validator";import{Server as l}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as u,ListToolsRequestSchema as d}from"@modelcontextprotocol/sdk/types.js";import{Liquid as f}from"liquidjs";function p(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:JSON.stringify(e)}function m(e){if(!e)return;let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=p(r));return Object.keys(t).length>0?t:void 0}function h(){let e=new t({serviceName:`browse-tool-mcp`});return{debug(t,n){e.log(`debug`,t,{attributes:m(n)})},info(t,n){e.log(`info`,t,{attributes:m(n)})},warn(t,n){e.log(`warn`,t,{attributes:m(n)})},error(t,n){e.log(`error`,t,{attributes:m(n)})}}}var g=class extends Error{code=`UNKNOWN_TOOL`;recovery=`Use ListTools to see available tools.`;availableTools;constructor(e,t,n){super(`Unknown tool: ${e}. Available tools: ${t.slice(0,5).join(`, `)}${t.length>5?`, ... (${t.length} total)`:``}. Use ListTools to see all available tools.`,n),this.name=`UnknownToolError`,this.availableTools=t}},_=class extends Error{code=`TOOL_EXECUTION_ERROR`;recovery;toolName;constructor(e,t,n){super(`Tool execution failed for '${e}': ${t}`,n),this.name=`ToolExecutionError`,this.toolName=e,this.recovery=n?.recovery??`Check tool inputs and try again.`}};function v(e,t){let n=e instanceof Error?e.message:`Unknown error occurred`,r={error:{code:e instanceof Error&&`code`in e?e.code:`TOOL_EXECUTION_ERROR`,message:n,toolName:t,recovery:e instanceof Error&&`recovery`in e?e.recovery:`Check tool inputs and try again.`}};return{content:[{type:`text`,text:JSON.stringify(r,null,2)}],isError:!0}}function y(t){let n=t?.container??i,r=t?.logger??h(),a=new l({name:`browse-tool`,version:`0.1.0`},{capabilities:{tools:{}}}),f=n.getAll(e.Tool),p=new Map;for(let e of f){let t=e.getDefinition();p.set(t.name,e)}return r.info(`MCP server initialized`,{toolCount:f.length}),a.setRequestHandler(d,async()=>(r.debug(`ListTools request received`),{tools:f.map(e=>e.getDefinition())})),a.setRequestHandler(u,async e=>{let{name:t,arguments:n}=e.params;r.debug(`Tool call received`,{toolName:t,timestamp:new Date().toISOString()});let i=p.get(t);if(!i){let e=Array.from(p.keys());throw r.warn(`Unknown tool requested`,{toolName:t,availableTools:e,timestamp:new Date().toISOString()}),new g(t,e)}let a=s(n??{},i.getInputSchema());try{let e=i.getInputSchema().parse(a);return await i.execute(e)}catch(e){if(e instanceof o.ZodError)return r.warn(`Tool input validation failed`,{toolName:t,timestamp:new Date().toISOString()}),{content:[{type:`text`,text:c(e,{schemaName:t,schema:i.getInputSchema()})}],isError:!0};let n=e instanceof _?e:new _(t,e instanceof Error?e.message:String(e),{cause:e});return r.error(`Tool execution failed`,{toolName:t,error:n.message,code:n.code,timestamp:new Date().toISOString()}),v(n,t)}}),a}var b='Create a browse-tool custom tool folder for use with `browse-tool mcp-serve --custom-tools <dir>`.\n\nGoal: {{ toolGoal }}\nPreferred tool name: {{ preferredToolName }}\nPage context: {{ pageContext }}\n\nRequirements:\n- Produce a `tools.yaml` file with a top-level `tools` array.\n- Each tool entry must include `name`, `description`, `script`, `capabilities`, and `inputSchema`.\n- Each tool entry may optionally include `suggestionActions` as a short text string describing the recommended next step after the tool succeeds.\n- `inputSchema` must be JSON Schema with `type: object`.\n- `inputSchema` must define at least one execution target field: `pageId` and/or `browserId`, each as `type: string` when present.\n- The script file must be `.ts` and export a named `run` function with the shape `export const run = async ({ page, browser, input, logger }) => { ... }`.\n- The `run` export runs in Node.js, not inside the page.\n- Use the provided `page` object for browser interaction. It will be a Playwright page in playwright mode or an extension page proxy in extension mode.\n- Use the provided `browser` helper when the tool needs to list pages, open a new page, or work from `browserId` instead of an existing `pageId`.\n- `browser` is not a raw Playwright browser. It exposes `browserId`, `mode`, `listPages()`, `getPage(pageId)`, `getCurrentPage()`, and `newPage({ url?, setAsCurrent? })`.\n- Use the provided `logger` object for instrumentation. It exposes `trace`, `debug`, `info`, `warn`, `error`, and `fatal`, and logs automatically attach to the active custom-tool telemetry context.\n- The logger also exposes `getTraceContext()` so a tool can return the active `traceId` and `spanId` when needed for downstream log inspection.\n- If you need DOM access, call `page.evaluate(...)` from the `run` export instead of assuming browser globals are available at module scope.\n- Keep the script self-contained and avoid relative imports.\n- Return either a plain object, a string, or an MCP-style `CallToolResult`.\n- Prefer read-only behavior unless the goal explicitly requires mutation.\n\nOutput format:\n- Show the full `tools.yaml` content.\n- Show the full `{{ preferredToolName }}.ts` content.\n- If the tool needs additional inputs beyond `pageId` or `browserId`, define them in `inputSchema` and read them from `input`.\n- Filesystem access is allowed because the script runs in Node.js.\n\nExample `tools.yaml` shape:\n```yaml\ntools:\n - name: get_post\n description: Get the current post from the open feed page\n script: get_post.ts\n suggestionActions: Open the author profile and review whether the post is worth engaging with\n capabilities:\n readOnlyHint: true\n openWorldHint: false\n inputSchema:\n type: object\n properties:\n pageId:\n type: string\n browserId:\n type: string\n selector:\n type: string\n required:\n - pageId\n```\n\nExample script shape:\n```ts\nexport const run = async ({ page, browser, input, logger }) => {\n const traceContext = logger.getTraceContext();\n logger.info("reading post", { attributes: { selector: input.selector ?? "body", browserId: browser.browserId } });\n const selector = input.selector ?? "body";\n const text = await page.evaluate((currentSelector) => {\n return document.querySelector(currentSelector)?.textContent ?? "";\n }, selector);\n return {\n pageUrl: page.url(),\n traceContext,\n text,\n };\n};\n```\n';const x={name:`custom_script_authoring`,description:`Generate a browse-tool custom tool folder with tools.yaml and TypeScript scripts.`,arguments:[{name:`toolGoal`,description:`What the custom tool should do on the page`,required:!0},{name:`toolName`,description:`Preferred snake_case tool name`,required:!1},{name:`pageContext`,description:`Optional context about the target page or workflow`,required:!1}]},S=new f;function C(e){let t=e.toolName?.trim()||`custom_page_tool`,n=e.pageContext?.trim()||`No extra page context provided.`;return[{role:`user`,content:{type:`text`,text:S.parseAndRenderSync(b,{toolGoal:e.toolGoal,preferredToolName:t,pageContext:n})}}]}export{e as PLAYWRIGHT_TYPES,r as StdioTransportHandler,a as StreamableHttpTransportHandler,_ as ToolExecutionError,g as UnknownToolError,i as container,n as createContainer,y as createServer,x as customScriptAuthoringPrompt,C as generateCustomScriptAuthoringPrompt};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require(`./streamable-http-
|
|
1
|
+
require(`./streamable-http-SGOVRHb7.cjs`);let e=require(`node:module`);const t=Symbol.for(`__locatorProxyBrand__`);var n=class e{[t]=!0;constructor(e,t){this.page=e,this.steps=t}getByRole(t,n){return new e(this.page,[...this.steps,{type:`role`,role:t,options:n}])}getByText(t,n){return new e(this.page,[...this.steps,{type:`text`,text:t,options:n}])}getByLabel(t,n){return new e(this.page,[...this.steps,{type:`label`,text:t,options:n}])}getByPlaceholder(t,n){return new e(this.page,[...this.steps,{type:`placeholder`,text:t,options:n}])}getByTestId(t){return new e(this.page,[...this.steps,{type:`testId`,testId:t}])}locator(t){return new e(this.page,[...this.steps,{type:`css`,selector:t}])}filter(t){return new e(this.page,[...this.steps,{type:`filter`,options:t}])}first(){return new e(this.page,[...this.steps,{type:`first`}])}last(){return this.nth(-1)}nth(t){return new e(this.page,[...this.steps,{type:`nth`,index:t}])}async click(e){let t=await this.resolveAndMark(e?.timeout);await this.page.click(t)}async fill(e,t){let n=await this.resolveAndMark(t?.timeout);await this.page.fill(n,e)}async type(e,t){let n=await this.resolveAndMark(t?.timeout);await this.page.type(n,e,{delay:t?.delay})}async press(e){let t=await this.resolveAndMark();await this.page.click(t),await this.page.press(e)}async hover(e){let t=await this.resolveAndMark(e?.timeout);await this.page.hover(t)}async selectOption(e){let t=await this.resolveAndMark();await this.page.selectOption(t,e)}async check(){let e=await this.resolveAndMark();await this.page.click(e)}async uncheck(){let e=await this.resolveAndMark();await this.page.click(e)}async isVisible(){let e=this.buildResolveScript(`isVisible`);return await this.page.evaluate(e)??!1}async isHidden(){return!await this.isVisible()}async isEnabled(){return!await this.evaluateProperty(`disabled`)}async isDisabled(){return!!await this.evaluateProperty(`disabled`)}async isChecked(){return!!await this.evaluateProperty(`checked`)}async evaluateProperty(e){let t=this.buildResolveScript(`getProperty`,e);return this.page.evaluate(t)}async textContent(){let e=this.buildResolveScript(`textContent`);return this.page.evaluate(e)}async innerText(){let e=this.buildResolveScript(`innerText`);return await this.page.evaluate(e)??``}async inputValue(){let e=this.buildResolveScript(`inputValue`);return await this.page.evaluate(e)??``}async count(){let e=this.buildResolveScript(`count`);return await this.page.evaluate(e)??0}async waitFor(e){let t=e?.timeout??5e3,n=e?.state??`visible`,r=Date.now();for(;Date.now()-r<t;){if(n===`visible`&&await this.isVisible()||n===`hidden`&&!await this.isVisible()||n===`attached`&&await this.count()>0||n===`detached`&&await this.count()===0)return;await new Promise(e=>setTimeout(e,100))}throw Error(`Locator waitFor("${n}") timed out after ${t}ms`)}async resolveAndMark(e){let t=e??5e3,n=Date.now(),r=`pw-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;for(;Date.now()-n<t;){let e=this.buildResolveScript(`mark`,r);if(await this.page.evaluate(e))return`[data-pw-proxy="${r}"]`;await new Promise(e=>setTimeout(e,100))}throw Error(`Locator could not resolve element within ${t}ms. Steps: ${this.describeSteps()}`)}describeSteps(){return this.steps.map(e=>{switch(e.type){case`role`:return`getByRole("${e.role}"${e.options?.name?`, { name: "${String(e.options.name)}" }`:``})`;case`text`:return`getByText("${String(e.text)}")`;case`label`:return`getByLabel("${String(e.text)}")`;case`placeholder`:return`getByPlaceholder("${String(e.text)}")`;case`testId`:return`getByTestId("${e.testId}")`;case`css`:return`locator("${e.selector}")`;case`filter`:return`filter(...)`;case`first`:return`first()`;case`nth`:return`nth(${e.index})`}}).join(`.`)}serializeSteps(){return this.steps.map(e=>{if(e.type===`text`||e.type===`label`||e.type===`placeholder`)return{...e,text:e.text instanceof RegExp?{__regex__:e.text.source,flags:e.text.flags}:e.text};if(e.type===`role`&&e.options?.name instanceof RegExp)return{...e,options:{...e.options,name:{__regex__:e.options.name.source,flags:e.options.name.flags}}};if(e.type===`filter`){let t={};return e.options.hasText&&(t.hasText=e.options.hasText instanceof RegExp?{__regex__:e.options.hasText.source,flags:e.options.hasText.flags}:e.options.hasText),e.options.has&&(t.hasSteps=e.options.has.serializeSteps()),{type:`filter`,options:t}}return e})}buildResolveScript(e,t){return`(function() {
|
|
2
2
|
var steps = ${JSON.stringify(this.serializeSteps())};
|
|
3
3
|
var action = "${e}";
|
|
4
4
|
var extra = ${t===void 0?`null`:JSON.stringify(t)};
|
|
@@ -249,4 +249,4 @@ require(`./streamable-http-CHbht6Hk.cjs`);let e=require(`node:module`);const t=S
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
return null;
|
|
252
|
-
})()`}};const r=Symbol.for(`__pageProxyBrand__`);async function i(e,t,n=5e3){let r=Date.now();for(;Date.now()-r<n;){if(await e())return;await new Promise(e=>setTimeout(e,100))}throw Error(t)}function a(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e===t}function o(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e.includes(t)}function s(e,t){let n=e=>t?!e:e;return{async toBeVisible(r){await i(async()=>n(await e.isVisible()),t?`Expected element to not be visible`:`Expected element to be visible`,r?.timeout)},async toBeHidden(r){await i(async()=>n(!await e.isVisible()),t?`Expected element to not be hidden`:`Expected element to be hidden`,r?.timeout)},async toBeDisabled(r){await i(async()=>n(!!await e.evaluateProperty(`disabled`)),t?`Expected element to not be disabled`:`Expected element to be disabled`,r?.timeout)},async toBeEnabled(r){await i(async()=>n(!await e.evaluateProperty(`disabled`)),t?`Expected element to not be enabled`:`Expected element to be enabled`,r?.timeout)},async toBeChecked(r){await i(async()=>n(!!await e.evaluateProperty(`checked`)),t?`Expected element to not be checked`:`Expected element to be checked`,r?.timeout)},async toHaveValue(r,o){await i(async()=>n(a(await e.evaluateProperty(`value`),r)),t?`Expected element to not have value "${String(r)}"`:`Expected element to have value "${String(r)}"`,o?.timeout)},async toHaveText(r,o){await i(async()=>n(a((await e.textContent())?.trim()??null,r)),t?`Expected element to not have text "${String(r)}"`:`Expected element to have text "${String(r)}"`,o?.timeout)},async toContainText(r,a){await i(async()=>n(o(await e.textContent(),r)),t?`Expected element to not contain text "${String(r)}"`:`Expected element to contain text "${String(r)}"`,a?.timeout)},async toHaveCount(r,a){await i(async()=>n(await e.count()===r),t?`Expected element count to not be ${r}`:`Expected element count to be ${r}`,a?.timeout)},get not(){return s(e,!t)}}}function c(e,t){let n=e=>t?!e:e;return{async toHaveURL(r,o){await i(async()=>n(a(await e.currentUrlAsync(),r)),t?`Expected page to not have URL "${String(r)}"`:`Expected page to have URL "${String(r)}"`,o?.timeout)},async toHaveTitle(r,o){await i(async()=>n(a(await e.title(),r)),t?`Expected page to not have title "${String(r)}"`:`Expected page to have title "${String(r)}"`,o?.timeout)},get not(){return c(e,!t)}}}function l(e){let n=e;if(n[t])return s(e,!1);if(n[r])return c(e,!1);throw Error(`createExtensionExpect called with unsupported target`)}function u(){try{return(0,e.createRequire)(require(`url`).pathToFileURL(__filename).href).resolve(`playwright/test`),require(`url`).pathToFileURL(__filename).href}catch{return typeof __PLAYWRIGHT_MCP_STUB_PATH__==`string`?`file://${__PLAYWRIGHT_MCP_STUB_PATH__}`:require(`url`).pathToFileURL(__filename).href}}const d=process,f=d.__pw_initiator__;d.__pw_initiator__=void 0;const p=(0,e.createRequire)(u())(`playwright/test`),{expect:m}=p;d.__pw_initiator__=f;const{request:h,chromium:g,firefox:_,webkit:v,devices:y,selectors:b,defineConfig:x,mergeExpects:S,mergeTests:C}=p,w=new Set([`acceptDownloads`,`baseURL`,`bypassCSP`,`colorScheme`,`deviceScaleFactor`,`extraHTTPHeaders`,`forcedColors`,`geolocation`,`hasTouch`,`httpCredentials`,`ignoreHTTPSErrors`,`isMobile`,`javaScriptEnabled`,`locale`,`offline`,`permissions`,`proxy`,`recordHar`,`reducedMotion`,`screen`,`serviceWorkers`,`storageState`,`timezoneId`,`userAgent`,`viewport`]),T=new Set([`browserName`,`channel`,`devtools`,`headless`,`launchOptions`,`slowMo`]),E=new Set([`browser`,`context`,`page`,`playwright`,`request`]);var D=class e{constructor(e,t=null){this.defs=e,this.parent=t}getAllDefs(){let e=this.parent?.getAllDefs()??new Map;return new Map([...e,...this.defs])}extend(t){return new e(t,this)}},O=class{currentSpecPath=``;nextDescribeId=0;rootDescribe;currentDescribe;constructor(){this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}createDescribeBlock(e,t){return{id:this.nextDescribeId++,title:e,tests:[],beforeEachHooks:[],afterEachHooks:[],children:[],parent:t,fixtureScope:null,config:{}}}setSpecPath(e){this.currentSpecPath=e}reset(){this.currentSpecPath=``,this.nextDescribeId=0,this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}addTest(e,t,n=!1,r=!1){let i={title:e,fn:t,fullTitle:this.getFullTitle(e),only:n,skip:r,config:{}};return this.currentDescribe.tests.push(i),i}ensureFixtureScope(e){if(e.size===0||this.currentDescribe.fixtureScope)return;let t=null,n=this.currentDescribe.parent;for(;n;){if(n.fixtureScope){t=n.fixtureScope;break}n=n.parent}this.currentDescribe.fixtureScope=new D(e,t)}configureDescribe(e){this.currentDescribe.config=P(this.currentDescribe.config,e)}enterDescribe(e){let t=this.createDescribeBlock(e,this.currentDescribe);this.currentDescribe.children.push(t),this.currentDescribe=t}exitDescribe(){this.currentDescribe.parent&&(this.currentDescribe=this.currentDescribe.parent)}addBeforeAll(e){this.currentDescribe.beforeAllHooks||(this.currentDescribe.beforeAllHooks=[]),this.currentDescribe.beforeAllHooks.push(e)}addAfterAll(e){this.currentDescribe.afterAllHooks||(this.currentDescribe.afterAllHooks=[]),this.currentDescribe.afterAllHooks.push(e)}addBeforeEach(e){this.currentDescribe.beforeEachHooks.push(e)}addAfterEach(e){this.currentDescribe.afterEachHooks.push(e)}markCurrentDescribeSkipped(){for(let e of this.currentDescribe.tests)e.skip=!0;for(let e of this.currentDescribe.children)this.markDescribeSkipped(e)}markDescribeSkipped(e){for(let t of e.tests)t.skip=!0;for(let t of e.children)this.markDescribeSkipped(t)}getFullTitle(e){let t=[],n=this.currentDescribe;for(;n;)n.title&&t.unshift(n.title),n=n.parent;return t.push(e),t.join(` > `)}collectFixtureDefs(e){return e.fixtureScope?.getAllDefs()??new Map}flattenTests(e,t,n,r={},i){let a=[],o=[...t,...e.beforeEachHooks],s=[...e.afterEachHooks,...n],c=e.beforeAllHooks??[],l=e.afterAllHooks??[],u=P(r,e.config),d=u.mode===`serial`?e.id:i,f=this.collectFixtureDefs(e),{fixtureOverrides:p}=F(u.use),m=I(f,p),h=!1,g=[];for(let t of e.tests)g.push({title:t.title,fullTitle:t.fullTitle,fn:m(async e=>{if(!h&&c.length>0){h=!0;for(let t of c)await t(e)}for(let t of o)await t(e);await t.fn(e);for(let t of s)await t(e)}),only:t.only,skip:t.skip,config:u,serialScopeId:d});for(let t of e.children)g.push(...this.flattenTests(t,o,s,u,d));if(l.length>0&&g.length>0){let e=g[g.length-1],t=e.fn;e.fn=async e=>{try{await t(e)}finally{for(let t of l)await t(e)}}}return a.push(...g),a}retrieveAndReset(){let e=this.flattenTests(this.rootDescribe,[],[]),t={specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length};return this.reset(),t}peek(){let e=this.flattenTests(this.rootDescribe,[],[]);return{specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length}}},k=class extends Error{isSkip=!0;constructor(e){super(e??`Skipped`),this.name=`SkipTestError`}};const A=`__playwrightMcpTestCollector__`,j=globalThis;j[A]||(j[A]=new O);const M=j[A];function N(e){let t=new Map;for(let[n,r]of Object.entries(e))typeof r==`function`?t.set(n,r):Array.isArray(r)&&r.length===2&&typeof r[1]==`function`&&t.set(n,r[1]);return t}function P(e,t){let n={...e};for(let[r,i]of Object.entries(t)){if(r===`use`){n.use={...e.use??{},...i};continue}n[r]=i}return n}function F(e){let t={},n={},r=[];if(!e)return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r};for(let[i,a]of Object.entries(e)){if(w.has(i)){t[i]=a;continue}if(T.has(i)){r.push(i);continue}if(E.has(i)){r.push(i);continue}n[i]=a}return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r}}function I(e,t){if(e.size===0&&Object.keys(t).length===0)return e=>e;let n=[...e.entries()];return e=>async r=>{let i={...r,...t};async function a(r){if(r>=n.length){await e(i);return}let[o,s]=n[r];if(Object.prototype.hasOwnProperty.call(t,o)){i[o]=t[o],await a(r+1);return}await s(i,async e=>{i[o]=e,await a(r+1)})}await a(0)}}function L(e){function t(){M.ensureFixtureScope(e)}function n(e,n){t(),M.addTest(e,n)}n.only=function(e,n){t(),M.addTest(e,n,!0,!1)},n.skip=function(e,n){if(e===void 0||e===!0)throw new k(typeof n==`string`?n:`Skipped`);e!==!1&&typeof e==`string`&&typeof n==`function`&&(t(),M.addTest(e,n,!1,!0))};let r=function(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()};return r.configure=function(e){M.configureDescribe(e)},n.describe=r,n.beforeAll=function(e){t(),M.addBeforeAll(e)},n.afterAll=function(e){t(),M.addAfterAll(e)},n.beforeEach=function(e){t(),M.addBeforeEach(e)},n.afterEach=function(e){t(),M.addAfterEach(e)},n.use=function(e){M.configureDescribe({use:e})},n.extend=function(t){return L(new Map([...e,...N(t)]))},n}const R=function(e,t){M.addTest(e,t)};function z(e,t){M.addTest(e,t,!0,!1)}function B(e,t){if(e===void 0||e===!0)throw new k(typeof t==`string`?t:`Skipped`);e!==!1&&typeof e==`string`&&typeof t==`function`&&M.addTest(e,t,!1,!0)}function V(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()}function H(e){M.addBeforeAll(e)}function U(e){M.addAfterAll(e)}function W(e){M.addBeforeEach(e)}function G(e){M.addAfterEach(e)}const K=V;K.configure=function(e){M.configureDescribe(e)},R.describe=K,R.beforeAll=H,R.afterAll=U,R.beforeEach=W,R.afterEach=G,R.use=function(e){M.configureDescribe({use:e})},R.only=z,R.skip=B,R.extend=function(e){return L(N(e))};function q(e){M.reset(),M.setSpecPath(e)}function J(){return M.retrieveAndReset()}function Y(){return M.peek()}function X(){M.reset()}const Z=(e=>{let n=e;return n?.[t]||n?.[r]?l(e):m(e)});Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return n}});
|
|
252
|
+
})()`}};const r=Symbol.for(`__pageProxyBrand__`);async function i(e,t,n=5e3){let r=Date.now();for(;Date.now()-r<n;){if(await e())return;await new Promise(e=>setTimeout(e,100))}throw Error(t)}function a(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e===t}function o(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e.includes(t)}function s(e,t){let n=e=>t?!e:e;return{async toBeVisible(r){await i(async()=>n(await e.isVisible()),t?`Expected element to not be visible`:`Expected element to be visible`,r?.timeout)},async toBeHidden(r){await i(async()=>n(!await e.isVisible()),t?`Expected element to not be hidden`:`Expected element to be hidden`,r?.timeout)},async toBeDisabled(r){await i(async()=>n(!!await e.evaluateProperty(`disabled`)),t?`Expected element to not be disabled`:`Expected element to be disabled`,r?.timeout)},async toBeEnabled(r){await i(async()=>n(!await e.evaluateProperty(`disabled`)),t?`Expected element to not be enabled`:`Expected element to be enabled`,r?.timeout)},async toBeChecked(r){await i(async()=>n(!!await e.evaluateProperty(`checked`)),t?`Expected element to not be checked`:`Expected element to be checked`,r?.timeout)},async toHaveValue(r,o){await i(async()=>n(a(await e.evaluateProperty(`value`),r)),t?`Expected element to not have value "${String(r)}"`:`Expected element to have value "${String(r)}"`,o?.timeout)},async toHaveText(r,o){await i(async()=>n(a((await e.textContent())?.trim()??null,r)),t?`Expected element to not have text "${String(r)}"`:`Expected element to have text "${String(r)}"`,o?.timeout)},async toContainText(r,a){await i(async()=>n(o(await e.textContent(),r)),t?`Expected element to not contain text "${String(r)}"`:`Expected element to contain text "${String(r)}"`,a?.timeout)},async toHaveCount(r,a){await i(async()=>n(await e.count()===r),t?`Expected element count to not be ${r}`:`Expected element count to be ${r}`,a?.timeout)},get not(){return s(e,!t)}}}function c(e,t){let n=e=>t?!e:e;return{async toHaveURL(r,o){await i(async()=>n(a(await e.currentUrlAsync(),r)),t?`Expected page to not have URL "${String(r)}"`:`Expected page to have URL "${String(r)}"`,o?.timeout)},async toHaveTitle(r,o){await i(async()=>n(a(await e.title(),r)),t?`Expected page to not have title "${String(r)}"`:`Expected page to have title "${String(r)}"`,o?.timeout)},get not(){return c(e,!t)}}}function l(e){let n=e;if(n[t])return s(e,!1);if(n[r])return c(e,!1);throw Error(`createExtensionExpect called with unsupported target`)}function u(){try{return(0,e.createRequire)(require(`url`).pathToFileURL(__filename).href).resolve(`playwright/test`),require(`url`).pathToFileURL(__filename).href}catch{return typeof __PLAYWRIGHT_MCP_STUB_PATH__==`string`?`file://${__PLAYWRIGHT_MCP_STUB_PATH__}`:require(`url`).pathToFileURL(__filename).href}}const d=process,f=d.__pw_initiator__;d.__pw_initiator__=void 0;const p=(0,e.createRequire)(u())(`playwright/test`),{expect:m}=p;d.__pw_initiator__=f;const{request:h,chromium:g,firefox:_,webkit:v,devices:y,selectors:b,defineConfig:x,mergeExpects:S,mergeTests:C}=p,w=new Set([`acceptDownloads`,`baseURL`,`bypassCSP`,`colorScheme`,`deviceScaleFactor`,`extraHTTPHeaders`,`forcedColors`,`geolocation`,`hasTouch`,`httpCredentials`,`ignoreHTTPSErrors`,`isMobile`,`javaScriptEnabled`,`locale`,`offline`,`permissions`,`proxy`,`recordHar`,`reducedMotion`,`screen`,`serviceWorkers`,`storageState`,`timezoneId`,`userAgent`,`viewport`]),T=new Set([`browserName`,`channel`,`devtools`,`headless`,`launchOptions`,`slowMo`]),E=new Set([`browser`,`context`,`page`,`playwright`,`request`]);var D=class e{constructor(e,t=null){this.defs=e,this.parent=t}getAllDefs(){let e=this.parent?.getAllDefs()??new Map;return new Map([...e,...this.defs])}extend(t){return new e(t,this)}},O=class{currentSpecPath=``;nextDescribeId=0;rootDescribe;currentDescribe;constructor(){this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}createDescribeBlock(e,t){return{id:this.nextDescribeId++,title:e,tests:[],beforeEachHooks:[],afterEachHooks:[],children:[],parent:t,fixtureScope:null,config:{}}}setSpecPath(e){this.currentSpecPath=e}reset(){this.currentSpecPath=``,this.nextDescribeId=0,this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}addTest(e,t,n=!1,r=!1){let i={title:e,fn:t,fullTitle:this.getFullTitle(e),only:n,skip:r,config:{}};return this.currentDescribe.tests.push(i),i}ensureFixtureScope(e){if(e.size===0||this.currentDescribe.fixtureScope)return;let t=null,n=this.currentDescribe.parent;for(;n;){if(n.fixtureScope){t=n.fixtureScope;break}n=n.parent}this.currentDescribe.fixtureScope=new D(e,t)}configureDescribe(e){this.currentDescribe.config=P(this.currentDescribe.config,e)}enterDescribe(e){let t=this.createDescribeBlock(e,this.currentDescribe);this.currentDescribe.children.push(t),this.currentDescribe=t}exitDescribe(){this.currentDescribe.parent&&(this.currentDescribe=this.currentDescribe.parent)}addBeforeAll(e){this.currentDescribe.beforeAllHooks||(this.currentDescribe.beforeAllHooks=[]),this.currentDescribe.beforeAllHooks.push(e)}addAfterAll(e){this.currentDescribe.afterAllHooks||(this.currentDescribe.afterAllHooks=[]),this.currentDescribe.afterAllHooks.push(e)}addBeforeEach(e){this.currentDescribe.beforeEachHooks.push(e)}addAfterEach(e){this.currentDescribe.afterEachHooks.push(e)}markCurrentDescribeSkipped(){for(let e of this.currentDescribe.tests)e.skip=!0;for(let e of this.currentDescribe.children)this.markDescribeSkipped(e)}markDescribeSkipped(e){for(let t of e.tests)t.skip=!0;for(let t of e.children)this.markDescribeSkipped(t)}getFullTitle(e){let t=[],n=this.currentDescribe;for(;n;)n.title&&t.unshift(n.title),n=n.parent;return t.push(e),t.join(` > `)}collectFixtureDefs(e){return e.fixtureScope?.getAllDefs()??new Map}flattenTests(e,t,n,r={},i){let a=[],o=[...t,...e.beforeEachHooks],s=[...e.afterEachHooks,...n],c=e.beforeAllHooks??[],l=e.afterAllHooks??[],u=P(r,e.config),d=u.mode===`serial`?e.id:i,f=this.collectFixtureDefs(e),{fixtureOverrides:p}=F(u.use),m=I(f,p),h=!1,g=[];for(let t of e.tests)g.push({title:t.title,fullTitle:t.fullTitle,fn:m(async e=>{if(!h&&c.length>0){h=!0;for(let t of c)await t(e)}for(let t of o)await t(e);await t.fn(e);for(let t of s)await t(e)}),only:t.only,skip:t.skip,config:u,serialScopeId:d});for(let t of e.children)g.push(...this.flattenTests(t,o,s,u,d));if(l.length>0&&g.length>0){let e=g[g.length-1],t=e.fn;e.fn=async e=>{try{await t(e)}finally{for(let t of l)await t(e)}}}return a.push(...g),a}retrieveAndReset(){let e=this.flattenTests(this.rootDescribe,[],[]),t={specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length};return this.reset(),t}peek(){let e=this.flattenTests(this.rootDescribe,[],[]);return{specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length}}},k=class extends Error{isSkip=!0;constructor(e){super(e??`Skipped`),this.name=`SkipTestError`}};const A=`__playwrightMcpTestCollector__`,j=globalThis;j[A]||(j[A]=new O);const M=j[A];function N(e){let t=new Map;for(let[n,r]of Object.entries(e))typeof r==`function`?t.set(n,r):Array.isArray(r)&&r.length===2&&typeof r[1]==`function`&&t.set(n,r[1]);return t}function P(e,t){let n={...e};for(let[r,i]of Object.entries(t)){if(r===`use`){n.use={...e.use,...i};continue}n[r]=i}return n}function F(e){let t={},n={},r=[];if(!e)return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r};for(let[i,a]of Object.entries(e)){if(w.has(i)){t[i]=a;continue}if(T.has(i)){r.push(i);continue}if(E.has(i)){r.push(i);continue}n[i]=a}return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r}}function I(e,t){if(e.size===0&&Object.keys(t).length===0)return e=>e;let n=[...e.entries()];return e=>async r=>{let i={...r,...t};async function a(r){if(r>=n.length){await e(i);return}let[o,s]=n[r];if(Object.prototype.hasOwnProperty.call(t,o)){i[o]=t[o],await a(r+1);return}await s(i,async e=>{i[o]=e,await a(r+1)})}await a(0)}}function L(e){function t(){M.ensureFixtureScope(e)}function n(e,n){t(),M.addTest(e,n)}n.only=function(e,n){t(),M.addTest(e,n,!0,!1)},n.skip=function(e,n){if(e===void 0||e===!0)throw new k(typeof n==`string`?n:`Skipped`);e!==!1&&typeof e==`string`&&typeof n==`function`&&(t(),M.addTest(e,n,!1,!0))};let r=function(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()};return r.configure=function(e){M.configureDescribe(e)},n.describe=r,n.beforeAll=function(e){t(),M.addBeforeAll(e)},n.afterAll=function(e){t(),M.addAfterAll(e)},n.beforeEach=function(e){t(),M.addBeforeEach(e)},n.afterEach=function(e){t(),M.addAfterEach(e)},n.use=function(e){M.configureDescribe({use:e})},n.extend=function(t){return L(new Map([...e,...N(t)]))},n}const R=function(e,t){M.addTest(e,t)};function z(e,t){M.addTest(e,t,!0,!1)}function B(e,t){if(e===void 0||e===!0)throw new k(typeof t==`string`?t:`Skipped`);e!==!1&&typeof e==`string`&&typeof t==`function`&&M.addTest(e,t,!1,!0)}function V(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()}function H(e){M.addBeforeAll(e)}function U(e){M.addAfterAll(e)}function W(e){M.addBeforeEach(e)}function G(e){M.addAfterEach(e)}const K=V;K.configure=function(e){M.configureDescribe(e)},R.describe=K,R.beforeAll=H,R.afterAll=U,R.beforeEach=W,R.afterEach=G,R.use=function(e){M.configureDescribe({use:e})},R.only=z,R.skip=B,R.extend=function(e){return L(N(e))};function q(e){M.reset(),M.setSpecPath(e)}function J(){return M.retrieveAndReset()}function Y(){return M.peek()}function X(){M.reset()}const Z=new Proxy(m,{apply(e,n,i){let a=i[0];return a?.[t]||a?.[r]?l(i[0]):Reflect.apply(e,n,i)}});Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return q}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return n}});
|
|
@@ -249,4 +249,4 @@ import{createRequire as e}from"node:module";const t=Symbol.for(`__locatorProxyBr
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
return null;
|
|
252
|
-
})()`}};const r=Symbol.for(`__pageProxyBrand__`);async function i(e,t,n=5e3){let r=Date.now();for(;Date.now()-r<n;){if(await e())return;await new Promise(e=>setTimeout(e,100))}throw Error(t)}function a(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e===t}function o(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e.includes(t)}function s(e,t){let n=e=>t?!e:e;return{async toBeVisible(r){await i(async()=>n(await e.isVisible()),t?`Expected element to not be visible`:`Expected element to be visible`,r?.timeout)},async toBeHidden(r){await i(async()=>n(!await e.isVisible()),t?`Expected element to not be hidden`:`Expected element to be hidden`,r?.timeout)},async toBeDisabled(r){await i(async()=>n(!!await e.evaluateProperty(`disabled`)),t?`Expected element to not be disabled`:`Expected element to be disabled`,r?.timeout)},async toBeEnabled(r){await i(async()=>n(!await e.evaluateProperty(`disabled`)),t?`Expected element to not be enabled`:`Expected element to be enabled`,r?.timeout)},async toBeChecked(r){await i(async()=>n(!!await e.evaluateProperty(`checked`)),t?`Expected element to not be checked`:`Expected element to be checked`,r?.timeout)},async toHaveValue(r,o){await i(async()=>n(a(await e.evaluateProperty(`value`),r)),t?`Expected element to not have value "${String(r)}"`:`Expected element to have value "${String(r)}"`,o?.timeout)},async toHaveText(r,o){await i(async()=>n(a((await e.textContent())?.trim()??null,r)),t?`Expected element to not have text "${String(r)}"`:`Expected element to have text "${String(r)}"`,o?.timeout)},async toContainText(r,a){await i(async()=>n(o(await e.textContent(),r)),t?`Expected element to not contain text "${String(r)}"`:`Expected element to contain text "${String(r)}"`,a?.timeout)},async toHaveCount(r,a){await i(async()=>n(await e.count()===r),t?`Expected element count to not be ${r}`:`Expected element count to be ${r}`,a?.timeout)},get not(){return s(e,!t)}}}function c(e,t){let n=e=>t?!e:e;return{async toHaveURL(r,o){await i(async()=>n(a(await e.currentUrlAsync(),r)),t?`Expected page to not have URL "${String(r)}"`:`Expected page to have URL "${String(r)}"`,o?.timeout)},async toHaveTitle(r,o){await i(async()=>n(a(await e.title(),r)),t?`Expected page to not have title "${String(r)}"`:`Expected page to have title "${String(r)}"`,o?.timeout)},get not(){return c(e,!t)}}}function l(e){let n=e;if(n[t])return s(e,!1);if(n[r])return c(e,!1);throw Error(`createExtensionExpect called with unsupported target`)}function u(){try{return e(import.meta.url).resolve(`playwright/test`),import.meta.url}catch{return typeof __PLAYWRIGHT_MCP_STUB_PATH__==`string`?`file://${__PLAYWRIGHT_MCP_STUB_PATH__}`:import.meta.url}}const d=process,f=d.__pw_initiator__;d.__pw_initiator__=void 0;const p=e(u())(`playwright/test`),{expect:m}=p;d.__pw_initiator__=f;const{request:h,chromium:g,firefox:_,webkit:v,devices:y,selectors:b,defineConfig:x,mergeExpects:S,mergeTests:C}=p,w=new Set([`acceptDownloads`,`baseURL`,`bypassCSP`,`colorScheme`,`deviceScaleFactor`,`extraHTTPHeaders`,`forcedColors`,`geolocation`,`hasTouch`,`httpCredentials`,`ignoreHTTPSErrors`,`isMobile`,`javaScriptEnabled`,`locale`,`offline`,`permissions`,`proxy`,`recordHar`,`reducedMotion`,`screen`,`serviceWorkers`,`storageState`,`timezoneId`,`userAgent`,`viewport`]),T=new Set([`browserName`,`channel`,`devtools`,`headless`,`launchOptions`,`slowMo`]),E=new Set([`browser`,`context`,`page`,`playwright`,`request`]);var D=class e{constructor(e,t=null){this.defs=e,this.parent=t}getAllDefs(){let e=this.parent?.getAllDefs()??new Map;return new Map([...e,...this.defs])}extend(t){return new e(t,this)}},O=class{currentSpecPath=``;nextDescribeId=0;rootDescribe;currentDescribe;constructor(){this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}createDescribeBlock(e,t){return{id:this.nextDescribeId++,title:e,tests:[],beforeEachHooks:[],afterEachHooks:[],children:[],parent:t,fixtureScope:null,config:{}}}setSpecPath(e){this.currentSpecPath=e}reset(){this.currentSpecPath=``,this.nextDescribeId=0,this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}addTest(e,t,n=!1,r=!1){let i={title:e,fn:t,fullTitle:this.getFullTitle(e),only:n,skip:r,config:{}};return this.currentDescribe.tests.push(i),i}ensureFixtureScope(e){if(e.size===0||this.currentDescribe.fixtureScope)return;let t=null,n=this.currentDescribe.parent;for(;n;){if(n.fixtureScope){t=n.fixtureScope;break}n=n.parent}this.currentDescribe.fixtureScope=new D(e,t)}configureDescribe(e){this.currentDescribe.config=P(this.currentDescribe.config,e)}enterDescribe(e){let t=this.createDescribeBlock(e,this.currentDescribe);this.currentDescribe.children.push(t),this.currentDescribe=t}exitDescribe(){this.currentDescribe.parent&&(this.currentDescribe=this.currentDescribe.parent)}addBeforeAll(e){this.currentDescribe.beforeAllHooks||(this.currentDescribe.beforeAllHooks=[]),this.currentDescribe.beforeAllHooks.push(e)}addAfterAll(e){this.currentDescribe.afterAllHooks||(this.currentDescribe.afterAllHooks=[]),this.currentDescribe.afterAllHooks.push(e)}addBeforeEach(e){this.currentDescribe.beforeEachHooks.push(e)}addAfterEach(e){this.currentDescribe.afterEachHooks.push(e)}markCurrentDescribeSkipped(){for(let e of this.currentDescribe.tests)e.skip=!0;for(let e of this.currentDescribe.children)this.markDescribeSkipped(e)}markDescribeSkipped(e){for(let t of e.tests)t.skip=!0;for(let t of e.children)this.markDescribeSkipped(t)}getFullTitle(e){let t=[],n=this.currentDescribe;for(;n;)n.title&&t.unshift(n.title),n=n.parent;return t.push(e),t.join(` > `)}collectFixtureDefs(e){return e.fixtureScope?.getAllDefs()??new Map}flattenTests(e,t,n,r={},i){let a=[],o=[...t,...e.beforeEachHooks],s=[...e.afterEachHooks,...n],c=e.beforeAllHooks??[],l=e.afterAllHooks??[],u=P(r,e.config),d=u.mode===`serial`?e.id:i,f=this.collectFixtureDefs(e),{fixtureOverrides:p}=F(u.use),m=I(f,p),h=!1,g=[];for(let t of e.tests)g.push({title:t.title,fullTitle:t.fullTitle,fn:m(async e=>{if(!h&&c.length>0){h=!0;for(let t of c)await t(e)}for(let t of o)await t(e);await t.fn(e);for(let t of s)await t(e)}),only:t.only,skip:t.skip,config:u,serialScopeId:d});for(let t of e.children)g.push(...this.flattenTests(t,o,s,u,d));if(l.length>0&&g.length>0){let e=g[g.length-1],t=e.fn;e.fn=async e=>{try{await t(e)}finally{for(let t of l)await t(e)}}}return a.push(...g),a}retrieveAndReset(){let e=this.flattenTests(this.rootDescribe,[],[]),t={specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length};return this.reset(),t}peek(){let e=this.flattenTests(this.rootDescribe,[],[]);return{specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length}}},k=class extends Error{isSkip=!0;constructor(e){super(e??`Skipped`),this.name=`SkipTestError`}};const A=`__playwrightMcpTestCollector__`,j=globalThis;j[A]||(j[A]=new O);const M=j[A];function N(e){let t=new Map;for(let[n,r]of Object.entries(e))typeof r==`function`?t.set(n,r):Array.isArray(r)&&r.length===2&&typeof r[1]==`function`&&t.set(n,r[1]);return t}function P(e,t){let n={...e};for(let[r,i]of Object.entries(t)){if(r===`use`){n.use={...e.use
|
|
252
|
+
})()`}};const r=Symbol.for(`__pageProxyBrand__`);async function i(e,t,n=5e3){let r=Date.now();for(;Date.now()-r<n;){if(await e())return;await new Promise(e=>setTimeout(e,100))}throw Error(t)}function a(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e===t}function o(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e.includes(t)}function s(e,t){let n=e=>t?!e:e;return{async toBeVisible(r){await i(async()=>n(await e.isVisible()),t?`Expected element to not be visible`:`Expected element to be visible`,r?.timeout)},async toBeHidden(r){await i(async()=>n(!await e.isVisible()),t?`Expected element to not be hidden`:`Expected element to be hidden`,r?.timeout)},async toBeDisabled(r){await i(async()=>n(!!await e.evaluateProperty(`disabled`)),t?`Expected element to not be disabled`:`Expected element to be disabled`,r?.timeout)},async toBeEnabled(r){await i(async()=>n(!await e.evaluateProperty(`disabled`)),t?`Expected element to not be enabled`:`Expected element to be enabled`,r?.timeout)},async toBeChecked(r){await i(async()=>n(!!await e.evaluateProperty(`checked`)),t?`Expected element to not be checked`:`Expected element to be checked`,r?.timeout)},async toHaveValue(r,o){await i(async()=>n(a(await e.evaluateProperty(`value`),r)),t?`Expected element to not have value "${String(r)}"`:`Expected element to have value "${String(r)}"`,o?.timeout)},async toHaveText(r,o){await i(async()=>n(a((await e.textContent())?.trim()??null,r)),t?`Expected element to not have text "${String(r)}"`:`Expected element to have text "${String(r)}"`,o?.timeout)},async toContainText(r,a){await i(async()=>n(o(await e.textContent(),r)),t?`Expected element to not contain text "${String(r)}"`:`Expected element to contain text "${String(r)}"`,a?.timeout)},async toHaveCount(r,a){await i(async()=>n(await e.count()===r),t?`Expected element count to not be ${r}`:`Expected element count to be ${r}`,a?.timeout)},get not(){return s(e,!t)}}}function c(e,t){let n=e=>t?!e:e;return{async toHaveURL(r,o){await i(async()=>n(a(await e.currentUrlAsync(),r)),t?`Expected page to not have URL "${String(r)}"`:`Expected page to have URL "${String(r)}"`,o?.timeout)},async toHaveTitle(r,o){await i(async()=>n(a(await e.title(),r)),t?`Expected page to not have title "${String(r)}"`:`Expected page to have title "${String(r)}"`,o?.timeout)},get not(){return c(e,!t)}}}function l(e){let n=e;if(n[t])return s(e,!1);if(n[r])return c(e,!1);throw Error(`createExtensionExpect called with unsupported target`)}function u(){try{return e(import.meta.url).resolve(`playwright/test`),import.meta.url}catch{return typeof __PLAYWRIGHT_MCP_STUB_PATH__==`string`?`file://${__PLAYWRIGHT_MCP_STUB_PATH__}`:import.meta.url}}const d=process,f=d.__pw_initiator__;d.__pw_initiator__=void 0;const p=e(u())(`playwright/test`),{expect:m}=p;d.__pw_initiator__=f;const{request:h,chromium:g,firefox:_,webkit:v,devices:y,selectors:b,defineConfig:x,mergeExpects:S,mergeTests:C}=p,w=new Set([`acceptDownloads`,`baseURL`,`bypassCSP`,`colorScheme`,`deviceScaleFactor`,`extraHTTPHeaders`,`forcedColors`,`geolocation`,`hasTouch`,`httpCredentials`,`ignoreHTTPSErrors`,`isMobile`,`javaScriptEnabled`,`locale`,`offline`,`permissions`,`proxy`,`recordHar`,`reducedMotion`,`screen`,`serviceWorkers`,`storageState`,`timezoneId`,`userAgent`,`viewport`]),T=new Set([`browserName`,`channel`,`devtools`,`headless`,`launchOptions`,`slowMo`]),E=new Set([`browser`,`context`,`page`,`playwright`,`request`]);var D=class e{constructor(e,t=null){this.defs=e,this.parent=t}getAllDefs(){let e=this.parent?.getAllDefs()??new Map;return new Map([...e,...this.defs])}extend(t){return new e(t,this)}},O=class{currentSpecPath=``;nextDescribeId=0;rootDescribe;currentDescribe;constructor(){this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}createDescribeBlock(e,t){return{id:this.nextDescribeId++,title:e,tests:[],beforeEachHooks:[],afterEachHooks:[],children:[],parent:t,fixtureScope:null,config:{}}}setSpecPath(e){this.currentSpecPath=e}reset(){this.currentSpecPath=``,this.nextDescribeId=0,this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}addTest(e,t,n=!1,r=!1){let i={title:e,fn:t,fullTitle:this.getFullTitle(e),only:n,skip:r,config:{}};return this.currentDescribe.tests.push(i),i}ensureFixtureScope(e){if(e.size===0||this.currentDescribe.fixtureScope)return;let t=null,n=this.currentDescribe.parent;for(;n;){if(n.fixtureScope){t=n.fixtureScope;break}n=n.parent}this.currentDescribe.fixtureScope=new D(e,t)}configureDescribe(e){this.currentDescribe.config=P(this.currentDescribe.config,e)}enterDescribe(e){let t=this.createDescribeBlock(e,this.currentDescribe);this.currentDescribe.children.push(t),this.currentDescribe=t}exitDescribe(){this.currentDescribe.parent&&(this.currentDescribe=this.currentDescribe.parent)}addBeforeAll(e){this.currentDescribe.beforeAllHooks||(this.currentDescribe.beforeAllHooks=[]),this.currentDescribe.beforeAllHooks.push(e)}addAfterAll(e){this.currentDescribe.afterAllHooks||(this.currentDescribe.afterAllHooks=[]),this.currentDescribe.afterAllHooks.push(e)}addBeforeEach(e){this.currentDescribe.beforeEachHooks.push(e)}addAfterEach(e){this.currentDescribe.afterEachHooks.push(e)}markCurrentDescribeSkipped(){for(let e of this.currentDescribe.tests)e.skip=!0;for(let e of this.currentDescribe.children)this.markDescribeSkipped(e)}markDescribeSkipped(e){for(let t of e.tests)t.skip=!0;for(let t of e.children)this.markDescribeSkipped(t)}getFullTitle(e){let t=[],n=this.currentDescribe;for(;n;)n.title&&t.unshift(n.title),n=n.parent;return t.push(e),t.join(` > `)}collectFixtureDefs(e){return e.fixtureScope?.getAllDefs()??new Map}flattenTests(e,t,n,r={},i){let a=[],o=[...t,...e.beforeEachHooks],s=[...e.afterEachHooks,...n],c=e.beforeAllHooks??[],l=e.afterAllHooks??[],u=P(r,e.config),d=u.mode===`serial`?e.id:i,f=this.collectFixtureDefs(e),{fixtureOverrides:p}=F(u.use),m=I(f,p),h=!1,g=[];for(let t of e.tests)g.push({title:t.title,fullTitle:t.fullTitle,fn:m(async e=>{if(!h&&c.length>0){h=!0;for(let t of c)await t(e)}for(let t of o)await t(e);await t.fn(e);for(let t of s)await t(e)}),only:t.only,skip:t.skip,config:u,serialScopeId:d});for(let t of e.children)g.push(...this.flattenTests(t,o,s,u,d));if(l.length>0&&g.length>0){let e=g[g.length-1],t=e.fn;e.fn=async e=>{try{await t(e)}finally{for(let t of l)await t(e)}}}return a.push(...g),a}retrieveAndReset(){let e=this.flattenTests(this.rootDescribe,[],[]),t={specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length};return this.reset(),t}peek(){let e=this.flattenTests(this.rootDescribe,[],[]);return{specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length}}},k=class extends Error{isSkip=!0;constructor(e){super(e??`Skipped`),this.name=`SkipTestError`}};const A=`__playwrightMcpTestCollector__`,j=globalThis;j[A]||(j[A]=new O);const M=j[A];function N(e){let t=new Map;for(let[n,r]of Object.entries(e))typeof r==`function`?t.set(n,r):Array.isArray(r)&&r.length===2&&typeof r[1]==`function`&&t.set(n,r[1]);return t}function P(e,t){let n={...e};for(let[r,i]of Object.entries(t)){if(r===`use`){n.use={...e.use,...i};continue}n[r]=i}return n}function F(e){let t={},n={},r=[];if(!e)return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r};for(let[i,a]of Object.entries(e)){if(w.has(i)){t[i]=a;continue}if(T.has(i)){r.push(i);continue}if(E.has(i)){r.push(i);continue}n[i]=a}return{runtimeUse:t,fixtureOverrides:n,unsupportedKeys:r}}function I(e,t){if(e.size===0&&Object.keys(t).length===0)return e=>e;let n=[...e.entries()];return e=>async r=>{let i={...r,...t};async function a(r){if(r>=n.length){await e(i);return}let[o,s]=n[r];if(Object.prototype.hasOwnProperty.call(t,o)){i[o]=t[o],await a(r+1);return}await s(i,async e=>{i[o]=e,await a(r+1)})}await a(0)}}function L(e){function t(){M.ensureFixtureScope(e)}function n(e,n){t(),M.addTest(e,n)}n.only=function(e,n){t(),M.addTest(e,n,!0,!1)},n.skip=function(e,n){if(e===void 0||e===!0)throw new k(typeof n==`string`?n:`Skipped`);e!==!1&&typeof e==`string`&&typeof n==`function`&&(t(),M.addTest(e,n,!1,!0))};let r=function(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()};return r.configure=function(e){M.configureDescribe(e)},n.describe=r,n.beforeAll=function(e){t(),M.addBeforeAll(e)},n.afterAll=function(e){t(),M.addAfterAll(e)},n.beforeEach=function(e){t(),M.addBeforeEach(e)},n.afterEach=function(e){t(),M.addAfterEach(e)},n.use=function(e){M.configureDescribe({use:e})},n.extend=function(t){return L(new Map([...e,...N(t)]))},n}const R=function(e,t){M.addTest(e,t)};function z(e,t){M.addTest(e,t,!0,!1)}function B(e,t){if(e===void 0||e===!0)throw new k(typeof t==`string`?t:`Skipped`);e!==!1&&typeof e==`string`&&typeof t==`function`&&M.addTest(e,t,!1,!0)}function V(e,t){M.enterDescribe(e);try{t()}catch(e){if(e instanceof k)M.markCurrentDescribeSkipped();else throw e}M.exitDescribe()}function H(e){M.addBeforeAll(e)}function U(e){M.addAfterAll(e)}function W(e){M.addBeforeEach(e)}function G(e){M.addAfterEach(e)}const K=V;K.configure=function(e){M.configureDescribe(e)},R.describe=K,R.beforeAll=H,R.afterAll=U,R.beforeEach=W,R.afterEach=G,R.use=function(e){M.configureDescribe({use:e})},R.only=z,R.skip=B,R.extend=function(e){return L(N(e))};function q(e){M.reset(),M.setSpecPath(e)}function J(){return M.retrieveAndReset()}function Y(){return M.peek()}function X(){M.reset()}const Z=new Proxy(m,{apply(e,n,i){let a=i[0];return a?.[t]||a?.[r]?l(i[0]):Reflect.apply(e,n,i)}});export{v as _,Z as a,q as c,Y as d,h as f,R as g,F as h,y as i,S as l,b as m,g as n,_ as o,X as p,x as r,J as s,k as t,C as u,r as v,n as y};
|
|
@@ -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-D--wiuvm.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:module`),S=require(`zod`),C=require(`node:async_hooks`),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-DR8tmuiB.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:module`),S=require(`zod`),C=require(`node:async_hooks`),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}var F;const rt=new Set([`docker`,`docker-vm`,`docker-chromium`,`docker-chrome-testing`,`docker-cloakbrowser`]),it=de,at=fe,ot=me,st=he,ct=4e3,I=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`,lt=[`Current Session`,`Current Tabs`,`Last Session`,`Last Tabs`,`Sessions`],ut=new Set([`Default`,`Guest Profile`,`System Profile`]);let dt=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;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={}){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={}){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||it,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||at,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||pe}:{image:process.env.PLAYWRIGHT_VM_DOCKER_IMAGE||ot,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||st,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(`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,c as t,h as n,p as r,s as i,t as a,v as o,y as s}from"./playwright-test-DlT1Ad7l.mjs";import{stripTypeScriptTypes as c}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as l,DEFAULT_REGISTRY_PATH as u,PortRegistryService as d,PortRegistryStateSchema as f,normalizeRepositoryPath as p}from"@agimon-ai/foundation-port-registry";import{ProcessRegistryService as m}from"@agimon-ai/foundation-process-registry";import{Container as h,ContainerModule as g,inject as _,injectable as v,optional as y}from"inversify";import*as b from"node:path";import x,{basename as S,dirname as C,isAbsolute as w,join as T,relative as E,resolve as D}from"node:path";import O,{constants as ee,createWriteStream as te,existsSync as k,mkdirSync as ne,readFileSync as re,writeFileSync as ie}from"node:fs";import*as ae from"node:fs/promises";import{access as oe,chmod as se,mkdir as A,readFile as j,readdir as ce,rename as le,rm as ue,stat as de,symlink as fe,unlink as pe,writeFile as M}from"node:fs/promises";import N,{arch as me,homedir as he,platform as ge,tmpdir as P}from"node:os";import{execFile as _e,execSync as ve,spawn as F}from"node:child_process";import{fileURLToPath as ye}from"node:url";import be,{chromium as xe,firefox as Se,webkit as Ce}from"playwright";import{Readable as we}from"node:stream";import{pipeline as Te}from"node:stream/promises";import{z as I}from"zod";import{AsyncLocalStorage as Ee}from"node:async_hooks";import{createNodeTelemetry as De}from"@agimon-ai/log-sink-mcp";import{SpanKind as Oe,SpanStatusCode as ke,context as Ae,trace as je}from"@opentelemetry/api";import{OTLPTraceExporter as Me}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes as Ne}from"@opentelemetry/resources";import{BatchSpanProcessor as Pe,NodeTracerProvider as Fe,SimpleSpanProcessor as Ie}from"@opentelemetry/sdk-trace-node";import{promisify as Le}from"node:util";import{parse as Re}from"yaml";import{build as ze}from"esbuild";import{randomUUID as L}from"node:crypto";import{isInitializeRequest as Be}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as Ve}from"@modelcontextprotocol/sdk/server/stdio.js";import{createServer as He}from"node:http";import{StreamableHTTPServerTransport as Ue}from"@modelcontextprotocol/sdk/server/streamableHttp.js";const R={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 z(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function B(e,t){return function(n,r){t(n,r,e)}}function V(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 We=class{sessions=new Map;sessionIdCounter=0;constructor(e,t,n,r,i,a){this.browserService=e,this.specRunner=t,this.specMetadataService=n,this.setupRunner=r,this.webServerManager=i,this.pageRegistry=a}async runSpec(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},browserId:a,pageId:o,hooksPath:s}=e,c=n??`spec-${++this.sessionIdCounter}`,l={id:c,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(c,l);let u=!!(a&&o);try{u&&(l.browserId=a,l.pageIds.push(o)),l.status=`running`,l.updatedAt=new Date;let e=await this.specRunner.executeSpec({specPath:t,sessionId:c,browserType:i.browserType,headless:i.headless,hooksPath:s,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&e.browser){let t=this.browserService.registerExternalBrowser(e.browser);if(l.browserId=t,e.keptPages?.length)for(let n of e.keptPages){let e=await this.pageRegistry.register({page:n.page,context:n.context,browserId:t,onClose:e=>{let t=l.pageIds.indexOf(e);t>=0&&l.pageIds.splice(t,1)}});l.pageIds.push(e)}}return l.status=`completed`,l.updatedAt=new Date,{automationId:c,status:`completed`,pageIds:r||u?[...l.pageIds]:[],browserId:r||u?l.browserId:null,specResult:e}}catch(e){return l.status=`failed`,l.error=e instanceof Error?e.message:String(e),l.updatedAt=new Date,{automationId:c,status:`failed`,pageIds:u?[...l.pageIds]:[],browserId:u?l.browserId:null}}}async runSpecEnhanced(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},testFilter:a,specArgs:o,configPath:s,runSetup:c=!1,startServer:l=!1,stopServerAfter:u=!0,browserId:d,pageId:f,hooksPath:p}=e,m=n??`spec-${++this.sessionIdCounter}`,h={id:m,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(m,h);let g,_;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?C(s):process.cwd();l&&e?.webServer&&(g=await this.webServerManager.startServer(e.webServer,n));let v=!!(d&&f);if(v&&(h.browserId=d,h.pageIds.push(f)),h.status=`running`,h.updatedAt=new Date,c&&e?.setupFile){let t=e.setupFile.startsWith(`/`)?e.setupFile:`${n}/${e.setupFile}`,r=e.setupExport??`default`,a=await this.browserService.launch(i);try{if(_=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!_.success)throw Error(_.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let y=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:m,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,..._?.state},hooksPath:p,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&y.browser){let e=this.browserService.registerExternalBrowser(y.browser);if(h.browserId=e,y.keptPages?.length)for(let t of y.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=h.pageIds.indexOf(e);t>=0&&h.pageIds.splice(t,1)}});h.pageIds.push(n)}}return h.status=`completed`,h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer(),{automationId:m,status:`completed`,pageIds:r||v?[...h.pageIds]:[],browserId:r||v?h.browserId:null,specResult:y,serverResult:g,setupResult:_}}catch(e){h.status=`failed`,h.error=e instanceof Error?e.message:String(e),h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer();let t=!!(d&&f);return{automationId:m,status:`failed`,pageIds:t?[...h.pageIds]:[],browserId:t?h.browserId:null,serverResult:g,setupResult:_}}}getSession(e){return this.sessions.get(e)}listSessions(){return Array.from(this.sessions.values())}async stop(e){let t=this.sessions.get(e);return t?(t.browserId&&await this.browserService.closeBrowser(t.browserId),t.status=`completed`,t.updatedAt=new Date,!0):!1}};We=V([v(),B(0,_(R.BrowserService)),B(1,_(R.SpecRunner)),B(2,_(R.SpecMetadataService)),B(3,_(R.SetupRunner)),B(4,_(R.WebServerManager)),B(5,_(R.PageRegistry)),z(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],We);var Ge=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let Ke=class{platform=N.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=[x.join(e,`SingletonLock`),x.join(e,`SingletonCookie`),x.join(e,`SingletonSocket`),x.join(e,`lockfile`)];for(let e of n)try{O.existsSync(e)&&await pe(e)}catch{}let r=x.join(e,`Default`,`LOCK`);try{O.existsSync(r)&&await pe(r)}catch{}return!0}async ensureAvailable(e){if(!O.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new Ge(`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=x.join(e,`SingletonLock`),r=x.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=O.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=O.existsSync(r)}return!t.isLocked&&O.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return O.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=x.join(e,`lockfile`);if(t.lockFilePath=n,O.existsSync(n))try{let e=await j(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`}}};Ke=V([v()],Ke);var qe=`147.0.7727.50`,Je=`browse-tool/chrome-for-testing:147.0.7727.50`,Ye=`/usr/local/bin/google-chrome-for-testing`,Xe=`linux/amd64`,Ze=`browse-tool/cloakbrowser:latest`,Qe=`/usr/local/bin/cloakbrowser`,$e=`linux/amd64`;const H=18e4;var et;const tt=qe,nt=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function rt(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 it(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:tt}function at(){let e=process.env[nt]?.trim();return e&&e.length>0?e:void 0}function ot(e){return rt(e,`147.0.7727.3`)>=0}function st(){return ot(it())}let ct=class{static{et=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=T(he(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=at();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 k(t)&&n===e?t:this.download(r)}catch(e){if(k(t))return t;throw e}}async isInstalled(){return k(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=T(this.cacheDir,`version.txt`);if(!k(e))return null;try{return(await j(e,`utf-8`)).trim()}catch{return null}}async update(){let e=T(this.cacheDir,`chrome`);return k(e)&&await ue(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){ne(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=T(this.cacheDir,`chrome.zip`),r=T(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await ue(n,{force:!0}),await M(T(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!k(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return ge()!==`win32`&&await se(i,493),i}async fetchVersionInfo(e){let t=await fetch(et.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 it()}async validateConfiguredExecutablePath(e){try{if(!(await de(e)).isFile())throw Error(`configured path is not a file`);return ge()!==`win32`&&await oe(e,ee.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${nt} 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=ge(),t=me();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=ge(),t=T(this.cacheDir,`chrome`);if(e===`darwin`)return T(t,`chrome-mac-${me()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return T(t,`chrome-linux64`,`chrome`);if(e===`win32`)return T(t,`chrome-win${me()===`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=te(r),a=n.body.getReader();await Te(new we({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await le(r,t)}async extractZip(e,t){let n=ge();return new Promise((r,i)=>{let a;n===`win32`?a=F(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):(ne(t,{recursive:!0}),a=F(`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}`))})})}};ct=et=V([v(),z(`design:paramtypes`,[])],ct);const lt=[`pnpm-workspace.yaml`,`nx.json`,`.git`],ut=Je,dt=Xe,ft=ut.split(`:`)[0]??ut,pt=Ze,mt=pt.split(`:`)[0]??pt;function ht(e=process.cwd()){let t=x.resolve(e);for(;;){for(let e of lt)if(gt(x.join(t,e)))return t;let n=x.dirname(t);if(n===t)return e;t=n}}function gt(e){return O.existsSync(e)}function _t(){return ft}function vt(e){return`${_t()}:${e}`}function yt(e){let t=x.resolve(e);for(let e=0;e<8;e+=1){let e=x.join(t,`package.json`);if(gt(e))try{if(JSON.parse(O.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=x.dirname(t);if(n===t)break;t=n}return x.resolve(e,`../..`)}function bt(){let e=x.dirname(ye(import.meta.url));return x.join(yt(e),`docker/chrome-for-testing.Dockerfile`)}function xt(){return ht(yt(x.dirname(ye(import.meta.url))))}function St(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function Ct(e){return(e?.trim()||dt).toLowerCase()}function wt(e){let t=Ct(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function Tt(e){return e.startsWith(`${_t()}:`)}async function Et(e,t){return await new Promise((n,r)=>{let i=``,a=F(`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 Dt(e){return(await Et([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Ot(e={}){let t=e.version?.trim()||St(e.image)||it(),n=e.image?.trim()||vt(t),r=Ct(e.platform),i=wt(r),a=e.stdio??`inherit`,o=await Et([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,bt(),xt()],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 kt(e){return!Tt(e.image)||await Dt(e.image)?!1:(await Ot({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function At(e){return e.startsWith(`${mt}:`)}function jt(){let e=x.dirname(ye(import.meta.url));return x.join(yt(e),`docker/cloakbrowser.Dockerfile`)}async function Mt(e={}){let t=e.image?.trim()||pt,n=Ct(e.platform),r=e.stdio??`inherit`,i=await Et([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,jt(),xt()],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 Nt(e){return!At(e.image)||await Dt(e.image)?!1:(await Mt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Pt(){let e=new Map,t=ve(`ps -axo pid=,ppid=`,{encoding:`utf-8`,stdio:[`ignore`,`pipe`,`ignore`],timeout:3e3});for(let n of t.split(`
|
|
1
|
+
import{a as e,c as t,h as n,p as r,s as i,t as a,v as o,y as s}from"./playwright-test-DoPVFxH9.mjs";import{stripTypeScriptTypes as c}from"node:module";import"reflect-metadata/lite";import{DEFAULT_PORT_RANGE as l,DEFAULT_REGISTRY_PATH as u,PortRegistryService as d,PortRegistryStateSchema as f,normalizeRepositoryPath as p}from"@agimon-ai/foundation-port-registry";import{ProcessRegistryService as m}from"@agimon-ai/foundation-process-registry";import{Container as h,ContainerModule as g,inject as _,injectable as v,optional as y}from"inversify";import*as b from"node:path";import x,{basename as S,dirname as C,isAbsolute as w,join as T,relative as E,resolve as D}from"node:path";import O,{constants as ee,createWriteStream as te,existsSync as k,mkdirSync as ne,readFileSync as re,writeFileSync as ie}from"node:fs";import*as ae from"node:fs/promises";import{access as oe,chmod as se,mkdir as A,readFile as j,readdir as ce,rename as le,rm as ue,stat as de,symlink as fe,unlink as pe,writeFile as M}from"node:fs/promises";import N,{arch as me,homedir as he,platform as ge,tmpdir as P}from"node:os";import{execFile as _e,execSync as ve,spawn as F}from"node:child_process";import{fileURLToPath as ye}from"node:url";import be,{chromium as xe,firefox as Se,webkit as Ce}from"playwright";import{Readable as we}from"node:stream";import{pipeline as Te}from"node:stream/promises";import{z as I}from"zod";import{AsyncLocalStorage as Ee}from"node:async_hooks";import{createNodeTelemetry as De}from"@agimon-ai/log-sink-mcp";import{SpanKind as Oe,SpanStatusCode as ke,context as Ae,trace as je}from"@opentelemetry/api";import{OTLPTraceExporter as Me}from"@opentelemetry/exporter-trace-otlp-http";import{resourceFromAttributes as Ne}from"@opentelemetry/resources";import{BatchSpanProcessor as Pe,NodeTracerProvider as Fe,SimpleSpanProcessor as Ie}from"@opentelemetry/sdk-trace-node";import{promisify as Le}from"node:util";import{parse as Re}from"yaml";import{build as ze}from"esbuild";import{randomUUID as L}from"node:crypto";import{isInitializeRequest as Be}from"@modelcontextprotocol/sdk/types.js";import{StdioServerTransport as Ve}from"@modelcontextprotocol/sdk/server/stdio.js";import{createServer as He}from"node:http";import{StreamableHTTPServerTransport as Ue}from"@modelcontextprotocol/sdk/server/streamableHttp.js";const R={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 z(e,t){if(typeof Reflect==`object`&&typeof Reflect.metadata==`function`)return Reflect.metadata(e,t)}function B(e,t){return function(n,r){t(n,r,e)}}function V(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 We=class{sessions=new Map;sessionIdCounter=0;constructor(e,t,n,r,i,a){this.browserService=e,this.specRunner=t,this.specMetadataService=n,this.setupRunner=r,this.webServerManager=i,this.pageRegistry=a}async runSpec(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},browserId:a,pageId:o,hooksPath:s}=e,c=n??`spec-${++this.sessionIdCounter}`,l={id:c,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(c,l);let u=!!(a&&o);try{u&&(l.browserId=a,l.pageIds.push(o)),l.status=`running`,l.updatedAt=new Date;let e=await this.specRunner.executeSpec({specPath:t,sessionId:c,browserType:i.browserType,headless:i.headless,hooksPath:s,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&e.browser){let t=this.browserService.registerExternalBrowser(e.browser);if(l.browserId=t,e.keptPages?.length)for(let n of e.keptPages){let e=await this.pageRegistry.register({page:n.page,context:n.context,browserId:t,onClose:e=>{let t=l.pageIds.indexOf(e);t>=0&&l.pageIds.splice(t,1)}});l.pageIds.push(e)}}return l.status=`completed`,l.updatedAt=new Date,{automationId:c,status:`completed`,pageIds:r||u?[...l.pageIds]:[],browserId:r||u?l.browserId:null,specResult:e}}catch(e){return l.status=`failed`,l.error=e instanceof Error?e.message:String(e),l.updatedAt=new Date,{automationId:c,status:`failed`,pageIds:u?[...l.pageIds]:[],browserId:u?l.browserId:null}}}async runSpecEnhanced(e){let{specPath:t,automationId:n,keepBrowserOpen:r=!1,browserOptions:i={},testFilter:a,specArgs:o,configPath:s,runSetup:c=!1,startServer:l=!1,stopServerAfter:u=!0,browserId:d,pageId:f,hooksPath:p}=e,m=n??`spec-${++this.sessionIdCounter}`,h={id:m,scriptPath:t,status:`pending`,pageIds:[],browserId:null,error:null,createdAt:new Date,updatedAt:new Date};this.sessions.set(m,h);let g,_;try{let e=s?await this.specMetadataService.loadPlaywrightConfig(s):null,n=s?C(s):process.cwd();l&&e?.webServer&&(g=await this.webServerManager.startServer(e.webServer,n));let v=!!(d&&f);if(v&&(h.browserId=d,h.pageIds.push(f)),h.status=`running`,h.updatedAt=new Date,c&&e?.setupFile){let t=e.setupFile.startsWith(`/`)?e.setupFile:`${n}/${e.setupFile}`,r=e.setupExport??`default`,a=await this.browserService.launch(i);try{if(_=await this.setupRunner.runSetup(t,r,{browser:a.browserInstance.browser,context:a.browserInstance.context,page:a.page}),!_.success)throw Error(_.error??`Setup failed`)}finally{await this.browserService.closeBrowser(a.browserInstance.id)}}let y=await this.specRunner.executeSpecEnhanced({specPath:t,sessionId:m,browserType:i.browserType,headless:i.headless,testFilter:a,specArgs:{...o,..._?.state},hooksPath:p,baseURL:i.baseURL,keepBrowserOpen:r});if(r&&y.browser){let e=this.browserService.registerExternalBrowser(y.browser);if(h.browserId=e,y.keptPages?.length)for(let t of y.keptPages){let n=await this.pageRegistry.register({page:t.page,context:t.context,browserId:e,onClose:e=>{let t=h.pageIds.indexOf(e);t>=0&&h.pageIds.splice(t,1)}});h.pageIds.push(n)}}return h.status=`completed`,h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer(),{automationId:m,status:`completed`,pageIds:r||v?[...h.pageIds]:[],browserId:r||v?h.browserId:null,specResult:y,serverResult:g,setupResult:_}}catch(e){h.status=`failed`,h.error=e instanceof Error?e.message:String(e),h.updatedAt=new Date,u&&g?.started&&await this.webServerManager.stopServer();let t=!!(d&&f);return{automationId:m,status:`failed`,pageIds:t?[...h.pageIds]:[],browserId:t?h.browserId:null,serverResult:g,setupResult:_}}}getSession(e){return this.sessions.get(e)}listSessions(){return Array.from(this.sessions.values())}async stop(e){let t=this.sessions.get(e);return t?(t.browserId&&await this.browserService.closeBrowser(t.browserId),t.status=`completed`,t.updatedAt=new Date,!0):!1}};We=V([v(),B(0,_(R.BrowserService)),B(1,_(R.SpecRunner)),B(2,_(R.SpecMetadataService)),B(3,_(R.SetupRunner)),B(4,_(R.WebServerManager)),B(5,_(R.PageRegistry)),z(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],We);var Ge=class extends Error{constructor(e,t,n){super(e),this.pid=t,this.userDataDir=n,this.name=`BrowserSessionConflictError`}};let Ke=class{platform=N.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=[x.join(e,`SingletonLock`),x.join(e,`SingletonCookie`),x.join(e,`SingletonSocket`),x.join(e,`lockfile`)];for(let e of n)try{O.existsSync(e)&&await pe(e)}catch{}let r=x.join(e,`Default`,`LOCK`);try{O.existsSync(r)&&await pe(r)}catch{}return!0}async ensureAvailable(e){if(!O.existsSync(e))return;let t=await this.checkLock(e);if(t.isLocked){if(t.isProcessAlive)throw new Ge(`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=x.join(e,`SingletonLock`),r=x.join(e,`SingletonSocket`);if(t.lockFilePath=n,t.socketPath=r,this.symlinkExists(n))try{let e=O.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=O.existsSync(r)}return!t.isLocked&&O.existsSync(r)&&(t.isLocked=!0,t.isProcessAlive=!0),t}symlinkExists(e){try{return O.lstatSync(e).isSymbolicLink()}catch{return!1}}async checkWindowsLock(e,t){let n=x.join(e,`lockfile`);if(t.lockFilePath=n,O.existsSync(n))try{let e=await j(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`}}};Ke=V([v()],Ke);var qe=`147.0.7727.50`,Je=`browse-tool/chrome-for-testing:147.0.7727.50`,Ye=`/usr/local/bin/google-chrome-for-testing`,Xe=`linux/amd64`,Ze=`browse-tool/cloakbrowser:latest`,Qe=`/usr/local/bin/cloakbrowser`,$e=`linux/amd64`;const H=18e4;var et;const tt=qe,nt=`BROWSE_TOOL_CHROME_FOR_TESTING_PATH`;function rt(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 it(){let e=process.env.BROWSE_TOOL_CHROME_FOR_TESTING_VERSION?.trim();return e&&e.length>0?e:tt}function at(){let e=process.env[nt]?.trim();return e&&e.length>0?e:void 0}function ot(e){return rt(e,`147.0.7727.3`)>=0}function st(){return ot(it())}let ct=class{static{et=this}static API_URL=`https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json`;cacheDir;constructor(){this.cacheDir=T(he(),`.cache`,`chrome-for-testing`)}async getExecutablePath(){let e=at();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 k(t)&&n===e?t:this.download(r)}catch(e){if(k(t))return t;throw e}}async isInstalled(){return k(this.getExpectedExecutablePath())}async getInstalledVersion(){let e=T(this.cacheDir,`version.txt`);if(!k(e))return null;try{return(await j(e,`utf-8`)).trim()}catch{return null}}async update(){let e=T(this.cacheDir,`chrome`);return k(e)&&await ue(e,{recursive:!0,force:!0}),this.download(await this.fetchVersionInfo(this.getRequestedVersion()))}async download(e){ne(this.cacheDir,{recursive:!0});let t=this.getDownloadUrl(e);if(!t)throw Error(`No Chrome for Testing download available for ${this.getPlatformKey()}`);let n=T(this.cacheDir,`chrome.zip`),r=T(this.cacheDir,`chrome`);await this.downloadFile(t,n),await this.extractZip(n,r),await ue(n,{force:!0}),await M(T(this.cacheDir,`version.txt`),e.version,`utf-8`);let i=this.getExpectedExecutablePath();if(!k(i))throw Error(`Chrome for Testing executable not found at expected path: ${i}`);return ge()!==`win32`&&await se(i,493),i}async fetchVersionInfo(e){let t=await fetch(et.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 it()}async validateConfiguredExecutablePath(e){try{if(!(await de(e)).isFile())throw Error(`configured path is not a file`);return ge()!==`win32`&&await oe(e,ee.X_OK),e}catch(t){throw Error(`Chrome for Testing executable configured by ${nt} 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=ge(),t=me();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=ge(),t=T(this.cacheDir,`chrome`);if(e===`darwin`)return T(t,`chrome-mac-${me()===`arm64`?`arm64`:`x64`}`,`Google Chrome for Testing.app`,`Contents`,`MacOS`,`Google Chrome for Testing`);if(e===`linux`)return T(t,`chrome-linux64`,`chrome`);if(e===`win32`)return T(t,`chrome-win${me()===`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=te(r),a=n.body.getReader();await Te(new we({async read(){let{done:e,value:t}=await a.read();e?this.push(null):this.push(Buffer.from(t))}}),i),await le(r,t)}async extractZip(e,t){let n=ge();return new Promise((r,i)=>{let a;n===`win32`?a=F(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -Path "${e}" -DestinationPath "${t}" -Force`]):(ne(t,{recursive:!0}),a=F(`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}`))})})}};ct=et=V([v(),z(`design:paramtypes`,[])],ct);const lt=[`pnpm-workspace.yaml`,`nx.json`,`.git`],ut=Je,dt=Xe,ft=ut.split(`:`)[0]??ut,pt=Ze,mt=pt.split(`:`)[0]??pt;function ht(e=process.cwd()){let t=x.resolve(e);for(;;){for(let e of lt)if(gt(x.join(t,e)))return t;let n=x.dirname(t);if(n===t)return e;t=n}}function gt(e){return O.existsSync(e)}function _t(){return ft}function vt(e){return`${_t()}:${e}`}function yt(e){let t=x.resolve(e);for(let e=0;e<8;e+=1){let e=x.join(t,`package.json`);if(gt(e))try{if(JSON.parse(O.readFileSync(e,`utf8`)).name===`@agimon-ai/browse-tool`)return t}catch{}let n=x.dirname(t);if(n===t)break;t=n}return x.resolve(e,`../..`)}function bt(){let e=x.dirname(ye(import.meta.url));return x.join(yt(e),`docker/chrome-for-testing.Dockerfile`)}function xt(){return ht(yt(x.dirname(ye(import.meta.url))))}function St(e){if(!e?.includes(`:`))return;let[,t]=e.split(`:`,2),n=t?.trim();if(!(!n||!/^\d+\.\d+\.\d+\.\d+$/.test(n)))return n}function Ct(e){return(e?.trim()||dt).toLowerCase()}function wt(e){let t=Ct(e);return t.includes(`arm64`)||t.includes(`aarch64`)?`linux-arm64`:`linux64`}function Tt(e){return e.startsWith(`${_t()}:`)}async function Et(e,t){return await new Promise((n,r)=>{let i=``,a=F(`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 Dt(e){return(await Et([`image`,`inspect`,e],`ignore`)).exitCode===0}async function Ot(e={}){let t=e.version?.trim()||St(e.image)||it(),n=e.image?.trim()||vt(t),r=Ct(e.platform),i=wt(r),a=e.stdio??`inherit`,o=await Et([`buildx`,`build`,`--platform`,r,`--load`,`-t`,n,`--build-arg`,`CFT_VERSION=${t}`,`--build-arg`,`CFT_ARCHIVE_PLATFORM=${i}`,`-f`,bt(),xt()],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 kt(e){return!Tt(e.image)||await Dt(e.image)?!1:(await Ot({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function At(e){return e.startsWith(`${mt}:`)}function jt(){let e=x.dirname(ye(import.meta.url));return x.join(yt(e),`docker/cloakbrowser.Dockerfile`)}async function Mt(e={}){let t=e.image?.trim()||pt,n=Ct(e.platform),r=e.stdio??`inherit`,i=await Et([`buildx`,`build`,`--platform`,n,`--load`,`-t`,t,`-f`,jt(),xt()],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 Nt(e){return!At(e.image)||await Dt(e.image)?!1:(await Mt({image:e.image,platform:e.platform,stdio:`inherit`}),!0)}function Pt(){let e=new Map,t=ve(`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 Ft(e){let t=[];try{let n;try{n=ve(`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=Pt().get(e)??[]}for(let e of n)t.push(...Ft(e)),t.push(e)}catch{}return t}function It(e){try{return process.kill(e,0),!0}catch{return!1}}async function Lt(e,t=3e3){if(!It(e))return;let n=[...Ft(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(It))return;await new Promise(e=>setTimeout(e,100))}for(let e of n)try{It(e)&&process.kill(e,`SIGKILL`)}catch{}}function Rt(e,t){let n=x.join(N.tmpdir(),`proxy-auth-ext-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);O.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 O.writeFileSync(x.join(n,`manifest.json`),JSON.stringify(r,null,2)),O.writeFileSync(x.join(n,`background.js`),i),n}function zt(e){let t=[`--proxy-server=${e.server}`];return e.bypass&&t.push(`--proxy-bypass-list=${e.bypass}`),t}var U;const Bt=new Set([`docker`,`docker-vm`,`docker-chromium`,`docker-chrome-testing`,`docker-cloakbrowser`]),Vt=Je,Ht=Ye,Ut=Ze,Wt=Qe,Gt=4e3,Kt=process.env.BROWSE_TOOL_DEBUG_EXTENSION_RECORDING===`1`,qt=[`Current Session`,`Current Tabs`,`Last Session`,`Last Tabs`,`Sessions`],Jt=new Set([`Default`,`Guest Profile`,`System Profile`]);let Yt=class{static{U=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;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={}){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={}){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=x.join(o,`manifest.json`);if(!O.existsSync(s))throw Error(`Extension manifest not found at ${s}. Please build the extension first: pnpm build:extension`);let c=i||(t?`browser-${t}`:`browser-${++this.browserIdCounter}`);if(i&&this.browsers.has(i))throw Error(`Browser ID "${i}" is already in use`);t&&(await this.profileService.get(t)||await this.profileService.create({name:t,browserType:`chromium`}));let l=this.createUserDataDir(t);l&&await this.lockManager.ensureAvailable(l);let u=await this.resolveVmRuntime({command:n,launchMode:a,vmDockerBrowser:e.vmDockerBrowser,extensionHostPath:o,requestedRuntimeExtensionPath:e.extensionPath,userDataDir:l});if(u.kind===`docker`)try{this.ensureDockerWritableUserDataDir(l)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to prepare Docker user-data directory "${l}": ${t}`,{cause:e})}let d=[u.extensionPath],f=[];if(u.kind===`docker`&&f.push(...this.createDockerVolumeArgs(o,u.extensionPath,!0)),e.proxy?.username&&e.proxy?.password){let t=Rt(e.proxy.username,e.proxy.password);if(u.kind===`docker`){let e=`${u.docker.extensionPathInContainer}-proxy-auth-${Date.now()}`;d.push(e),f.push(...this.createDockerVolumeArgs(t,e,!0))}else d.push(t)}let p=d.join(`,`),m=process.env.BROWSE_TOOL_AUTO_SELECT_TAB_CAPTURE_SOURCE_TITLE?.trim(),h=process.env.BROWSE_TOOL_AUTO_SELECT_DESKTOP_CAPTURE_SOURCE?.trim(),g=process.env.BROWSE_TOOL_USE_FAKE_UI_FOR_MEDIA_STREAM===`1`,_=u.kind===`docker`||process.platform===`linux`&&typeof process.getuid==`function`&&process.getuid()===0,v=[`--user-data-dir=${u.userDataDir}`,`--load-extension=${p}`,`--disable-extensions-except=${p}`,`--enable-extensions`,`--auto-accept-this-tab-capture`,...g?[`--use-fake-ui-for-media-stream`]:[],...m?[`--auto-select-tab-capture-source-by-title=${m}`]:[],...h?[`--auto-select-desktop-capture-source=${h}`]:[],`--disable-blink-features=AutomationControlled`,`--disable-infobars`,`--no-first-run`,`--no-default-browser-check`,`--disable-background-timer-throttling`,`--disable-backgrounding-occluded-windows`,`--disable-renderer-backgrounding`,`--disable-component-update`,`--disable-features=TranslateUI`,`--disable-ipc-flooding-protection`,...u.kind===`docker`?[`--disable-gpu`]:[],`--silent-debugger-extension-api`,`--password-store=basic`,`--use-mock-keychain`,`--disable-dev-shm-usage`,`--webrtc-ip-handling-policy=disable_non_proxied_udp`,`--enforce-webrtc-ip-permission-check`,`--lang=${process.env.LANG?.split(`.`)[0]?.replace(`_`,`-`)||`en-US`}`,..._?[`--no-sandbox`,`--disable-setuid-sandbox`]:[],`--window-size=1920,1080`,...e.proxy?zt(e.proxy):[],e.url??`about:blank`],y=e=>{try{let t=new URL(e);return t.protocol===`http:`||t.protocol===`https:`||t.protocol===`file:`}catch{return!1}},b=e=>{if(!y(e))return e;try{let t=new URL(e),n=t.pathname.endsWith(`/`)&&t.pathname!==`/`?t.pathname.slice(0,-1):t.pathname;return`${t.protocol}//${t.host}${n}${t.search}`}catch{return e}},S=b(e.url??`about:blank`),C=a===`vm`&&r.length>0?[...v,...r.filter(e=>y(e)?b(e)!==S:!0)]:r.length>0?r:v,w=u.kind===`docker`?{TZ:Intl.DateTimeFormat().resolvedOptions().timeZone,...e.dockerEnv}:e.dockerEnv,T=u.kind===`docker`?[...this.buildDockerSpawnArgs(u.docker,l,f,e.dockerRunArgs,w,e.dockerEnableVnc),...C]:C,E,D=0,ee;for(let n=1;n<=3;n+=1)try{let n=()=>this.startExtensionBrowser({browserId:c,launchMode:a,profileName:t,vmRuntime:u,spawnArgs:T,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||ee)){let e=[D>0?`Docker retry cleanup attempts: ${D}.`:``,ee?`Last Docker retry cleanup: ${ee}.`:``].filter(Boolean).join(` `);E.message=`${E.message} ${e}`.trim()}throw E}u.kind===`docker`&&(D+=1,ee=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 kt({image:i,platform:s}):await Nt({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&&!O.existsSync(x.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||Vt,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||Ht,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||Xe}:{image:process.env.PLAYWRIGHT_VM_DOCKER_IMAGE||Ut,browserBinary:process.env.PLAYWRIGHT_VM_DOCKER_BROWSER_BINARY||Wt,platform:process.env.PLAYWRIGHT_VM_DOCKER_PLATFORM||$e}}createDockerVolumeArgs(e,t,n){return[`-v`,`${x.resolve(e)}:${t}${n?`:ro`:``}`]}ensureDockerWritableUserDataDir(e){O.mkdirSync(e,{recursive:!0,mode:511});let t=[e];for(;t.length>0;){let e=t.pop();if(!e)continue;O.chmodSync(e,511);let n=O.readdirSync(e,{withFileTypes:!0});for(let r of n){let n=x.join(e,r.name);O.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(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../playwright-test-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../playwright-test-DR8tmuiB.cjs`);exports.SkipTestError=e.t,exports.chromium=e.n,exports.defineConfig=e.r,exports.devices=e.i,exports.expect=e.a,exports.firefox=e.o,exports.getCollectedSuite=e.s,exports.initCollector=e.c,exports.mergeExpects=e.l,exports.mergeTests=e.u,exports.peekCollectedSuite=e.d,exports.request=e.f,exports.resetCollector=e.p,exports.selectors=e.m,exports.splitUseConfig=e.h,exports.test=e.g,exports.webkit=e._;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g}from"../playwright-test-
|
|
1
|
+
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g}from"../playwright-test-DoPVFxH9.mjs";export{h as SkipTestError,u as chromium,p as defineConfig,s as devices,t as expect,d as firefox,m as getCollectedSuite,n as initCollector,c as mergeExpects,g as mergeTests,r as peekCollectedSuite,i as request,f as resetCollector,l as selectors,o as splitUseConfig,a as test,e as webkit};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/browse-tool",
|
|
3
3
|
"description": "MCP server for browser automation using Playwright with profile management, page registry, and multi-browser support",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.1",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"ws": "8.20.0",
|
|
45
45
|
"yaml": "2.8.3",
|
|
46
46
|
"zod": "4.4.1",
|
|
47
|
-
"@agimon-ai/foundation-
|
|
48
|
-
"@agimon-ai/log-sink-mcp": "0.
|
|
49
|
-
"@agimon-ai/foundation-validator": "0.
|
|
50
|
-
"@agimon-ai/foundation-
|
|
47
|
+
"@agimon-ai/foundation-process-registry": "0.11.1",
|
|
48
|
+
"@agimon-ai/log-sink-mcp": "0.11.1",
|
|
49
|
+
"@agimon-ai/foundation-validator": "0.8.1",
|
|
50
|
+
"@agimon-ai/foundation-port-registry": "0.11.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/chrome": "0.1.40",
|