@midscene/web 1.9.8-beta-20260618091332.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/es/{mcp-agent-init-args.mjs → agent-init-args.mjs} +2 -2
  2. package/dist/es/{mcp-agent-init-args.mjs.map → agent-init-args.mjs.map} +1 -1
  3. package/dist/es/{mcp-tools-cdp.mjs → agent-tools-cdp.mjs} +5 -5
  4. package/dist/es/agent-tools-cdp.mjs.map +1 -0
  5. package/dist/es/{mcp-tools-puppeteer.mjs → agent-tools-puppeteer.mjs} +7 -7
  6. package/dist/es/agent-tools-puppeteer.mjs.map +1 -0
  7. package/dist/es/{mcp-tools.mjs → agent-tools.mjs} +4 -4
  8. package/dist/es/agent-tools.mjs.map +1 -0
  9. package/dist/es/bridge-mode/io-client.mjs +1 -1
  10. package/dist/es/bridge-mode/io-server.mjs +2 -2
  11. package/dist/es/bridge-mode/io-server.mjs.map +1 -1
  12. package/dist/es/bridge-mode/page-browser-side.mjs +1 -1
  13. package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -1
  14. package/dist/es/cdp-proxy-constants.mjs.map +1 -1
  15. package/dist/es/cdp-proxy-manager.mjs +1 -1
  16. package/dist/es/cdp-proxy-manager.mjs.map +1 -1
  17. package/dist/es/cdp-proxy.mjs.map +1 -1
  18. package/dist/es/cdp-target-store.mjs +1 -1
  19. package/dist/es/cdp-target-store.mjs.map +1 -1
  20. package/dist/es/chrome-extension/page.mjs +1 -0
  21. package/dist/es/chrome-extension/page.mjs.map +1 -1
  22. package/dist/es/cli.mjs +4 -4
  23. package/dist/es/cli.mjs.map +1 -1
  24. package/dist/es/index.mjs +2 -2
  25. package/dist/es/playwright/index.mjs +5 -3
  26. package/dist/es/playwright/index.mjs.map +1 -1
  27. package/dist/es/puppeteer/base-page.mjs +24 -38
  28. package/dist/es/puppeteer/base-page.mjs.map +1 -1
  29. package/dist/es/puppeteer/index.mjs +5 -3
  30. package/dist/es/puppeteer/index.mjs.map +1 -1
  31. package/dist/es/web-element.mjs.map +1 -1
  32. package/dist/lib/{mcp-agent-init-args.js → agent-init-args.js} +2 -2
  33. package/dist/lib/{mcp-agent-init-args.js.map → agent-init-args.js.map} +1 -1
  34. package/dist/lib/{mcp-tools-cdp.js → agent-tools-cdp.js} +7 -7
  35. package/dist/lib/agent-tools-cdp.js.map +1 -0
  36. package/dist/lib/{mcp-tools-puppeteer.js → agent-tools-puppeteer.js} +9 -9
  37. package/dist/lib/agent-tools-puppeteer.js.map +1 -0
  38. package/dist/lib/{mcp-tools.js → agent-tools.js} +6 -6
  39. package/dist/lib/agent-tools.js.map +1 -0
  40. package/dist/lib/bridge-mode/io-client.js +1 -1
  41. package/dist/lib/bridge-mode/io-server.js +2 -2
  42. package/dist/lib/bridge-mode/io-server.js.map +1 -1
  43. package/dist/lib/bridge-mode/page-browser-side.js +1 -1
  44. package/dist/lib/bridge-mode/page-browser-side.js.map +1 -1
  45. package/dist/lib/cdp-proxy-constants.js.map +1 -1
  46. package/dist/lib/cdp-proxy-manager.js +1 -1
  47. package/dist/lib/cdp-proxy-manager.js.map +1 -1
  48. package/dist/lib/cdp-proxy.js.map +1 -1
  49. package/dist/lib/cdp-target-store.js +1 -1
  50. package/dist/lib/cdp-target-store.js.map +1 -1
  51. package/dist/lib/chrome-extension/page.js +1 -0
  52. package/dist/lib/chrome-extension/page.js.map +1 -1
  53. package/dist/lib/cli.js +5 -5
  54. package/dist/lib/cli.js.map +1 -1
  55. package/dist/lib/index.js +4 -4
  56. package/dist/lib/playwright/index.js +5 -3
  57. package/dist/lib/playwright/index.js.map +1 -1
  58. package/dist/lib/puppeteer/base-page.js +24 -38
  59. package/dist/lib/puppeteer/base-page.js.map +1 -1
  60. package/dist/lib/puppeteer/index.js +5 -3
  61. package/dist/lib/puppeteer/index.js.map +1 -1
  62. package/dist/lib/web-element.js.map +1 -1
  63. package/dist/types/{mcp-agent-init-args.d.ts → agent-init-args.d.ts} +1 -1
  64. package/dist/types/{mcp-tools-cdp.d.ts → agent-tools-cdp.d.ts} +4 -4
  65. package/dist/types/{mcp-tools-puppeteer.d.ts → agent-tools-puppeteer.d.ts} +4 -4
  66. package/dist/types/{mcp-tools.d.ts → agent-tools.d.ts} +4 -4
  67. package/dist/types/cdp-proxy-manager.d.ts +1 -1
  68. package/dist/types/cdp-proxy.d.ts +1 -1
  69. package/dist/types/index.d.ts +2 -2
  70. package/dist/types/puppeteer/base-page.d.ts +1 -10
  71. package/dist/types/web-element.d.ts +3 -0
  72. package/package.json +4 -9
  73. package/dist/es/mcp-server.mjs +0 -35
  74. package/dist/es/mcp-server.mjs.map +0 -1
  75. package/dist/es/mcp-tools-cdp.mjs.map +0 -1
  76. package/dist/es/mcp-tools-puppeteer.mjs.map +0 -1
  77. package/dist/es/mcp-tools.mjs.map +0 -1
  78. package/dist/lib/mcp-server.js +0 -75
  79. package/dist/lib/mcp-server.js.map +0 -1
  80. package/dist/lib/mcp-tools-cdp.js.map +0 -1
  81. package/dist/lib/mcp-tools-puppeteer.js.map +0 -1
  82. package/dist/lib/mcp-tools.js.map +0 -1
  83. package/dist/types/mcp-server.d.ts +0 -26
@@ -1 +1 @@
1
- {"version":3,"file":"cdp-proxy.mjs","sources":["../../src/cdp-proxy.ts"],"sourcesContent":["/**\n * CDP WebSocket Proxy — standalone process.\n *\n * Holds a persistent WebSocket connection to Chrome's CDP endpoint and\n * exposes a local WebSocket server. Midscene CLI processes connect to the\n * proxy instead of Chrome directly, so Chrome's \"Allow remote debugging\"\n * permission popup only fires once (when the proxy connects).\n *\n * Lifecycle notes:\n * - When all downstream clients disconnect the proxy stays running but\n * marks the upstream as needing reconnection. The actual reconnect is\n * deferred to the moment the next client connects, so Chrome's CDP\n * state (notably Target.setDiscoverTargets) is reset and the new\n * client receives all targetCreated events.\n * - On startup, if another proxy is already alive the new instance\n * announces \"duplicate proxy detected\" on stderr and exits 0 without\n * touching the existing metadata files.\n *\n * Exit conditions:\n * 1. Upstream Chrome connection closes or errors.\n * 2. No downstream client message for IDLE_TIMEOUT_MS (default 5 min).\n * 3. SIGTERM / SIGINT.\n * 4. Duplicate proxy detected on startup (exits 0 with stderr notice).\n *\n * Usage (spawned by mcp-tools-cdp.ts):\n * node cdp-proxy.js <chrome-ws-endpoint>\n *\n * On startup, prints the proxy endpoint to stdout as a single JSON line:\n * {\"endpoint\":\"ws://127.0.0.1:<port>/devtools/browser\"}\n * and writes:\n * - PROXY_ENDPOINT_FILE — the local proxy URL above\n * - PROXY_PID_FILE — this process's pid\n * - PROXY_UPSTREAM_FILE — the Chrome endpoint the proxy is connected to,\n * so callers can detect when the requested\n * upstream has changed and replace the proxy.\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { createServer } from 'node:http';\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n PROXY_ENDPOINT_FILE,\n PROXY_PID_FILE,\n PROXY_UPSTREAM_FILE,\n} from './cdp-proxy-constants';\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nconst IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\nconst chromeEndpoint = process.argv[2];\nif (!chromeEndpoint) {\n process.stderr.write('Usage: node cdp-proxy.js <chrome-ws-endpoint>\\n');\n process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// Cleanup\n// ---------------------------------------------------------------------------\n\nfunction cleanupIfOwned() {\n // Only clean up if the PID file exists and records *our* PID.\n // If the file is missing (e.g. already deleted by killProxy()) or\n // unreadable, skip cleanup — another process may have taken over.\n try {\n if (!existsSync(PROXY_PID_FILE)) return;\n const pid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());\n if (pid !== process.pid) return;\n } catch {\n return;\n }\n try {\n if (existsSync(PROXY_ENDPOINT_FILE)) unlinkSync(PROXY_ENDPOINT_FILE);\n } catch {}\n try {\n if (existsSync(PROXY_PID_FILE)) unlinkSync(PROXY_PID_FILE);\n } catch {}\n try {\n if (existsSync(PROXY_UPSTREAM_FILE)) unlinkSync(PROXY_UPSTREAM_FILE);\n } catch {}\n}\n\n/**\n * Maximum time to wait for the stderr drain callback before forcing exit.\n * The callback should normally fire within microseconds, but if the pipe\n * has been closed by the parent it may never run. 500ms is generous\n * enough to be effectively unreachable in the happy path while still\n * keeping the process from hanging if something goes wrong.\n */\nconst STDERR_FLUSH_FALLBACK_MS = 500;\n\n/**\n * Exit after the stderr diagnostic has been flushed.\n *\n * When the proxy's stderr is a pipe (parent uses stdio 'pipe'), Node's\n * process.stderr.write() is asynchronous on POSIX. Calling process.exit()\n * immediately afterwards can drop the pending write, which would silently\n * lose the very diagnostic the caller is relying on. Wait for the drain\n * callback before exiting, with a short fallback timer in case the\n * callback never fires (e.g. the pipe is already closed).\n */\nfunction exitWithStderr(message: string, code = 0): void {\n let exited = false;\n const doExit = () => {\n if (exited) return;\n exited = true;\n process.exit(code);\n };\n const fallback = setTimeout(doExit, STDERR_FLUSH_FALLBACK_MS);\n fallback.unref?.();\n try {\n process.stderr.write(message, () => doExit());\n } catch {\n doExit();\n }\n}\n\nfunction shutdown(reason: string): void {\n cleanupIfOwned();\n exitWithStderr(`[cdp-proxy] shutting down: ${reason}\\n`, 0);\n}\n\nprocess.on('SIGTERM', () => shutdown('SIGTERM'));\nprocess.on('SIGINT', () => shutdown('SIGINT'));\nprocess.on('uncaughtException', (e) => shutdown(`uncaught: ${e.message}`));\n\n// ---------------------------------------------------------------------------\n// Idle timer\n// ---------------------------------------------------------------------------\n\nlet idleTimer: ReturnType<typeof setTimeout> | null = null;\n\nfunction resetIdleTimer() {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = setTimeout(\n () => shutdown('idle timeout (5min)'),\n IDLE_TIMEOUT_MS,\n );\n}\n\nresetIdleTimer();\n\n// ---------------------------------------------------------------------------\n// Upstream: connect to Chrome\n// ---------------------------------------------------------------------------\n\nconst clients = new Set<WebSocket>();\n\n/**\n * Whether we are intentionally reconnecting the upstream WebSocket.\n * When true, the old upstream's close/error events should not trigger shutdown.\n */\nlet reconnecting = false;\n\n/**\n * Whether the upstream WebSocket needs to be reconnected before the next\n * client can use it. Set to true when all downstream clients disconnect;\n * the actual reconnect is deferred until a new client connects so that\n * Chrome's permission popup only fires when someone actually needs it.\n */\nlet needsUpstreamReconnect = false;\n\n/**\n * Messages from downstream clients that arrived while the upstream WebSocket\n * was not yet open (e.g. during a reconnect). Flushed once upstream opens.\n */\nconst pendingUpstreamMessages: {\n data: WebSocket.RawData;\n isBinary: boolean;\n}[] = [];\n\n/**\n * Create a new upstream WebSocket to Chrome and bind its event handlers.\n * Used for both initial connection and reconnection.\n */\nfunction createUpstream(endpoint: string): WebSocket {\n const ws = new WebSocket(endpoint);\n\n ws.on('error', (err) => {\n // A failed reconnect must not leave the flag stuck on.\n reconnecting = false;\n shutdown(`upstream error: ${err.message}`);\n });\n\n ws.on('close', (code, reasonBuf) => {\n if (reconnecting) return;\n const reason = reasonBuf?.toString?.() || '';\n const detail = reason\n ? ` (code=${code}, reason=${reason})`\n : ` (code=${code})`;\n shutdown(`upstream closed${detail}`);\n });\n\n // Forward upstream messages to all downstream clients\n ws.on('message', (data, isBinary) => {\n resetIdleTimer();\n for (const client of clients) {\n if (client.readyState === WebSocket.OPEN) {\n client.send(data, { binary: isBinary });\n }\n }\n });\n\n ws.on('open', () => {\n reconnecting = false;\n for (const msg of pendingUpstreamMessages) {\n ws.send(msg.data, { binary: msg.isBinary });\n }\n pendingUpstreamMessages.length = 0;\n });\n\n return ws;\n}\n\nlet upstream = createUpstream(chromeEndpoint);\n\n/**\n * Reconnect the upstream WebSocket to Chrome.\n *\n * Called when all downstream clients have disconnected. This resets the CDP\n * protocol state on Chrome's side — critically, the Target.setDiscoverTargets\n * subscription — so the next client that connects gets a fresh session and\n * receives all targetCreated events.\n *\n * `reconnecting` is cleared by the new upstream's `open` (or `error`) handler,\n * not here — the new socket is still CONNECTING when this returns.\n */\nfunction reconnectUpstream() {\n reconnecting = true;\n upstream.removeAllListeners();\n upstream.close();\n upstream = createUpstream(chromeEndpoint);\n resetIdleTimer();\n}\n\n// ---------------------------------------------------------------------------\n// Downstream: local WebSocket server\n// ---------------------------------------------------------------------------\n\nconst httpServer = createServer((_req, res) => {\n res.writeHead(404);\n res.end();\n});\n\nconst wss = new WebSocketServer({ server: httpServer });\n\nwss.on('connection', (clientWs) => {\n // Reconnect the upstream WebSocket if the previous session ended.\n // This resets Chrome's CDP protocol state (Target.setDiscoverTargets, etc.)\n // so the new client receives all targetCreated events.\n if (needsUpstreamReconnect && upstream.readyState === WebSocket.OPEN) {\n reconnectUpstream();\n needsUpstreamReconnect = false;\n }\n\n clients.add(clientWs);\n resetIdleTimer();\n\n // Forward downstream messages to upstream (buffer if upstream is reconnecting)\n clientWs.on('message', (data, isBinary) => {\n resetIdleTimer();\n if (upstream.readyState === WebSocket.OPEN) {\n upstream.send(data, { binary: isBinary });\n } else {\n pendingUpstreamMessages.push({ data, isBinary });\n }\n });\n\n const removeClient = () => {\n clients.delete(clientWs);\n // When all downstream clients disconnect, mark that the upstream needs\n // reconnecting to reset Chrome's CDP protocol state. The actual\n // reconnect is deferred until the next client connects so we don't\n // trigger Chrome's permission popup while nobody is using the proxy.\n if (clients.size === 0) {\n pendingUpstreamMessages.length = 0;\n needsUpstreamReconnect = true;\n }\n };\n\n clientWs.on('close', removeClient);\n clientWs.on('error', removeClient);\n});\n\n// ---------------------------------------------------------------------------\n// Start\n// ---------------------------------------------------------------------------\n\n// Start the HTTP/WebSocket server once the initial upstream connection opens.\n// This listener is added *after* createUpstream() already bound its own 'open'\n// handler (which flushes pendingUpstreamMessages), so both will fire.\nupstream.once('open', () => {\n // Check for duplicate proxy\n if (existsSync(PROXY_PID_FILE)) {\n try {\n const existingPid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());\n if (existingPid !== process.pid) {\n try {\n process.kill(existingPid, 0);\n // Another proxy is alive — exit without cleanupIfOwned() (we don't\n // own the metadata files). Announce the reason on stderr so the\n // parent process can distinguish this path from upstream failures,\n // then bail out of this open handler so we don't proceed to listen().\n exitWithStderr(\n `[cdp-proxy] duplicate proxy detected (existing pid=${existingPid})\\n`,\n 0,\n );\n return;\n } catch {\n // dead — we take over\n }\n }\n } catch {}\n }\n\n httpServer.listen(0, '127.0.0.1', () => {\n const addr = httpServer.address();\n if (!addr || typeof addr === 'string') {\n shutdown('failed to get server address');\n return;\n }\n\n const proxyEndpoint = `ws://127.0.0.1:${addr.port}/devtools/browser`;\n\n writeFileSync(PROXY_ENDPOINT_FILE, proxyEndpoint);\n writeFileSync(PROXY_PID_FILE, String(process.pid));\n writeFileSync(PROXY_UPSTREAM_FILE, chromeEndpoint);\n\n process.stdout.write(`${JSON.stringify({ endpoint: proxyEndpoint })}\\n`);\n });\n});\n"],"names":["IDLE_TIMEOUT_MS","chromeEndpoint","process","cleanupIfOwned","existsSync","PROXY_PID_FILE","pid","Number","readFileSync","PROXY_ENDPOINT_FILE","unlinkSync","PROXY_UPSTREAM_FILE","STDERR_FLUSH_FALLBACK_MS","exitWithStderr","message","code","exited","doExit","fallback","setTimeout","shutdown","reason","e","idleTimer","resetIdleTimer","clearTimeout","clients","Set","reconnecting","needsUpstreamReconnect","pendingUpstreamMessages","createUpstream","endpoint","ws","WebSocket","err","reasonBuf","detail","data","isBinary","client","msg","upstream","reconnectUpstream","httpServer","createServer","_req","res","wss","WebSocketServer","clientWs","removeClient","existingPid","addr","proxyEndpoint","writeFileSync","String","JSON"],"mappings":"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"}
1
+ {"version":3,"file":"cdp-proxy.mjs","sources":["../../src/cdp-proxy.ts"],"sourcesContent":["/**\n * CDP WebSocket Proxy — standalone process.\n *\n * Holds a persistent WebSocket connection to Chrome's CDP endpoint and\n * exposes a local WebSocket server. Midscene CLI processes connect to the\n * proxy instead of Chrome directly, so Chrome's \"Allow remote debugging\"\n * permission popup only fires once (when the proxy connects).\n *\n * Lifecycle notes:\n * - When all downstream clients disconnect the proxy stays running but\n * marks the upstream as needing reconnection. The actual reconnect is\n * deferred to the moment the next client connects, so Chrome's CDP\n * state (notably Target.setDiscoverTargets) is reset and the new\n * client receives all targetCreated events.\n * - On startup, if another proxy is already alive the new instance\n * announces \"duplicate proxy detected\" on stderr and exits 0 without\n * touching the existing metadata files.\n *\n * Exit conditions:\n * 1. Upstream Chrome connection closes or errors.\n * 2. No downstream client message for IDLE_TIMEOUT_MS (default 5 min).\n * 3. SIGTERM / SIGINT.\n * 4. Duplicate proxy detected on startup (exits 0 with stderr notice).\n *\n * Usage (spawned by agent-tools-cdp.ts):\n * node cdp-proxy.js <chrome-ws-endpoint>\n *\n * On startup, prints the proxy endpoint to stdout as a single JSON line:\n * {\"endpoint\":\"ws://127.0.0.1:<port>/devtools/browser\"}\n * and writes:\n * - PROXY_ENDPOINT_FILE — the local proxy URL above\n * - PROXY_PID_FILE — this process's pid\n * - PROXY_UPSTREAM_FILE — the Chrome endpoint the proxy is connected to,\n * so callers can detect when the requested\n * upstream has changed and replace the proxy.\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { createServer } from 'node:http';\nimport WebSocket, { WebSocketServer } from 'ws';\nimport {\n PROXY_ENDPOINT_FILE,\n PROXY_PID_FILE,\n PROXY_UPSTREAM_FILE,\n} from './cdp-proxy-constants';\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nconst IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\nconst chromeEndpoint = process.argv[2];\nif (!chromeEndpoint) {\n process.stderr.write('Usage: node cdp-proxy.js <chrome-ws-endpoint>\\n');\n process.exit(1);\n}\n\n// ---------------------------------------------------------------------------\n// Cleanup\n// ---------------------------------------------------------------------------\n\nfunction cleanupIfOwned() {\n // Only clean up if the PID file exists and records *our* PID.\n // If the file is missing (e.g. already deleted by killProxy()) or\n // unreadable, skip cleanup — another process may have taken over.\n try {\n if (!existsSync(PROXY_PID_FILE)) return;\n const pid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());\n if (pid !== process.pid) return;\n } catch {\n return;\n }\n try {\n if (existsSync(PROXY_ENDPOINT_FILE)) unlinkSync(PROXY_ENDPOINT_FILE);\n } catch {}\n try {\n if (existsSync(PROXY_PID_FILE)) unlinkSync(PROXY_PID_FILE);\n } catch {}\n try {\n if (existsSync(PROXY_UPSTREAM_FILE)) unlinkSync(PROXY_UPSTREAM_FILE);\n } catch {}\n}\n\n/**\n * Maximum time to wait for the stderr drain callback before forcing exit.\n * The callback should normally fire within microseconds, but if the pipe\n * has been closed by the parent it may never run. 500ms is generous\n * enough to be effectively unreachable in the happy path while still\n * keeping the process from hanging if something goes wrong.\n */\nconst STDERR_FLUSH_FALLBACK_MS = 500;\n\n/**\n * Exit after the stderr diagnostic has been flushed.\n *\n * When the proxy's stderr is a pipe (parent uses stdio 'pipe'), Node's\n * process.stderr.write() is asynchronous on POSIX. Calling process.exit()\n * immediately afterwards can drop the pending write, which would silently\n * lose the very diagnostic the caller is relying on. Wait for the drain\n * callback before exiting, with a short fallback timer in case the\n * callback never fires (e.g. the pipe is already closed).\n */\nfunction exitWithStderr(message: string, code = 0): void {\n let exited = false;\n const doExit = () => {\n if (exited) return;\n exited = true;\n process.exit(code);\n };\n const fallback = setTimeout(doExit, STDERR_FLUSH_FALLBACK_MS);\n fallback.unref?.();\n try {\n process.stderr.write(message, () => doExit());\n } catch {\n doExit();\n }\n}\n\nfunction shutdown(reason: string): void {\n cleanupIfOwned();\n exitWithStderr(`[cdp-proxy] shutting down: ${reason}\\n`, 0);\n}\n\nprocess.on('SIGTERM', () => shutdown('SIGTERM'));\nprocess.on('SIGINT', () => shutdown('SIGINT'));\nprocess.on('uncaughtException', (e) => shutdown(`uncaught: ${e.message}`));\n\n// ---------------------------------------------------------------------------\n// Idle timer\n// ---------------------------------------------------------------------------\n\nlet idleTimer: ReturnType<typeof setTimeout> | null = null;\n\nfunction resetIdleTimer() {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = setTimeout(\n () => shutdown('idle timeout (5min)'),\n IDLE_TIMEOUT_MS,\n );\n}\n\nresetIdleTimer();\n\n// ---------------------------------------------------------------------------\n// Upstream: connect to Chrome\n// ---------------------------------------------------------------------------\n\nconst clients = new Set<WebSocket>();\n\n/**\n * Whether we are intentionally reconnecting the upstream WebSocket.\n * When true, the old upstream's close/error events should not trigger shutdown.\n */\nlet reconnecting = false;\n\n/**\n * Whether the upstream WebSocket needs to be reconnected before the next\n * client can use it. Set to true when all downstream clients disconnect;\n * the actual reconnect is deferred until a new client connects so that\n * Chrome's permission popup only fires when someone actually needs it.\n */\nlet needsUpstreamReconnect = false;\n\n/**\n * Messages from downstream clients that arrived while the upstream WebSocket\n * was not yet open (e.g. during a reconnect). Flushed once upstream opens.\n */\nconst pendingUpstreamMessages: {\n data: WebSocket.RawData;\n isBinary: boolean;\n}[] = [];\n\n/**\n * Create a new upstream WebSocket to Chrome and bind its event handlers.\n * Used for both initial connection and reconnection.\n */\nfunction createUpstream(endpoint: string): WebSocket {\n const ws = new WebSocket(endpoint);\n\n ws.on('error', (err) => {\n // A failed reconnect must not leave the flag stuck on.\n reconnecting = false;\n shutdown(`upstream error: ${err.message}`);\n });\n\n ws.on('close', (code, reasonBuf) => {\n if (reconnecting) return;\n const reason = reasonBuf?.toString?.() || '';\n const detail = reason\n ? ` (code=${code}, reason=${reason})`\n : ` (code=${code})`;\n shutdown(`upstream closed${detail}`);\n });\n\n // Forward upstream messages to all downstream clients\n ws.on('message', (data, isBinary) => {\n resetIdleTimer();\n for (const client of clients) {\n if (client.readyState === WebSocket.OPEN) {\n client.send(data, { binary: isBinary });\n }\n }\n });\n\n ws.on('open', () => {\n reconnecting = false;\n for (const msg of pendingUpstreamMessages) {\n ws.send(msg.data, { binary: msg.isBinary });\n }\n pendingUpstreamMessages.length = 0;\n });\n\n return ws;\n}\n\nlet upstream = createUpstream(chromeEndpoint);\n\n/**\n * Reconnect the upstream WebSocket to Chrome.\n *\n * Called when all downstream clients have disconnected. This resets the CDP\n * protocol state on Chrome's side — critically, the Target.setDiscoverTargets\n * subscription — so the next client that connects gets a fresh session and\n * receives all targetCreated events.\n *\n * `reconnecting` is cleared by the new upstream's `open` (or `error`) handler,\n * not here — the new socket is still CONNECTING when this returns.\n */\nfunction reconnectUpstream() {\n reconnecting = true;\n upstream.removeAllListeners();\n upstream.close();\n upstream = createUpstream(chromeEndpoint);\n resetIdleTimer();\n}\n\n// ---------------------------------------------------------------------------\n// Downstream: local WebSocket server\n// ---------------------------------------------------------------------------\n\nconst httpServer = createServer((_req, res) => {\n res.writeHead(404);\n res.end();\n});\n\nconst wss = new WebSocketServer({ server: httpServer });\n\nwss.on('connection', (clientWs) => {\n // Reconnect the upstream WebSocket if the previous session ended.\n // This resets Chrome's CDP protocol state (Target.setDiscoverTargets, etc.)\n // so the new client receives all targetCreated events.\n if (needsUpstreamReconnect && upstream.readyState === WebSocket.OPEN) {\n reconnectUpstream();\n needsUpstreamReconnect = false;\n }\n\n clients.add(clientWs);\n resetIdleTimer();\n\n // Forward downstream messages to upstream (buffer if upstream is reconnecting)\n clientWs.on('message', (data, isBinary) => {\n resetIdleTimer();\n if (upstream.readyState === WebSocket.OPEN) {\n upstream.send(data, { binary: isBinary });\n } else {\n pendingUpstreamMessages.push({ data, isBinary });\n }\n });\n\n const removeClient = () => {\n clients.delete(clientWs);\n // When all downstream clients disconnect, mark that the upstream needs\n // reconnecting to reset Chrome's CDP protocol state. The actual\n // reconnect is deferred until the next client connects so we don't\n // trigger Chrome's permission popup while nobody is using the proxy.\n if (clients.size === 0) {\n pendingUpstreamMessages.length = 0;\n needsUpstreamReconnect = true;\n }\n };\n\n clientWs.on('close', removeClient);\n clientWs.on('error', removeClient);\n});\n\n// ---------------------------------------------------------------------------\n// Start\n// ---------------------------------------------------------------------------\n\n// Start the HTTP/WebSocket server once the initial upstream connection opens.\n// This listener is added *after* createUpstream() already bound its own 'open'\n// handler (which flushes pendingUpstreamMessages), so both will fire.\nupstream.once('open', () => {\n // Check for duplicate proxy\n if (existsSync(PROXY_PID_FILE)) {\n try {\n const existingPid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());\n if (existingPid !== process.pid) {\n try {\n process.kill(existingPid, 0);\n // Another proxy is alive — exit without cleanupIfOwned() (we don't\n // own the metadata files). Announce the reason on stderr so the\n // parent process can distinguish this path from upstream failures,\n // then bail out of this open handler so we don't proceed to listen().\n exitWithStderr(\n `[cdp-proxy] duplicate proxy detected (existing pid=${existingPid})\\n`,\n 0,\n );\n return;\n } catch {\n // dead — we take over\n }\n }\n } catch {}\n }\n\n httpServer.listen(0, '127.0.0.1', () => {\n const addr = httpServer.address();\n if (!addr || typeof addr === 'string') {\n shutdown('failed to get server address');\n return;\n }\n\n const proxyEndpoint = `ws://127.0.0.1:${addr.port}/devtools/browser`;\n\n writeFileSync(PROXY_ENDPOINT_FILE, proxyEndpoint);\n writeFileSync(PROXY_PID_FILE, String(process.pid));\n writeFileSync(PROXY_UPSTREAM_FILE, chromeEndpoint);\n\n process.stdout.write(`${JSON.stringify({ endpoint: proxyEndpoint })}\\n`);\n });\n});\n"],"names":["IDLE_TIMEOUT_MS","chromeEndpoint","process","cleanupIfOwned","existsSync","PROXY_PID_FILE","pid","Number","readFileSync","PROXY_ENDPOINT_FILE","unlinkSync","PROXY_UPSTREAM_FILE","STDERR_FLUSH_FALLBACK_MS","exitWithStderr","message","code","exited","doExit","fallback","setTimeout","shutdown","reason","e","idleTimer","resetIdleTimer","clearTimeout","clients","Set","reconnecting","needsUpstreamReconnect","pendingUpstreamMessages","createUpstream","endpoint","ws","WebSocket","err","reasonBuf","detail","data","isBinary","client","msg","upstream","reconnectUpstream","httpServer","createServer","_req","res","wss","WebSocketServer","clientWs","removeClient","existingPid","addr","proxyEndpoint","writeFileSync","String","JSON"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { getDebug } from "@midscene/shared/logger";
3
3
  import { TARGET_ID_FILE } from "./cdp-proxy-constants.mjs";
4
- const debug = getDebug('mcp:cdp:target-store');
4
+ const debug = getDebug('agent-tools:cdp:target-store');
5
5
  function readSavedTargetId() {
6
6
  if (!existsSync(TARGET_ID_FILE)) return null;
7
7
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"cdp-target-store.mjs","sources":["../../src/cdp-target-store.ts"],"sourcesContent":["/**\n * Persistent store for the CDP-mode \"current tab\" targetId.\n *\n * The Midscene CLI runs each command as a fresh Node process, so anything\n * the previous command knew about which tab was being driven must survive\n * across processes. This store writes the chosen targetId to a temp file\n * after `connect`/`act`/etc. succeed, and the next command reads it back\n * to bind to the exact same tab — even when Chrome holds 14 of them.\n *\n * Owns nothing else. The CDP proxy lifecycle and its own metadata files\n * live in `cdp-proxy-manager.ts`.\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { TARGET_ID_FILE } from './cdp-proxy-constants';\n\nconst debug = getDebug('mcp:cdp:target-store');\n\n/**\n * Read the saved targetId, or null if no command has stored one yet.\n */\nexport function readSavedTargetId(): string | null {\n if (!existsSync(TARGET_ID_FILE)) return null;\n try {\n return readFileSync(TARGET_ID_FILE, 'utf-8').trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Save a targetId so the next CLI command can rebind to the same tab.\n */\nexport function saveTargetId(targetId: string): void {\n try {\n writeFileSync(TARGET_ID_FILE, targetId, 'utf-8');\n debug('Saved targetId: %s', targetId);\n } catch (err) {\n debug('Failed to save targetId: %s', err);\n }\n}\n\n/**\n * Discard the saved targetId — call when disconnecting or when the\n * upstream Chrome changes (the targetId would point into the old\n * browser's tab list).\n */\nexport function cleanupTargetIdFile(): void {\n try {\n if (existsSync(TARGET_ID_FILE)) unlinkSync(TARGET_ID_FILE);\n } catch {}\n}\n"],"names":["debug","getDebug","readSavedTargetId","existsSync","TARGET_ID_FILE","readFileSync","saveTargetId","targetId","writeFileSync","err","cleanupTargetIdFile","unlinkSync"],"mappings":";;;AAiBA,MAAMA,QAAQC,SAAS;AAKhB,SAASC;IACd,IAAI,CAACC,WAAWC,iBAAiB,OAAO;IACxC,IAAI;QACF,OAAOC,aAAaD,gBAAgB,SAAS,IAAI,MAAM;IACzD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAKO,SAASE,aAAaC,QAAgB;IAC3C,IAAI;QACFC,cAAcJ,gBAAgBG,UAAU;QACxCP,MAAM,sBAAsBO;IAC9B,EAAE,OAAOE,KAAK;QACZT,MAAM,+BAA+BS;IACvC;AACF;AAOO,SAASC;IACd,IAAI;QACF,IAAIP,WAAWC,iBAAiBO,WAAWP;IAC7C,EAAE,OAAM,CAAC;AACX"}
1
+ {"version":3,"file":"cdp-target-store.mjs","sources":["../../src/cdp-target-store.ts"],"sourcesContent":["/**\n * Persistent store for the CDP-mode \"current tab\" targetId.\n *\n * The Midscene CLI runs each command as a fresh Node process, so anything\n * the previous command knew about which tab was being driven must survive\n * across processes. This store writes the chosen targetId to a temp file\n * after `connect`/`act`/etc. succeed, and the next command reads it back\n * to bind to the exact same tab — even when Chrome holds 14 of them.\n *\n * Owns nothing else. The CDP proxy lifecycle and its own metadata files\n * live in `cdp-proxy-manager.ts`.\n */\n\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { getDebug } from '@midscene/shared/logger';\nimport { TARGET_ID_FILE } from './cdp-proxy-constants';\n\nconst debug = getDebug('agent-tools:cdp:target-store');\n\n/**\n * Read the saved targetId, or null if no command has stored one yet.\n */\nexport function readSavedTargetId(): string | null {\n if (!existsSync(TARGET_ID_FILE)) return null;\n try {\n return readFileSync(TARGET_ID_FILE, 'utf-8').trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Save a targetId so the next CLI command can rebind to the same tab.\n */\nexport function saveTargetId(targetId: string): void {\n try {\n writeFileSync(TARGET_ID_FILE, targetId, 'utf-8');\n debug('Saved targetId: %s', targetId);\n } catch (err) {\n debug('Failed to save targetId: %s', err);\n }\n}\n\n/**\n * Discard the saved targetId — call when disconnecting or when the\n * upstream Chrome changes (the targetId would point into the old\n * browser's tab list).\n */\nexport function cleanupTargetIdFile(): void {\n try {\n if (existsSync(TARGET_ID_FILE)) unlinkSync(TARGET_ID_FILE);\n } catch {}\n}\n"],"names":["debug","getDebug","readSavedTargetId","existsSync","TARGET_ID_FILE","readFileSync","saveTargetId","targetId","writeFileSync","err","cleanupTargetIdFile","unlinkSync"],"mappings":";;;AAiBA,MAAMA,QAAQC,SAAS;AAKhB,SAASC;IACd,IAAI,CAACC,WAAWC,iBAAiB,OAAO;IACxC,IAAI;QACF,OAAOC,aAAaD,gBAAgB,SAAS,IAAI,MAAM;IACzD,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAKO,SAASE,aAAaC,QAAgB;IAC3C,IAAI;QACFC,cAAcJ,gBAAgBG,UAAU;QACxCP,MAAM,sBAAsBO;IAC9B,EAAE,OAAOE,KAAK;QACZT,MAAM,+BAA+BS;IACvC;AACF;AAOO,SAASC;IACd,IAAI;QACF,IAAIP,WAAWC,iBAAiBO,WAAWP;IAC7C,EAAE,OAAM,CAAC;AACX"}
@@ -207,6 +207,7 @@ class ChromeExtensionProxyPage {
207
207
  async registerFileChooserListener(handler) {
208
208
  const registrationVersion = ++this.fileChooserRegistrationVersion;
209
209
  const tabId = await this.getTabIdOrConnectToCurrentTab();
210
+ await this.ensureDebuggerAttached();
210
211
  await this.sendCommandToDebugger('Page.enable', {});
211
212
  await this.sendCommandToDebugger('DOM.enable', {});
212
213
  await this.sendCommandToDebugger('Page.setInterceptFileChooserDialog', {
@@ -1 +1 @@
1
- {"version":3,"file":"chrome-extension/page.mjs","sources":["../../../src/chrome-extension/page.ts"],"sourcesContent":["/// <reference types=\"chrome\" />\n\n/*\n It is used to interact with the page tab from the chrome extension.\n The page must be active when interacting with it.\n*/\n\nimport { limitOpenNewTabScript } from '@/web-element';\nimport type {\n ElementCacheFeature,\n ElementTreeNode,\n Point,\n Rect,\n Size,\n} from '@midscene/core';\nimport type {\n AbstractInterface,\n DeviceAction,\n FileChooserHandler,\n FileChooserRegistration,\n} from '@midscene/core/device';\nimport type { ElementInfo } from '@midscene/shared/extractor';\nimport { treeToList } from '@midscene/shared/extractor';\nimport { createImgBase64ByFormat } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { Protocol as CDPTypes } from 'devtools-protocol';\nimport {\n type CacheFeatureOptions,\n type WebElementCacheFeature,\n buildRectFromElementInfo,\n judgeOrderSensitive,\n sanitizeXpaths,\n} from '../common/cache-helper';\nimport {\n type KeyInput,\n type MouseButton,\n commonWebActionsForWebPage,\n} from '../web-page';\nimport { CdpKeyboard } from './cdpInput';\nimport {\n getHtmlElementScript,\n injectStopWaterFlowAnimation,\n injectWaterFlowAnimation,\n} from './dynamic-scripts';\n\nconst debug = getDebug('web:chrome-extension:page');\n\nfunction sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction hasFlatNodeAttribute(\n attributes: string[] | undefined,\n name: string,\n): boolean {\n if (!attributes) return false;\n for (let i = 0; i < attributes.length; i += 2) {\n if (attributes[i] === name) return true;\n }\n return false;\n}\n\nfunction serializeError(error: Error): {\n message: string;\n name?: string;\n stack?: string;\n} {\n return {\n message: error.message,\n name: error.name,\n stack: error.stack,\n };\n}\n\nexport default class ChromeExtensionProxyPage implements AbstractInterface {\n interfaceType = 'chrome-extension-proxy';\n\n public forceSameTabNavigation: boolean;\n\n private viewportSize?: Size;\n\n private activeTabId: number | null = null;\n\n private destroyed = false;\n\n private fileChooserEventHandler?: Parameters<\n typeof chrome.debugger.onEvent.addListener\n >[0];\n\n private fileChooserRegistrationVersion = 0;\n\n private bridgeFileChooserDispose?: () => void;\n\n private bridgeFileChooserGetError?: FileChooserRegistration['getError'];\n\n private isMobileEmulation: boolean | null = null;\n\n public _continueWhenFailedToAttachDebugger = false;\n\n constructor(forceSameTabNavigation: boolean) {\n this.forceSameTabNavigation = forceSameTabNavigation;\n }\n\n actionSpace(): DeviceAction[] {\n return commonWebActionsForWebPage(this);\n }\n\n public async setActiveTabId(tabId: number) {\n if (this.activeTabId) {\n throw new Error(\n `Active tab id is already set, which is ${this.activeTabId}, cannot set it to ${tabId}`,\n );\n }\n await chrome.tabs.update(tabId, { active: true });\n this.activeTabId = tabId;\n }\n\n public async getActiveTabId() {\n return this.activeTabId;\n }\n\n /**\n * Get a list of current tabs\n * @returns {Promise<Array<{id: number, title: string, url: string}>>}\n */\n public async getBrowserTabList(): Promise<\n { id: string; title: string; url: string; currentActiveTab: boolean }[]\n > {\n const tabs = await chrome.tabs.query({ currentWindow: true });\n return tabs\n .map((tab) => ({\n id: `${tab.id}`,\n title: tab.title,\n url: tab.url,\n currentActiveTab: tab.active,\n }))\n .filter((tab) => tab.id && tab.title && tab.url) as {\n id: string;\n title: string;\n url: string;\n currentActiveTab: boolean;\n }[];\n }\n\n public async getTabIdOrConnectToCurrentTab() {\n if (this.activeTabId) {\n // alway keep on the connected tab\n return this.activeTabId;\n }\n const tabId = await chrome.tabs\n .query({ active: true, currentWindow: true })\n .then((tabs) => tabs[0]?.id);\n this.activeTabId = tabId || 0;\n return this.activeTabId;\n }\n\n /**\n * Ensure debugger is attached to the current tab.\n * Uses lazy attach pattern - only attaches when needed.\n */\n private async ensureDebuggerAttached() {\n assert(!this.destroyed, 'Page is destroyed');\n\n const url = await this.url();\n if (url.startsWith('chrome://')) {\n throw new Error(\n 'Cannot attach debugger to chrome:// pages, please use Midscene in a normal page with http://, https:// or file://',\n );\n }\n\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n try {\n await chrome.debugger.attach({ tabId }, '1.3');\n console.log('Debugger attached to tab:', tabId);\n } catch (error) {\n const errorMsg = (error as Error)?.message || '';\n // Already attached is OK, we can continue\n if (errorMsg.includes('Another debugger is already attached')) {\n console.log('Debugger already attached to tab:', tabId);\n return;\n }\n\n if (this._continueWhenFailedToAttachDebugger) {\n console.warn(\n 'Failed to attach debugger, but continuing due to _continueWhenFailedToAttachDebugger flag',\n error,\n );\n return;\n }\n\n throw error;\n }\n\n // Wait for debugger banner in Chrome to appear\n await sleep(500);\n\n // Enable water flow animation. Non-fatal: this is a purely visual\n // overlay, and Chrome can briefly detach the debugger between\n // attach() and the eval below (cross-origin navigation / Site\n // Isolation race). If we awaited it and it threw \"Debugger is not\n // attached\", the error would propagate out of the catch block in\n // sendCommandToDebugger, preventing the actual CDP command from\n // ever being retried. Fire-and-forget here so the lazy-attach\n // retry can succeed even when the animation fails.\n this.enableWaterFlowAnimation().catch((err) => {\n console.warn('Failed to enable water flow animation:', err);\n });\n }\n\n private async showMousePointer(x: number, y: number) {\n // update mouse pointer while redirecting\n const pointerScript = `(() => {\n if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {\n window.midsceneWaterFlowAnimation.enable();\n window.midsceneWaterFlowAnimation.showMousePointer(${x}, ${y});\n } else {\n console.log('midsceneWaterFlowAnimation is not defined');\n }\n })()`;\n\n await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `${pointerScript}`,\n });\n }\n\n private async hideMousePointer() {\n await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {\n window.midsceneWaterFlowAnimation.hideMousePointer();\n }\n })()`,\n });\n }\n\n /**\n * Public method to detach debugger without destroying the page instance.\n * Useful for error recovery scenarios where we want to remove the debugger banner\n * without completely destroying the page.\n */\n public async detachDebugger(tabId?: number) {\n const tabIdToDetach = tabId || (await this.getTabIdOrConnectToCurrentTab());\n console.log('detaching debugger from tab:', tabIdToDetach);\n\n try {\n await this.disableWaterFlowAnimation(tabIdToDetach);\n await sleep(200); // wait for the animation to stop\n } catch (error) {\n console.warn('Failed to disable water flow animation', error);\n }\n\n try {\n await chrome.debugger.detach({ tabId: tabIdToDetach });\n console.log('Debugger detached successfully from tab:', tabIdToDetach);\n } catch (error) {\n // Tab might be closed or debugger already detached - this is OK\n console.warn(\n 'Failed to detach debugger (may already be detached):',\n error,\n );\n }\n }\n\n private async enableWaterFlowAnimation() {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n // limit open page in new tab\n if (this.forceSameTabNavigation) {\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: limitOpenNewTabScript,\n });\n }\n\n const script = await injectWaterFlowAnimation();\n // we will call this function in sendCommandToDebugger, so we have to use the chrome.debugger.sendCommand\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: script,\n });\n }\n\n private async disableWaterFlowAnimation(tabId: number) {\n const script = await injectStopWaterFlowAnimation();\n\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: script,\n });\n }\n\n /**\n * Send a command to the debugger with automatic attach and retry on detachment.\n * Uses lazy attach pattern - will automatically attach if not already attached.\n */\n private async sendCommandToDebugger<ResponseType = any, RequestType = any>(\n command: string,\n params: RequestType,\n retryCount = 0,\n ): Promise<ResponseType> {\n const MAX_RETRIES = 2;\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n try {\n // Try to send command directly first\n const result = (await chrome.debugger.sendCommand(\n { tabId },\n command,\n params as any,\n )) as ResponseType;\n\n // Enable water flow animation after successful command (don't await)\n this.enableWaterFlowAnimation().catch((err) => {\n console.warn('Failed to enable water flow animation:', err);\n });\n\n return result;\n } catch (error) {\n // If command failed, check if it's because debugger is not attached\n const errorMsg = (error as Error)?.message || '';\n const isDetachError =\n errorMsg.includes('Debugger is not attached') ||\n errorMsg.includes('Cannot access a Target') ||\n errorMsg.includes('No target with given id');\n\n if (isDetachError && retryCount < MAX_RETRIES) {\n console.log(\n `Debugger not attached for command \"${command}\", attempting to attach (retry ${retryCount + 1}/${MAX_RETRIES})`,\n );\n\n // Try to attach and retry\n await this.ensureDebuggerAttached();\n\n return this.sendCommandToDebugger<ResponseType, RequestType>(\n command,\n params,\n retryCount + 1,\n );\n }\n\n // Not a detach error or out of retries\n throw error;\n }\n }\n\n private async getPageContentByCDP() {\n const script = await getHtmlElementScript();\n\n // check tab url\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n\n const expression = () => {\n const tree = (\n window as any\n ).midscene_element_inspector.webExtractNodeTree();\n\n return {\n tree,\n size: {\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n },\n };\n };\n const returnValue = await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: `(${expression.toString()})()`,\n returnByValue: true,\n });\n\n if (!returnValue.result.value) {\n const errorDescription =\n returnValue.exceptionDetails?.exception?.description || '';\n if (!errorDescription) {\n console.error('returnValue from cdp', returnValue);\n }\n throw new Error(\n `Failed to get page content from page, error: ${errorDescription}`,\n );\n }\n\n return returnValue.result.value as {\n tree: ElementTreeNode<ElementInfo>;\n size: Size;\n };\n }\n\n public async evaluateJavaScript(script: string) {\n return this.sendCommandToDebugger('Runtime.evaluate', {\n expression: script,\n awaitPromise: true,\n });\n }\n\n async registerFileChooserListener(\n handler: (chooser: FileChooserHandler) => Promise<void>,\n ): Promise<FileChooserRegistration> {\n const registrationVersion = ++this.fileChooserRegistrationVersion;\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n await this.sendCommandToDebugger('Page.enable', {});\n await this.sendCommandToDebugger('DOM.enable', {});\n await this.sendCommandToDebugger('Page.setInterceptFileChooserDialog', {\n enabled: true,\n });\n\n if (this.fileChooserEventHandler) {\n chrome.debugger.onEvent.removeListener(this.fileChooserEventHandler);\n this.fileChooserEventHandler = undefined;\n }\n\n let capturedError: Error | undefined;\n let pendingFileChooserHandling = Promise.resolve();\n\n const fileChooserEventHandler: Parameters<\n typeof chrome.debugger.onEvent.addListener\n >[0] = (source, method, params) => {\n if (source.tabId !== tabId || method !== 'Page.fileChooserOpened') {\n return;\n }\n\n const event = params as CDPTypes.Page.FileChooserOpenedEvent;\n if (event.backendNodeId === undefined) {\n debug(\n 'chrome extension file chooser opened without backendNodeId, skip',\n );\n return;\n }\n\n const currentFileChooserHandling = (async () => {\n try {\n await handler({\n accept: async (files: string[]) => {\n const { node } = await this.sendCommandToDebugger<\n CDPTypes.DOM.DescribeNodeResponse,\n CDPTypes.DOM.DescribeNodeRequest\n >('DOM.describeNode', {\n backendNodeId: event.backendNodeId,\n });\n\n const hasWebkitDirectory =\n hasFlatNodeAttribute(node.attributes, 'webkitdirectory') ||\n hasFlatNodeAttribute(node.attributes, 'directory');\n if (hasWebkitDirectory) {\n throw new Error(\n 'Directory upload (webkitdirectory) is not supported in Chrome extension bridge mode. Please use Playwright instead, which supports directory upload since version 1.45.',\n );\n }\n\n if (\n files.length > 1 &&\n !hasFlatNodeAttribute(node.attributes, 'multiple')\n ) {\n throw new Error(\n 'Non-multiple file input can only accept single file',\n );\n }\n\n await this.sendCommandToDebugger('DOM.setFileInputFiles', {\n files,\n backendNodeId: event.backendNodeId,\n });\n },\n });\n } catch (error) {\n capturedError =\n error instanceof Error ? error : new Error(String(error));\n }\n })();\n const previousFileChooserHandling = pendingFileChooserHandling;\n pendingFileChooserHandling = Promise.all([\n previousFileChooserHandling,\n currentFileChooserHandling,\n ]).then(() => {});\n };\n\n this.fileChooserEventHandler = fileChooserEventHandler;\n chrome.debugger.onEvent.addListener(fileChooserEventHandler);\n\n return {\n dispose: () => {\n if (this.fileChooserEventHandler !== fileChooserEventHandler) {\n return;\n }\n chrome.debugger.onEvent.removeListener(fileChooserEventHandler);\n this.fileChooserEventHandler = undefined;\n Promise.resolve()\n .then(() => {\n if (this.fileChooserRegistrationVersion !== registrationVersion) {\n return;\n }\n return this.sendCommandToDebugger(\n 'Page.setInterceptFileChooserDialog',\n {\n enabled: false,\n },\n );\n })\n .catch((error) => {\n debug('failed to disable file chooser interception: %O', error);\n });\n },\n getError: async () => {\n await pendingFileChooserHandling;\n return capturedError;\n },\n };\n }\n\n async registerFileChooserAccept(files: string[]): Promise<void> {\n this.clearFileChooserAccept();\n const { dispose, getError } = await this.registerFileChooserListener(\n async (chooser) => {\n await chooser.accept(files);\n },\n );\n this.bridgeFileChooserDispose = dispose;\n this.bridgeFileChooserGetError = getError;\n }\n\n clearFileChooserAccept(): void {\n this.bridgeFileChooserDispose?.();\n this.bridgeFileChooserDispose = undefined;\n this.bridgeFileChooserGetError = undefined;\n }\n\n async getFileChooserError(): Promise<\n { message: string; name?: string; stack?: string } | undefined\n > {\n const error = await this.bridgeFileChooserGetError?.();\n return error ? serializeError(error) : undefined;\n }\n\n async beforeInvokeAction(): Promise<void> {\n // current implementation is wait until domReadyState is complete\n try {\n await this.waitUntilNetworkIdle();\n } catch (error) {\n // console.warn('Failed to wait until network idle', error);\n }\n }\n\n private async waitUntilNetworkIdle() {\n const timeout = 10000;\n const startTime = Date.now();\n let lastReadyState = '';\n while (Date.now() - startTime < timeout) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: 'document.readyState',\n });\n lastReadyState = result.result.value;\n if (lastReadyState === 'complete') {\n await new Promise((resolve) => setTimeout(resolve, 300));\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 300));\n }\n throw new Error(\n `Failed to wait until network idle, last readyState: ${lastReadyState}`,\n );\n }\n\n // @deprecated\n async getElementsInfo() {\n const tree = await this.getElementsNodeTree();\n return treeToList(tree);\n }\n\n async getXpathsByPoint(point: Point, isOrderSensitive = false) {\n const script = await getHtmlElementScript();\n\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `window.midscene_element_inspector.getXpathsByPoint({left: ${point.left}, top: ${point.top}}, ${isOrderSensitive})`,\n returnByValue: true,\n });\n return result.result.value;\n }\n\n async getElementInfoByXpath(xpath: string) {\n const script = await getHtmlElementScript();\n\n // check tab url\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `window.midscene_element_inspector.getElementInfoByXpath(${JSON.stringify(xpath)})`,\n returnByValue: true,\n });\n return result.result.value;\n }\n\n async cacheFeatureForPoint(\n center: [number, number],\n options?: CacheFeatureOptions,\n ): Promise<ElementCacheFeature> {\n const point: Point = { left: center[0], top: center[1] };\n\n try {\n const isOrderSensitive = await judgeOrderSensitive(options, debug);\n const xpaths = await this.getXpathsByPoint(point, isOrderSensitive);\n return { xpaths: sanitizeXpaths(xpaths) };\n } catch (error) {\n debug('cacheFeatureForPoint failed: %O', error);\n return { xpaths: [] };\n }\n }\n\n async rectMatchesCacheFeature(feature: ElementCacheFeature): Promise<Rect> {\n const xpaths = sanitizeXpaths((feature as WebElementCacheFeature).xpaths);\n\n for (const xpath of xpaths) {\n try {\n const elementInfo = await this.getElementInfoByXpath(xpath);\n if (elementInfo?.rect) {\n return buildRectFromElementInfo(elementInfo);\n }\n } catch (error) {\n debug('rectMatchesCacheFeature failed for xpath %s: %O', xpath, error);\n }\n }\n\n throw new Error(\n `No matching element rect found for cache feature (tried ${xpaths.length} xpath(s))`,\n );\n }\n\n async getElementsNodeTree() {\n await this.hideMousePointer();\n const content = await this.getPageContentByCDP();\n if (content?.size) {\n this.viewportSize = content.size;\n }\n\n return content?.tree || { node: null, children: [] };\n }\n\n async size() {\n if (this.viewportSize) return this.viewportSize;\n\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: '({width: window.innerWidth, height: window.innerHeight})',\n returnByValue: true,\n });\n\n const sizeInfo: Size = result.result.value;\n console.log('sizeInfo', sizeInfo);\n\n this.viewportSize = sizeInfo;\n return sizeInfo;\n }\n\n async screenshotBase64() {\n // screenshot by cdp\n await this.hideMousePointer();\n const format = 'jpeg';\n const base64 = await this.sendCommandToDebugger('Page.captureScreenshot', {\n format,\n quality: 90,\n });\n return createImgBase64ByFormat(format, base64.data);\n }\n\n async url() {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n const url = await chrome.tabs.get(tabId).then((tab) => tab.url);\n return url || '';\n }\n\n async navigate(url: string): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.update(tabId, { url });\n // Wait for navigation to complete\n // Note: debugger will auto-reattach on next command if detached during navigation\n await this.waitUntilNetworkIdle();\n }\n\n async reload(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.reload(tabId);\n // Wait for reload to complete\n await this.waitUntilNetworkIdle();\n }\n\n async goBack(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.goBack(tabId);\n // Wait for navigation to complete\n await this.waitUntilNetworkIdle();\n }\n\n async goForward(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.goForward(tabId);\n // Wait for navigation to complete\n await this.waitUntilNetworkIdle();\n }\n\n async stopLoading(): Promise<void> {\n await this.sendCommandToDebugger('Page.stopLoading', {});\n }\n\n async navigationState(): Promise<{ isLoading: boolean }> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n const tab = await chrome.tabs.get(tabId);\n return { isLoading: tab.status === 'loading' };\n }\n\n async scrollUntilTop(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(0, -9999999);\n }\n\n async scrollUntilBottom(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(0, 9999999);\n }\n\n async scrollUntilLeft(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(-9999999, 0);\n }\n\n async scrollUntilRight(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(9999999, 0);\n }\n\n async scrollUp(distance?: number, startingPoint?: Point) {\n const { height } = await this.size();\n const scrollDistance = distance || height * 0.7;\n return this.mouse.wheel(\n 0,\n -scrollDistance,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollDown(distance?: number, startingPoint?: Point) {\n const { height } = await this.size();\n const scrollDistance = distance || height * 0.7;\n return this.mouse.wheel(\n 0,\n scrollDistance,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollLeft(distance?: number, startingPoint?: Point) {\n const { width } = await this.size();\n const scrollDistance = distance || width * 0.7;\n return this.mouse.wheel(\n -scrollDistance,\n 0,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollRight(distance?: number, startingPoint?: Point) {\n const { width } = await this.size();\n const scrollDistance = distance || width * 0.7;\n return this.mouse.wheel(\n scrollDistance,\n 0,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async clearInput(element: ElementInfo) {\n if (!element) {\n console.warn('No element to clear input');\n return;\n }\n\n await this.mouse.click(element.center[0], element.center[1]);\n\n await this.sendCommandToDebugger('Input.dispatchKeyEvent', {\n type: 'keyDown',\n commands: ['selectAll'],\n });\n\n await this.sendCommandToDebugger('Input.dispatchKeyEvent', {\n type: 'keyUp',\n commands: ['selectAll'],\n });\n\n await sleep(100);\n\n await this.keyboard.press({\n key: 'Backspace',\n });\n }\n\n private latestMouseX = 100;\n private latestMouseY = 100;\n\n mouse = {\n click: async (\n x: number,\n y: number,\n options?: { button?: MouseButton; count?: number },\n ) => {\n const { button = 'left', count = 1 } = options || {};\n await this.mouse.move(x, y);\n // detect if the page is in mobile emulation mode\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n if (this.isMobileEmulation && button === 'left') {\n // in mobile emulation mode, directly inject click event\n const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints,\n modifiers: 0,\n });\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n // standard mousePressed + mouseReleased\n for (let i = 0; i < count; i++) {\n const clickCount = i + 1;\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x,\n y,\n button,\n clickCount,\n });\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x,\n y,\n button,\n clickCount,\n });\n if (i < count - 1) {\n await sleep(50);\n }\n }\n }\n },\n wheel: async (\n deltaX: number,\n deltaY: number,\n startX?: number,\n startY?: number,\n ) => {\n const finalX = startX || this.latestMouseX;\n const finalY = startY || this.latestMouseY;\n await this.showMousePointer(finalX, finalY);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseWheel',\n x: finalX,\n y: finalY,\n deltaX,\n deltaY,\n });\n this.latestMouseX = finalX;\n this.latestMouseY = finalY;\n },\n move: async (x: number, y: number) => {\n await this.showMousePointer(x, y);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseMoved',\n x,\n y,\n });\n this.latestMouseX = x;\n this.latestMouseY = y;\n },\n drag: async (\n from: { x: number; y: number },\n to: { x: number; y: number },\n ) => {\n await this.mouse.move(from.x, from.y);\n\n await sleep(200);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x: from.x,\n y: from.y,\n button: 'left',\n clickCount: 1,\n });\n\n await sleep(300);\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseMoved',\n x: to.x,\n y: to.y,\n });\n\n await sleep(500);\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x: to.x,\n y: to.y,\n button: 'left',\n clickCount: 1,\n });\n\n await sleep(200);\n\n await this.mouse.move(to.x, to.y);\n },\n };\n\n keyboard = {\n type: async (text: string) => {\n const cdpKeyboard = new CdpKeyboard({\n send: this.sendCommandToDebugger.bind(this),\n });\n await cdpKeyboard.type(text, { delay: 0 });\n },\n press: async (\n action:\n | { key: KeyInput; command?: string }\n | { key: KeyInput; command?: string }[],\n ) => {\n const cdpKeyboard = new CdpKeyboard({\n send: this.sendCommandToDebugger.bind(this),\n });\n const keys = Array.isArray(action) ? action : [action];\n for (const k of keys) {\n const commands = k.command ? [k.command] : [];\n await cdpKeyboard.down(k.key, { commands });\n }\n for (const k of [...keys].reverse()) {\n await cdpKeyboard.up(k.key);\n }\n },\n };\n\n async destroy(): Promise<void> {\n this.destroyed = true;\n this.clearFileChooserAccept();\n const tabIdToDetach = this.activeTabId;\n this.activeTabId = null;\n if (tabIdToDetach) {\n await this.detachDebugger(tabIdToDetach);\n }\n }\n\n async longPress(x: number, y: number, duration?: number) {\n duration = duration || 500;\n // Keep a lower bound so the press is registered as a long press rather than\n // a click, but never cap the upper bound: the duration is the caller's\n // intent (e.g. \"hold for 6 seconds\").\n const MIN_LONG_PRESS_DURATION = 300;\n if (duration < MIN_LONG_PRESS_DURATION) {\n duration = MIN_LONG_PRESS_DURATION;\n }\n await this.mouse.move(x, y);\n\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n if (this.isMobileEmulation) {\n const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints,\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, duration));\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x,\n y,\n button: 'left',\n clickCount: 1,\n });\n await new Promise((res) => setTimeout(res, duration));\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x,\n y,\n button: 'left',\n clickCount: 1,\n });\n }\n this.latestMouseX = x;\n this.latestMouseY = y;\n }\n\n async swipe(\n from: { x: number; y: number },\n to: { x: number; y: number },\n duration?: number,\n ) {\n const LONG_PRESS_THRESHOLD = 500;\n const MIN_PRESS_THRESHOLD = 150;\n duration = duration || 300;\n if (duration < MIN_PRESS_THRESHOLD) {\n duration = MIN_PRESS_THRESHOLD;\n }\n if (duration > LONG_PRESS_THRESHOLD) {\n duration = LONG_PRESS_THRESHOLD;\n }\n\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n const steps = 30;\n const delay = duration / steps;\n\n if (this.isMobileEmulation) {\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints: [{ x: Math.round(from.x), y: Math.round(from.y) }],\n modifiers: 0,\n });\n\n for (let i = 1; i <= steps; i++) {\n const x = from.x + (to.x - from.x) * (i / steps);\n const y = from.y + (to.y - from.y) * (i / steps);\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchMove',\n touchPoints: [{ x: Math.round(x), y: Math.round(y) }],\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n await this.mouse.move(from.x, from.y);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x: from.x,\n y: from.y,\n button: 'left',\n clickCount: 1,\n });\n\n for (let i = 1; i <= steps; i++) {\n const x = from.x + (to.x - from.x) * (i / steps);\n const y = from.y + (to.y - from.y) * (i / steps);\n await this.mouse.move(x, y);\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x: to.x,\n y: to.y,\n button: 'left',\n clickCount: 1,\n });\n }\n\n this.latestMouseX = to.x;\n this.latestMouseY = to.y;\n }\n\n async pinch(\n centerX: number,\n centerY: number,\n startDistance: number,\n endDistance: number,\n duration = 500,\n ): Promise<void> {\n const steps = 30;\n const delay = duration / steps;\n\n const halfStart = startDistance / 2;\n const halfEnd = endDistance / 2;\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints: [\n {\n x: Math.round(centerX),\n y: Math.round(centerY - halfStart),\n id: 0,\n },\n {\n x: Math.round(centerX),\n y: Math.round(centerY + halfStart),\n id: 1,\n },\n ],\n modifiers: 0,\n });\n\n for (let i = 1; i <= steps; i++) {\n const progress = i / steps;\n const currentHalf = halfStart + (halfEnd - halfStart) * progress;\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchMove',\n touchPoints: [\n {\n x: Math.round(centerX),\n y: Math.round(centerY - currentHalf),\n id: 0,\n },\n {\n x: Math.round(centerX),\n y: Math.round(centerY + currentHalf),\n id: 1,\n },\n ],\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n }\n}\n"],"names":["debug","getDebug","sleep","ms","Promise","resolve","setTimeout","hasFlatNodeAttribute","attributes","name","i","serializeError","error","ChromeExtensionProxyPage","commonWebActionsForWebPage","tabId","Error","chrome","tabs","tab","assert","url","console","errorMsg","err","x","y","pointerScript","tabIdToDetach","limitOpenNewTabScript","script","injectWaterFlowAnimation","injectStopWaterFlowAnimation","command","params","retryCount","MAX_RETRIES","result","isDetachError","getHtmlElementScript","expression","tree","window","document","returnValue","errorDescription","handler","registrationVersion","undefined","capturedError","pendingFileChooserHandling","fileChooserEventHandler","source","method","event","currentFileChooserHandling","files","node","hasWebkitDirectory","String","previousFileChooserHandling","dispose","getError","chooser","timeout","startTime","Date","lastReadyState","treeToList","point","isOrderSensitive","xpath","JSON","center","options","judgeOrderSensitive","xpaths","sanitizeXpaths","feature","elementInfo","buildRectFromElementInfo","content","sizeInfo","format","base64","createImgBase64ByFormat","startingPoint","distance","height","scrollDistance","width","element","duration","MIN_LONG_PRESS_DURATION","touchPoints","Math","res","from","to","LONG_PRESS_THRESHOLD","MIN_PRESS_THRESHOLD","steps","delay","centerX","centerY","startDistance","endDistance","halfStart","halfEnd","progress","currentHalf","forceSameTabNavigation","button","count","clickCount","deltaX","deltaY","startX","startY","finalX","finalY","text","cdpKeyboard","CdpKeyboard","action","keys","Array","k","commands"],"mappings":"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"}
1
+ {"version":3,"file":"chrome-extension/page.mjs","sources":["../../../src/chrome-extension/page.ts"],"sourcesContent":["/// <reference types=\"chrome\" />\n\n/*\n It is used to interact with the page tab from the chrome extension.\n The page must be active when interacting with it.\n*/\n\nimport { limitOpenNewTabScript } from '@/web-element';\nimport type {\n ElementCacheFeature,\n ElementTreeNode,\n Point,\n Rect,\n Size,\n} from '@midscene/core';\nimport type {\n AbstractInterface,\n DeviceAction,\n FileChooserHandler,\n FileChooserRegistration,\n} from '@midscene/core/device';\nimport type { ElementInfo } from '@midscene/shared/extractor';\nimport { treeToList } from '@midscene/shared/extractor';\nimport { createImgBase64ByFormat } from '@midscene/shared/img';\nimport { getDebug } from '@midscene/shared/logger';\nimport { assert } from '@midscene/shared/utils';\nimport type { Protocol as CDPTypes } from 'devtools-protocol';\nimport {\n type CacheFeatureOptions,\n type WebElementCacheFeature,\n buildRectFromElementInfo,\n judgeOrderSensitive,\n sanitizeXpaths,\n} from '../common/cache-helper';\nimport {\n type KeyInput,\n type MouseButton,\n commonWebActionsForWebPage,\n} from '../web-page';\nimport { CdpKeyboard } from './cdpInput';\nimport {\n getHtmlElementScript,\n injectStopWaterFlowAnimation,\n injectWaterFlowAnimation,\n} from './dynamic-scripts';\n\nconst debug = getDebug('web:chrome-extension:page');\n\nfunction sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction hasFlatNodeAttribute(\n attributes: string[] | undefined,\n name: string,\n): boolean {\n if (!attributes) return false;\n for (let i = 0; i < attributes.length; i += 2) {\n if (attributes[i] === name) return true;\n }\n return false;\n}\n\nfunction serializeError(error: Error): {\n message: string;\n name?: string;\n stack?: string;\n} {\n return {\n message: error.message,\n name: error.name,\n stack: error.stack,\n };\n}\n\nexport default class ChromeExtensionProxyPage implements AbstractInterface {\n interfaceType = 'chrome-extension-proxy';\n\n public forceSameTabNavigation: boolean;\n\n private viewportSize?: Size;\n\n private activeTabId: number | null = null;\n\n private destroyed = false;\n\n private fileChooserEventHandler?: Parameters<\n typeof chrome.debugger.onEvent.addListener\n >[0];\n\n private fileChooserRegistrationVersion = 0;\n\n private bridgeFileChooserDispose?: () => void;\n\n private bridgeFileChooserGetError?: FileChooserRegistration['getError'];\n\n private isMobileEmulation: boolean | null = null;\n\n public _continueWhenFailedToAttachDebugger = false;\n\n constructor(forceSameTabNavigation: boolean) {\n this.forceSameTabNavigation = forceSameTabNavigation;\n }\n\n actionSpace(): DeviceAction[] {\n return commonWebActionsForWebPage(this);\n }\n\n public async setActiveTabId(tabId: number) {\n if (this.activeTabId) {\n throw new Error(\n `Active tab id is already set, which is ${this.activeTabId}, cannot set it to ${tabId}`,\n );\n }\n await chrome.tabs.update(tabId, { active: true });\n this.activeTabId = tabId;\n }\n\n public async getActiveTabId() {\n return this.activeTabId;\n }\n\n /**\n * Get a list of current tabs\n * @returns {Promise<Array<{id: number, title: string, url: string}>>}\n */\n public async getBrowserTabList(): Promise<\n { id: string; title: string; url: string; currentActiveTab: boolean }[]\n > {\n const tabs = await chrome.tabs.query({ currentWindow: true });\n return tabs\n .map((tab) => ({\n id: `${tab.id}`,\n title: tab.title,\n url: tab.url,\n currentActiveTab: tab.active,\n }))\n .filter((tab) => tab.id && tab.title && tab.url) as {\n id: string;\n title: string;\n url: string;\n currentActiveTab: boolean;\n }[];\n }\n\n public async getTabIdOrConnectToCurrentTab() {\n if (this.activeTabId) {\n // alway keep on the connected tab\n return this.activeTabId;\n }\n const tabId = await chrome.tabs\n .query({ active: true, currentWindow: true })\n .then((tabs) => tabs[0]?.id);\n this.activeTabId = tabId || 0;\n return this.activeTabId;\n }\n\n /**\n * Ensure debugger is attached to the current tab.\n * Uses lazy attach pattern - only attaches when needed.\n */\n private async ensureDebuggerAttached() {\n assert(!this.destroyed, 'Page is destroyed');\n\n const url = await this.url();\n if (url.startsWith('chrome://')) {\n throw new Error(\n 'Cannot attach debugger to chrome:// pages, please use Midscene in a normal page with http://, https:// or file://',\n );\n }\n\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n try {\n await chrome.debugger.attach({ tabId }, '1.3');\n console.log('Debugger attached to tab:', tabId);\n } catch (error) {\n const errorMsg = (error as Error)?.message || '';\n // Already attached is OK, we can continue\n if (errorMsg.includes('Another debugger is already attached')) {\n console.log('Debugger already attached to tab:', tabId);\n return;\n }\n\n if (this._continueWhenFailedToAttachDebugger) {\n console.warn(\n 'Failed to attach debugger, but continuing due to _continueWhenFailedToAttachDebugger flag',\n error,\n );\n return;\n }\n\n throw error;\n }\n\n // Wait for debugger banner in Chrome to appear\n await sleep(500);\n\n // Enable water flow animation. Non-fatal: this is a purely visual\n // overlay, and Chrome can briefly detach the debugger between\n // attach() and the eval below (cross-origin navigation / Site\n // Isolation race). If we awaited it and it threw \"Debugger is not\n // attached\", the error would propagate out of the catch block in\n // sendCommandToDebugger, preventing the actual CDP command from\n // ever being retried. Fire-and-forget here so the lazy-attach\n // retry can succeed even when the animation fails.\n this.enableWaterFlowAnimation().catch((err) => {\n console.warn('Failed to enable water flow animation:', err);\n });\n }\n\n private async showMousePointer(x: number, y: number) {\n // update mouse pointer while redirecting\n const pointerScript = `(() => {\n if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {\n window.midsceneWaterFlowAnimation.enable();\n window.midsceneWaterFlowAnimation.showMousePointer(${x}, ${y});\n } else {\n console.log('midsceneWaterFlowAnimation is not defined');\n }\n })()`;\n\n await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `${pointerScript}`,\n });\n }\n\n private async hideMousePointer() {\n await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {\n window.midsceneWaterFlowAnimation.hideMousePointer();\n }\n })()`,\n });\n }\n\n /**\n * Public method to detach debugger without destroying the page instance.\n * Useful for error recovery scenarios where we want to remove the debugger banner\n * without completely destroying the page.\n */\n public async detachDebugger(tabId?: number) {\n const tabIdToDetach = tabId || (await this.getTabIdOrConnectToCurrentTab());\n console.log('detaching debugger from tab:', tabIdToDetach);\n\n try {\n await this.disableWaterFlowAnimation(tabIdToDetach);\n await sleep(200); // wait for the animation to stop\n } catch (error) {\n console.warn('Failed to disable water flow animation', error);\n }\n\n try {\n await chrome.debugger.detach({ tabId: tabIdToDetach });\n console.log('Debugger detached successfully from tab:', tabIdToDetach);\n } catch (error) {\n // Tab might be closed or debugger already detached - this is OK\n console.warn(\n 'Failed to detach debugger (may already be detached):',\n error,\n );\n }\n }\n\n private async enableWaterFlowAnimation() {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n // limit open page in new tab\n if (this.forceSameTabNavigation) {\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: limitOpenNewTabScript,\n });\n }\n\n const script = await injectWaterFlowAnimation();\n // we will call this function in sendCommandToDebugger, so we have to use the chrome.debugger.sendCommand\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: script,\n });\n }\n\n private async disableWaterFlowAnimation(tabId: number) {\n const script = await injectStopWaterFlowAnimation();\n\n await chrome.debugger.sendCommand({ tabId }, 'Runtime.evaluate', {\n expression: script,\n });\n }\n\n /**\n * Send a command to the debugger with automatic attach and retry on detachment.\n * Uses lazy attach pattern - will automatically attach if not already attached.\n */\n private async sendCommandToDebugger<ResponseType = any, RequestType = any>(\n command: string,\n params: RequestType,\n retryCount = 0,\n ): Promise<ResponseType> {\n const MAX_RETRIES = 2;\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n try {\n // Try to send command directly first\n const result = (await chrome.debugger.sendCommand(\n { tabId },\n command,\n params as any,\n )) as ResponseType;\n\n // Enable water flow animation after successful command (don't await)\n this.enableWaterFlowAnimation().catch((err) => {\n console.warn('Failed to enable water flow animation:', err);\n });\n\n return result;\n } catch (error) {\n // If command failed, check if it's because debugger is not attached\n const errorMsg = (error as Error)?.message || '';\n const isDetachError =\n errorMsg.includes('Debugger is not attached') ||\n errorMsg.includes('Cannot access a Target') ||\n errorMsg.includes('No target with given id');\n\n if (isDetachError && retryCount < MAX_RETRIES) {\n console.log(\n `Debugger not attached for command \"${command}\", attempting to attach (retry ${retryCount + 1}/${MAX_RETRIES})`,\n );\n\n // Try to attach and retry\n await this.ensureDebuggerAttached();\n\n return this.sendCommandToDebugger<ResponseType, RequestType>(\n command,\n params,\n retryCount + 1,\n );\n }\n\n // Not a detach error or out of retries\n throw error;\n }\n }\n\n private async getPageContentByCDP() {\n const script = await getHtmlElementScript();\n\n // check tab url\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n\n const expression = () => {\n const tree = (\n window as any\n ).midscene_element_inspector.webExtractNodeTree();\n\n return {\n tree,\n size: {\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n },\n };\n };\n const returnValue = await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: `(${expression.toString()})()`,\n returnByValue: true,\n });\n\n if (!returnValue.result.value) {\n const errorDescription =\n returnValue.exceptionDetails?.exception?.description || '';\n if (!errorDescription) {\n console.error('returnValue from cdp', returnValue);\n }\n throw new Error(\n `Failed to get page content from page, error: ${errorDescription}`,\n );\n }\n\n return returnValue.result.value as {\n tree: ElementTreeNode<ElementInfo>;\n size: Size;\n };\n }\n\n public async evaluateJavaScript(script: string) {\n return this.sendCommandToDebugger('Runtime.evaluate', {\n expression: script,\n awaitPromise: true,\n });\n }\n\n async registerFileChooserListener(\n handler: (chooser: FileChooserHandler) => Promise<void>,\n ): Promise<FileChooserRegistration> {\n const registrationVersion = ++this.fileChooserRegistrationVersion;\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n\n await this.ensureDebuggerAttached();\n await this.sendCommandToDebugger('Page.enable', {});\n await this.sendCommandToDebugger('DOM.enable', {});\n await this.sendCommandToDebugger('Page.setInterceptFileChooserDialog', {\n enabled: true,\n });\n\n if (this.fileChooserEventHandler) {\n chrome.debugger.onEvent.removeListener(this.fileChooserEventHandler);\n this.fileChooserEventHandler = undefined;\n }\n\n let capturedError: Error | undefined;\n let pendingFileChooserHandling = Promise.resolve();\n\n const fileChooserEventHandler: Parameters<\n typeof chrome.debugger.onEvent.addListener\n >[0] = (source, method, params) => {\n if (source.tabId !== tabId || method !== 'Page.fileChooserOpened') {\n return;\n }\n\n const event = params as CDPTypes.Page.FileChooserOpenedEvent;\n if (event.backendNodeId === undefined) {\n debug(\n 'chrome extension file chooser opened without backendNodeId, skip',\n );\n return;\n }\n\n const currentFileChooserHandling = (async () => {\n try {\n await handler({\n accept: async (files: string[]) => {\n const { node } = await this.sendCommandToDebugger<\n CDPTypes.DOM.DescribeNodeResponse,\n CDPTypes.DOM.DescribeNodeRequest\n >('DOM.describeNode', {\n backendNodeId: event.backendNodeId,\n });\n\n const hasWebkitDirectory =\n hasFlatNodeAttribute(node.attributes, 'webkitdirectory') ||\n hasFlatNodeAttribute(node.attributes, 'directory');\n if (hasWebkitDirectory) {\n throw new Error(\n 'Directory upload (webkitdirectory) is not supported in Chrome extension bridge mode. Please use Playwright instead, which supports directory upload since version 1.45.',\n );\n }\n\n if (\n files.length > 1 &&\n !hasFlatNodeAttribute(node.attributes, 'multiple')\n ) {\n throw new Error(\n 'Non-multiple file input can only accept single file',\n );\n }\n\n await this.sendCommandToDebugger('DOM.setFileInputFiles', {\n files,\n backendNodeId: event.backendNodeId,\n });\n },\n });\n } catch (error) {\n capturedError =\n error instanceof Error ? error : new Error(String(error));\n }\n })();\n const previousFileChooserHandling = pendingFileChooserHandling;\n pendingFileChooserHandling = Promise.all([\n previousFileChooserHandling,\n currentFileChooserHandling,\n ]).then(() => {});\n };\n\n this.fileChooserEventHandler = fileChooserEventHandler;\n chrome.debugger.onEvent.addListener(fileChooserEventHandler);\n\n return {\n dispose: () => {\n if (this.fileChooserEventHandler !== fileChooserEventHandler) {\n return;\n }\n chrome.debugger.onEvent.removeListener(fileChooserEventHandler);\n this.fileChooserEventHandler = undefined;\n Promise.resolve()\n .then(() => {\n if (this.fileChooserRegistrationVersion !== registrationVersion) {\n return;\n }\n return this.sendCommandToDebugger(\n 'Page.setInterceptFileChooserDialog',\n {\n enabled: false,\n },\n );\n })\n .catch((error) => {\n debug('failed to disable file chooser interception: %O', error);\n });\n },\n getError: async () => {\n await pendingFileChooserHandling;\n return capturedError;\n },\n };\n }\n\n async registerFileChooserAccept(files: string[]): Promise<void> {\n this.clearFileChooserAccept();\n const { dispose, getError } = await this.registerFileChooserListener(\n async (chooser) => {\n await chooser.accept(files);\n },\n );\n this.bridgeFileChooserDispose = dispose;\n this.bridgeFileChooserGetError = getError;\n }\n\n clearFileChooserAccept(): void {\n this.bridgeFileChooserDispose?.();\n this.bridgeFileChooserDispose = undefined;\n this.bridgeFileChooserGetError = undefined;\n }\n\n async getFileChooserError(): Promise<\n { message: string; name?: string; stack?: string } | undefined\n > {\n const error = await this.bridgeFileChooserGetError?.();\n return error ? serializeError(error) : undefined;\n }\n\n async beforeInvokeAction(): Promise<void> {\n // current implementation is wait until domReadyState is complete\n try {\n await this.waitUntilNetworkIdle();\n } catch (error) {\n // console.warn('Failed to wait until network idle', error);\n }\n }\n\n private async waitUntilNetworkIdle() {\n const timeout = 10000;\n const startTime = Date.now();\n let lastReadyState = '';\n while (Date.now() - startTime < timeout) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: 'document.readyState',\n });\n lastReadyState = result.result.value;\n if (lastReadyState === 'complete') {\n await new Promise((resolve) => setTimeout(resolve, 300));\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 300));\n }\n throw new Error(\n `Failed to wait until network idle, last readyState: ${lastReadyState}`,\n );\n }\n\n // @deprecated\n async getElementsInfo() {\n const tree = await this.getElementsNodeTree();\n return treeToList(tree);\n }\n\n async getXpathsByPoint(point: Point, isOrderSensitive = false) {\n const script = await getHtmlElementScript();\n\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `window.midscene_element_inspector.getXpathsByPoint({left: ${point.left}, top: ${point.top}}, ${isOrderSensitive})`,\n returnByValue: true,\n });\n return result.result.value;\n }\n\n async getElementInfoByXpath(xpath: string) {\n const script = await getHtmlElementScript();\n\n // check tab url\n await this.sendCommandToDebugger<\n CDPTypes.Runtime.EvaluateResponse,\n CDPTypes.Runtime.EvaluateRequest\n >('Runtime.evaluate', {\n expression: script,\n });\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `window.midscene_element_inspector.getElementInfoByXpath(${JSON.stringify(xpath)})`,\n returnByValue: true,\n });\n return result.result.value;\n }\n\n async cacheFeatureForPoint(\n center: [number, number],\n options?: CacheFeatureOptions,\n ): Promise<ElementCacheFeature> {\n const point: Point = { left: center[0], top: center[1] };\n\n try {\n const isOrderSensitive = await judgeOrderSensitive(options, debug);\n const xpaths = await this.getXpathsByPoint(point, isOrderSensitive);\n return { xpaths: sanitizeXpaths(xpaths) };\n } catch (error) {\n debug('cacheFeatureForPoint failed: %O', error);\n return { xpaths: [] };\n }\n }\n\n async rectMatchesCacheFeature(feature: ElementCacheFeature): Promise<Rect> {\n const xpaths = sanitizeXpaths((feature as WebElementCacheFeature).xpaths);\n\n for (const xpath of xpaths) {\n try {\n const elementInfo = await this.getElementInfoByXpath(xpath);\n if (elementInfo?.rect) {\n return buildRectFromElementInfo(elementInfo);\n }\n } catch (error) {\n debug('rectMatchesCacheFeature failed for xpath %s: %O', xpath, error);\n }\n }\n\n throw new Error(\n `No matching element rect found for cache feature (tried ${xpaths.length} xpath(s))`,\n );\n }\n\n async getElementsNodeTree() {\n await this.hideMousePointer();\n const content = await this.getPageContentByCDP();\n if (content?.size) {\n this.viewportSize = content.size;\n }\n\n return content?.tree || { node: null, children: [] };\n }\n\n async size() {\n if (this.viewportSize) return this.viewportSize;\n\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: '({width: window.innerWidth, height: window.innerHeight})',\n returnByValue: true,\n });\n\n const sizeInfo: Size = result.result.value;\n console.log('sizeInfo', sizeInfo);\n\n this.viewportSize = sizeInfo;\n return sizeInfo;\n }\n\n async screenshotBase64() {\n // screenshot by cdp\n await this.hideMousePointer();\n const format = 'jpeg';\n const base64 = await this.sendCommandToDebugger('Page.captureScreenshot', {\n format,\n quality: 90,\n });\n return createImgBase64ByFormat(format, base64.data);\n }\n\n async url() {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n const url = await chrome.tabs.get(tabId).then((tab) => tab.url);\n return url || '';\n }\n\n async navigate(url: string): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.update(tabId, { url });\n // Wait for navigation to complete\n // Note: debugger will auto-reattach on next command if detached during navigation\n await this.waitUntilNetworkIdle();\n }\n\n async reload(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.reload(tabId);\n // Wait for reload to complete\n await this.waitUntilNetworkIdle();\n }\n\n async goBack(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.goBack(tabId);\n // Wait for navigation to complete\n await this.waitUntilNetworkIdle();\n }\n\n async goForward(): Promise<void> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n await chrome.tabs.goForward(tabId);\n // Wait for navigation to complete\n await this.waitUntilNetworkIdle();\n }\n\n async stopLoading(): Promise<void> {\n await this.sendCommandToDebugger('Page.stopLoading', {});\n }\n\n async navigationState(): Promise<{ isLoading: boolean }> {\n const tabId = await this.getTabIdOrConnectToCurrentTab();\n const tab = await chrome.tabs.get(tabId);\n return { isLoading: tab.status === 'loading' };\n }\n\n async scrollUntilTop(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(0, -9999999);\n }\n\n async scrollUntilBottom(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(0, 9999999);\n }\n\n async scrollUntilLeft(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(-9999999, 0);\n }\n\n async scrollUntilRight(startingPoint?: Point) {\n if (startingPoint) {\n await this.mouse.move(startingPoint.left, startingPoint.top);\n }\n return this.mouse.wheel(9999999, 0);\n }\n\n async scrollUp(distance?: number, startingPoint?: Point) {\n const { height } = await this.size();\n const scrollDistance = distance || height * 0.7;\n return this.mouse.wheel(\n 0,\n -scrollDistance,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollDown(distance?: number, startingPoint?: Point) {\n const { height } = await this.size();\n const scrollDistance = distance || height * 0.7;\n return this.mouse.wheel(\n 0,\n scrollDistance,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollLeft(distance?: number, startingPoint?: Point) {\n const { width } = await this.size();\n const scrollDistance = distance || width * 0.7;\n return this.mouse.wheel(\n -scrollDistance,\n 0,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async scrollRight(distance?: number, startingPoint?: Point) {\n const { width } = await this.size();\n const scrollDistance = distance || width * 0.7;\n return this.mouse.wheel(\n scrollDistance,\n 0,\n startingPoint?.left,\n startingPoint?.top,\n );\n }\n\n async clearInput(element: ElementInfo) {\n if (!element) {\n console.warn('No element to clear input');\n return;\n }\n\n await this.mouse.click(element.center[0], element.center[1]);\n\n await this.sendCommandToDebugger('Input.dispatchKeyEvent', {\n type: 'keyDown',\n commands: ['selectAll'],\n });\n\n await this.sendCommandToDebugger('Input.dispatchKeyEvent', {\n type: 'keyUp',\n commands: ['selectAll'],\n });\n\n await sleep(100);\n\n await this.keyboard.press({\n key: 'Backspace',\n });\n }\n\n private latestMouseX = 100;\n private latestMouseY = 100;\n\n mouse = {\n click: async (\n x: number,\n y: number,\n options?: { button?: MouseButton; count?: number },\n ) => {\n const { button = 'left', count = 1 } = options || {};\n await this.mouse.move(x, y);\n // detect if the page is in mobile emulation mode\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n if (this.isMobileEmulation && button === 'left') {\n // in mobile emulation mode, directly inject click event\n const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints,\n modifiers: 0,\n });\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n // standard mousePressed + mouseReleased\n for (let i = 0; i < count; i++) {\n const clickCount = i + 1;\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x,\n y,\n button,\n clickCount,\n });\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x,\n y,\n button,\n clickCount,\n });\n if (i < count - 1) {\n await sleep(50);\n }\n }\n }\n },\n wheel: async (\n deltaX: number,\n deltaY: number,\n startX?: number,\n startY?: number,\n ) => {\n const finalX = startX || this.latestMouseX;\n const finalY = startY || this.latestMouseY;\n await this.showMousePointer(finalX, finalY);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseWheel',\n x: finalX,\n y: finalY,\n deltaX,\n deltaY,\n });\n this.latestMouseX = finalX;\n this.latestMouseY = finalY;\n },\n move: async (x: number, y: number) => {\n await this.showMousePointer(x, y);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseMoved',\n x,\n y,\n });\n this.latestMouseX = x;\n this.latestMouseY = y;\n },\n drag: async (\n from: { x: number; y: number },\n to: { x: number; y: number },\n ) => {\n await this.mouse.move(from.x, from.y);\n\n await sleep(200);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x: from.x,\n y: from.y,\n button: 'left',\n clickCount: 1,\n });\n\n await sleep(300);\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseMoved',\n x: to.x,\n y: to.y,\n });\n\n await sleep(500);\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x: to.x,\n y: to.y,\n button: 'left',\n clickCount: 1,\n });\n\n await sleep(200);\n\n await this.mouse.move(to.x, to.y);\n },\n };\n\n keyboard = {\n type: async (text: string) => {\n const cdpKeyboard = new CdpKeyboard({\n send: this.sendCommandToDebugger.bind(this),\n });\n await cdpKeyboard.type(text, { delay: 0 });\n },\n press: async (\n action:\n | { key: KeyInput; command?: string }\n | { key: KeyInput; command?: string }[],\n ) => {\n const cdpKeyboard = new CdpKeyboard({\n send: this.sendCommandToDebugger.bind(this),\n });\n const keys = Array.isArray(action) ? action : [action];\n for (const k of keys) {\n const commands = k.command ? [k.command] : [];\n await cdpKeyboard.down(k.key, { commands });\n }\n for (const k of [...keys].reverse()) {\n await cdpKeyboard.up(k.key);\n }\n },\n };\n\n async destroy(): Promise<void> {\n this.destroyed = true;\n this.clearFileChooserAccept();\n const tabIdToDetach = this.activeTabId;\n this.activeTabId = null;\n if (tabIdToDetach) {\n await this.detachDebugger(tabIdToDetach);\n }\n }\n\n async longPress(x: number, y: number, duration?: number) {\n duration = duration || 500;\n // Keep a lower bound so the press is registered as a long press rather than\n // a click, but never cap the upper bound: the duration is the caller's\n // intent (e.g. \"hold for 6 seconds\").\n const MIN_LONG_PRESS_DURATION = 300;\n if (duration < MIN_LONG_PRESS_DURATION) {\n duration = MIN_LONG_PRESS_DURATION;\n }\n await this.mouse.move(x, y);\n\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n if (this.isMobileEmulation) {\n const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints,\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, duration));\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x,\n y,\n button: 'left',\n clickCount: 1,\n });\n await new Promise((res) => setTimeout(res, duration));\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x,\n y,\n button: 'left',\n clickCount: 1,\n });\n }\n this.latestMouseX = x;\n this.latestMouseY = y;\n }\n\n async swipe(\n from: { x: number; y: number },\n to: { x: number; y: number },\n duration?: number,\n ) {\n const LONG_PRESS_THRESHOLD = 500;\n const MIN_PRESS_THRESHOLD = 150;\n duration = duration || 300;\n if (duration < MIN_PRESS_THRESHOLD) {\n duration = MIN_PRESS_THRESHOLD;\n }\n if (duration > LONG_PRESS_THRESHOLD) {\n duration = LONG_PRESS_THRESHOLD;\n }\n\n if (this.isMobileEmulation === null) {\n const result = await this.sendCommandToDebugger('Runtime.evaluate', {\n expression: `(() => {\n return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);\n })()`,\n returnByValue: true,\n });\n this.isMobileEmulation = result?.result?.value;\n }\n\n const steps = 30;\n const delay = duration / steps;\n\n if (this.isMobileEmulation) {\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints: [{ x: Math.round(from.x), y: Math.round(from.y) }],\n modifiers: 0,\n });\n\n for (let i = 1; i <= steps; i++) {\n const x = from.x + (to.x - from.x) * (i / steps);\n const y = from.y + (to.y - from.y) * (i / steps);\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchMove',\n touchPoints: [{ x: Math.round(x), y: Math.round(y) }],\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n } else {\n await this.mouse.move(from.x, from.y);\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mousePressed',\n x: from.x,\n y: from.y,\n button: 'left',\n clickCount: 1,\n });\n\n for (let i = 1; i <= steps; i++) {\n const x = from.x + (to.x - from.x) * (i / steps);\n const y = from.y + (to.y - from.y) * (i / steps);\n await this.mouse.move(x, y);\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchMouseEvent', {\n type: 'mouseReleased',\n x: to.x,\n y: to.y,\n button: 'left',\n clickCount: 1,\n });\n }\n\n this.latestMouseX = to.x;\n this.latestMouseY = to.y;\n }\n\n async pinch(\n centerX: number,\n centerY: number,\n startDistance: number,\n endDistance: number,\n duration = 500,\n ): Promise<void> {\n const steps = 30;\n const delay = duration / steps;\n\n const halfStart = startDistance / 2;\n const halfEnd = endDistance / 2;\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchStart',\n touchPoints: [\n {\n x: Math.round(centerX),\n y: Math.round(centerY - halfStart),\n id: 0,\n },\n {\n x: Math.round(centerX),\n y: Math.round(centerY + halfStart),\n id: 1,\n },\n ],\n modifiers: 0,\n });\n\n for (let i = 1; i <= steps; i++) {\n const progress = i / steps;\n const currentHalf = halfStart + (halfEnd - halfStart) * progress;\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchMove',\n touchPoints: [\n {\n x: Math.round(centerX),\n y: Math.round(centerY - currentHalf),\n id: 0,\n },\n {\n x: Math.round(centerX),\n y: Math.round(centerY + currentHalf),\n id: 1,\n },\n ],\n modifiers: 0,\n });\n await new Promise((res) => setTimeout(res, delay));\n }\n\n await this.sendCommandToDebugger('Input.dispatchTouchEvent', {\n type: 'touchEnd',\n touchPoints: [],\n modifiers: 0,\n });\n }\n}\n"],"names":["debug","getDebug","sleep","ms","Promise","resolve","setTimeout","hasFlatNodeAttribute","attributes","name","i","serializeError","error","ChromeExtensionProxyPage","commonWebActionsForWebPage","tabId","Error","chrome","tabs","tab","assert","url","console","errorMsg","err","x","y","pointerScript","tabIdToDetach","limitOpenNewTabScript","script","injectWaterFlowAnimation","injectStopWaterFlowAnimation","command","params","retryCount","MAX_RETRIES","result","isDetachError","getHtmlElementScript","expression","tree","window","document","returnValue","errorDescription","handler","registrationVersion","undefined","capturedError","pendingFileChooserHandling","fileChooserEventHandler","source","method","event","currentFileChooserHandling","files","node","hasWebkitDirectory","String","previousFileChooserHandling","dispose","getError","chooser","timeout","startTime","Date","lastReadyState","treeToList","point","isOrderSensitive","xpath","JSON","center","options","judgeOrderSensitive","xpaths","sanitizeXpaths","feature","elementInfo","buildRectFromElementInfo","content","sizeInfo","format","base64","createImgBase64ByFormat","startingPoint","distance","height","scrollDistance","width","element","duration","MIN_LONG_PRESS_DURATION","touchPoints","Math","res","from","to","LONG_PRESS_THRESHOLD","MIN_PRESS_THRESHOLD","steps","delay","centerX","centerY","startDistance","endDistance","halfStart","halfEnd","progress","currentHalf","forceSameTabNavigation","button","count","clickCount","deltaX","deltaY","startX","startY","finalX","finalY","text","cdpKeyboard","CdpKeyboard","action","keys","Array","k","commands"],"mappings":"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"}
package/dist/es/cli.mjs CHANGED
@@ -3,10 +3,10 @@ import { join } from "node:path";
3
3
  import { createReportCliCommands } from "@midscene/core";
4
4
  import { reportCLIError, runToolsCLI } from "@midscene/shared/cli";
5
5
  import dotenv from "dotenv";
6
+ import { WebMidsceneTools } from "./agent-tools.mjs";
7
+ import { WebCdpMidsceneTools } from "./agent-tools-cdp.mjs";
8
+ import { WebPuppeteerMidsceneTools } from "./agent-tools-puppeteer.mjs";
6
9
  import { parseWebCliOptions } from "./cli-options.mjs";
7
- import { WebMidsceneTools } from "./mcp-tools.mjs";
8
- import { WebCdpMidsceneTools } from "./mcp-tools-cdp.mjs";
9
- import { WebPuppeteerMidsceneTools } from "./mcp-tools-puppeteer.mjs";
10
10
  const envFile = join(process.cwd(), '.env');
11
11
  if (existsSync(envFile)) dotenv.config({
12
12
  path: envFile
@@ -18,7 +18,7 @@ Promise.resolve().then(()=>{
18
18
  return runToolsCLI(tools, 'midscene-web', {
19
19
  stripPrefix: 'web_',
20
20
  argv: parsedOptions.argv,
21
- version: "1.9.8-beta-20260618091332.0",
21
+ version: "1.10.0",
22
22
  extraCommands: createReportCliCommands()
23
23
  });
24
24
  }).catch((e)=>{
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","sources":["../../src/cli.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createReportCliCommands } from '@midscene/core';\nimport { reportCLIError, runToolsCLI } from '@midscene/shared/cli';\nimport dotenv from 'dotenv';\nimport { parseWebCliOptions } from './cli-options';\nimport { WebMidsceneTools } from './mcp-tools';\nimport { WebCdpMidsceneTools } from './mcp-tools-cdp';\nimport { WebPuppeteerMidsceneTools } from './mcp-tools-puppeteer';\n\n// Load .env early so MIDSCENE_CDP_ENDPOINT is available during arg parsing\nconst envFile = join(process.cwd(), '.env');\nif (existsSync(envFile)) {\n dotenv.config({ path: envFile });\n}\n\ndeclare const __VERSION__: string;\n\nPromise.resolve()\n .then(() => {\n const parsedOptions = parseWebCliOptions(process.argv.slice(2));\n\n let tools:\n | WebMidsceneTools\n | WebPuppeteerMidsceneTools\n | WebCdpMidsceneTools;\n if (parsedOptions.mode === 'bridge') {\n tools = new WebMidsceneTools();\n } else if (parsedOptions.mode === 'cdp') {\n tools = new WebCdpMidsceneTools(parsedOptions.cdpEndpoint!);\n } else {\n tools = new WebPuppeteerMidsceneTools(parsedOptions.viewport);\n }\n\n return runToolsCLI(tools, 'midscene-web', {\n stripPrefix: 'web_',\n argv: parsedOptions.argv,\n version: __VERSION__,\n extraCommands: createReportCliCommands(),\n });\n })\n .catch((e) => {\n process.exit(reportCLIError(e));\n });\n"],"names":["envFile","join","process","existsSync","dotenv","Promise","parsedOptions","parseWebCliOptions","tools","WebMidsceneTools","WebCdpMidsceneTools","WebPuppeteerMidsceneTools","runToolsCLI","__VERSION__","createReportCliCommands","e","reportCLIError"],"mappings":";;;;;;;;;AAWA,MAAMA,UAAUC,KAAKC,QAAQ,GAAG,IAAI;AACpC,IAAIC,WAAWH,UACbI,OAAO,MAAM,CAAC;IAAE,MAAMJ;AAAQ;AAKhCK,QAAQ,OAAO,GACZ,IAAI,CAAC;IACJ,MAAMC,gBAAgBC,mBAAmBL,QAAQ,IAAI,CAAC,KAAK,CAAC;IAE5D,IAAIM;IAKFA,QADEF,AAAuB,aAAvBA,cAAc,IAAI,GACZ,IAAIG,qBACHH,AAAuB,UAAvBA,cAAc,IAAI,GACnB,IAAII,oBAAoBJ,cAAc,WAAW,IAEjD,IAAIK,0BAA0BL,cAAc,QAAQ;IAG9D,OAAOM,YAAYJ,OAAO,gBAAgB;QACxC,aAAa;QACb,MAAMF,cAAc,IAAI;QACxB,SAASO;QACT,eAAeC;IACjB;AACF,GACC,KAAK,CAAC,CAACC;IACNb,QAAQ,IAAI,CAACc,eAAeD;AAC9B"}
1
+ {"version":3,"file":"cli.mjs","sources":["../../src/cli.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createReportCliCommands } from '@midscene/core';\nimport { reportCLIError, runToolsCLI } from '@midscene/shared/cli';\nimport dotenv from 'dotenv';\nimport { WebMidsceneTools } from './agent-tools';\nimport { WebCdpMidsceneTools } from './agent-tools-cdp';\nimport { WebPuppeteerMidsceneTools } from './agent-tools-puppeteer';\nimport { parseWebCliOptions } from './cli-options';\n\n// Load .env early so MIDSCENE_CDP_ENDPOINT is available during arg parsing\nconst envFile = join(process.cwd(), '.env');\nif (existsSync(envFile)) {\n dotenv.config({ path: envFile });\n}\n\ndeclare const __VERSION__: string;\n\nPromise.resolve()\n .then(() => {\n const parsedOptions = parseWebCliOptions(process.argv.slice(2));\n\n let tools:\n | WebMidsceneTools\n | WebPuppeteerMidsceneTools\n | WebCdpMidsceneTools;\n if (parsedOptions.mode === 'bridge') {\n tools = new WebMidsceneTools();\n } else if (parsedOptions.mode === 'cdp') {\n tools = new WebCdpMidsceneTools(parsedOptions.cdpEndpoint!);\n } else {\n tools = new WebPuppeteerMidsceneTools(parsedOptions.viewport);\n }\n\n return runToolsCLI(tools, 'midscene-web', {\n stripPrefix: 'web_',\n argv: parsedOptions.argv,\n version: __VERSION__,\n extraCommands: createReportCliCommands(),\n });\n })\n .catch((e) => {\n process.exit(reportCLIError(e));\n });\n"],"names":["envFile","join","process","existsSync","dotenv","Promise","parsedOptions","parseWebCliOptions","tools","WebMidsceneTools","WebCdpMidsceneTools","WebPuppeteerMidsceneTools","runToolsCLI","__VERSION__","createReportCliCommands","e","reportCLIError"],"mappings":";;;;;;;;;AAWA,MAAMA,UAAUC,KAAKC,QAAQ,GAAG,IAAI;AACpC,IAAIC,WAAWH,UACbI,OAAO,MAAM,CAAC;IAAE,MAAMJ;AAAQ;AAKhCK,QAAQ,OAAO,GACZ,IAAI,CAAC;IACJ,MAAMC,gBAAgBC,mBAAmBL,QAAQ,IAAI,CAAC,KAAK,CAAC;IAE5D,IAAIM;IAKFA,QADEF,AAAuB,aAAvBA,cAAc,IAAI,GACZ,IAAIG,qBACHH,AAAuB,UAAvBA,cAAc,IAAI,GACnB,IAAII,oBAAoBJ,cAAc,WAAW,IAEjD,IAAIK,0BAA0BL,cAAc,QAAQ;IAG9D,OAAOM,YAAYJ,OAAO,gBAAgB;QACxC,aAAa;QACb,MAAMF,cAAc,IAAI;QACxB,SAASO;QACT,eAAeC;IACjB;AACF,GACC,KAAK,CAAC,CAACC;IACNb,QAAQ,IAAI,CAACc,eAAeD;AAC9B"}
package/dist/es/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { PlaywrightAgent, PlaywrightAiFixture } from "./playwright/index.mjs";
2
2
  import { Agent } from "@midscene/core/agent";
3
3
  import { PuppeteerAgent } from "./puppeteer/index.mjs";
4
4
  import { StaticPage, StaticPageAgent } from "./static/index.mjs";
5
- import { WebMidsceneTools } from "./mcp-tools.mjs";
5
+ import { WebMidsceneTools } from "./agent-tools.mjs";
6
6
  import { webPlaygroundPlatform } from "./platform.mjs";
7
- import { WebCdpMidsceneTools } from "./mcp-tools-cdp.mjs";
7
+ import { WebCdpMidsceneTools } from "./agent-tools-cdp.mjs";
8
8
  export { Agent as PageAgent, PlaywrightAgent, PlaywrightAiFixture, PuppeteerAgent, StaticPage, StaticPageAgent, WebCdpMidsceneTools, WebMidsceneTools, webPlaygroundPlatform };
@@ -24,11 +24,13 @@ class PlaywrightAgent extends Agent {
24
24
  if (!page) throw new Error('[midscene] PlaywrightAgent requires a valid Playwright page instance. Please make sure to pass a valid page object.');
25
25
  const webPage = new WebPage(page, opts);
26
26
  super(webPage, opts);
27
- const { forceSameTabNavigation = true, forceChromeSelectRendering } = opts ?? {};
27
+ const { forceSameTabNavigation = true, forceChromeSelectRendering = true } = opts ?? {};
28
28
  if (forceSameTabNavigation) forceClosePopup(page, debug);
29
29
  if (forceChromeSelectRendering) {
30
- const playwrightVersion = getPlaywrightVersion();
31
- if (playwrightVersion && !semver.gte(playwrightVersion, '1.52.0')) console.warn(`[midscene:error] forceChromeSelectRendering requires Playwright >= 1.52.0, but current version is ${playwrightVersion}. This feature may not work correctly.`);
30
+ if (opts?.forceChromeSelectRendering === true) {
31
+ const playwrightVersion = getPlaywrightVersion();
32
+ if (playwrightVersion && !semver.gte(playwrightVersion, '1.52.0')) console.warn(`[midscene:error] forceChromeSelectRendering requires Playwright >= 1.52.0, but current version is ${playwrightVersion}. This feature may not work correctly.`);
33
+ }
32
34
  base_page_mjs_forceChromeSelectRendering(page);
33
35
  }
34
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"playwright/index.mjs","sources":["../../../src/playwright/index.ts"],"sourcesContent":["import { Agent as PageAgent } from '@midscene/core/agent';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport { WebPage as PlaywrightWebPage } from './page';\n\nexport type {\n PlayWrightAiFixtureType,\n PlaywrightAiFixtureOptions,\n} from './ai-fixture';\nexport { PlaywrightAiFixture } from './ai-fixture';\nexport { overrideAIConfig } from '@midscene/shared/env';\nexport { WebPage as PlaywrightWebPage } from './page';\nexport type { WebPageAgentOpt } from '@/web-element';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport { getDebug } from '@midscene/shared/logger';\nimport semver from 'semver';\nimport {\n BROWSER_NAVIGATION_ERROR_PATTERN,\n forceChromeSelectRendering as applyChromeSelectRendering,\n forceClosePopup,\n} from '../puppeteer/base-page';\nimport { getWebpackRequire } from '../utils';\n\nconst debug = getDebug('playwright:agent');\n\n/**\n * Get Playwright version from package.json\n */\nfunction getPlaywrightVersion(): string | null {\n try {\n const playwrightPkg = getWebpackRequire()('playwright/package.json');\n return playwrightPkg.version || null;\n } catch (error) {\n console.error('[midscene:error] Failed to get Playwright version', error);\n return null;\n }\n}\n\nexport class PlaywrightAgent extends PageAgent<PlaywrightWebPage> {\n protected isRetryableContextError(error: unknown): boolean {\n return (\n error instanceof Error &&\n BROWSER_NAVIGATION_ERROR_PATTERN.test(error.message)\n );\n }\n\n constructor(page: PlaywrightPage, opts?: WebPageAgentOpt) {\n if (!page) {\n throw new Error(\n '[midscene] PlaywrightAgent requires a valid Playwright page instance. Please make sure to pass a valid page object.',\n );\n }\n const webPage = new PlaywrightWebPage(page, opts);\n super(webPage, opts);\n\n const { forceSameTabNavigation = true, forceChromeSelectRendering } =\n opts ?? {};\n\n if (forceSameTabNavigation) {\n forceClosePopup(page, debug);\n }\n\n if (forceChromeSelectRendering) {\n // Check Playwright version requirement (>= 1.52)\n const playwrightVersion = getPlaywrightVersion();\n if (playwrightVersion && !semver.gte(playwrightVersion, '1.52.0')) {\n console.warn(\n `[midscene:error] forceChromeSelectRendering requires Playwright >= 1.52.0, but current version is ${playwrightVersion}. This feature may not work correctly.`,\n );\n }\n applyChromeSelectRendering(page);\n }\n }\n}\n"],"names":["debug","getDebug","getPlaywrightVersion","playwrightPkg","getWebpackRequire","error","console","PlaywrightAgent","PageAgent","Error","BROWSER_NAVIGATION_ERROR_PATTERN","page","opts","webPage","PlaywrightWebPage","forceSameTabNavigation","forceChromeSelectRendering","forceClosePopup","playwrightVersion","semver","applyChromeSelectRendering"],"mappings":";;;;;;;;AAsBA,MAAMA,QAAQC,SAAS;AAKvB,SAASC;IACP,IAAI;QACF,MAAMC,gBAAgBC,oBAAoB;QAC1C,OAAOD,cAAc,OAAO,IAAI;IAClC,EAAE,OAAOE,OAAO;QACdC,QAAQ,KAAK,CAAC,qDAAqDD;QACnE,OAAO;IACT;AACF;AAEO,MAAME,wBAAwBC;IACzB,wBAAwBH,KAAc,EAAW;QACzD,OACEA,iBAAiBI,SACjBC,iCAAiC,IAAI,CAACL,MAAM,OAAO;IAEvD;IAEA,YAAYM,IAAoB,EAAEC,IAAsB,CAAE;QACxD,IAAI,CAACD,MACH,MAAM,IAAIF,MACR;QAGJ,MAAMI,UAAU,IAAIC,QAAkBH,MAAMC;QAC5C,KAAK,CAACC,SAASD;QAEf,MAAM,EAAEG,yBAAyB,IAAI,EAAEC,0BAA0B,EAAE,GACjEJ,QAAQ,CAAC;QAEX,IAAIG,wBACFE,gBAAgBN,MAAMX;QAGxB,IAAIgB,4BAA4B;YAE9B,MAAME,oBAAoBhB;YAC1B,IAAIgB,qBAAqB,CAACC,OAAO,GAAG,CAACD,mBAAmB,WACtDZ,QAAQ,IAAI,CACV,CAAC,kGAAkG,EAAEY,kBAAkB,sCAAsC,CAAC;YAGlKE,yCAA2BT;QAC7B;IACF;AACF"}
1
+ {"version":3,"file":"playwright/index.mjs","sources":["../../../src/playwright/index.ts"],"sourcesContent":["import { Agent as PageAgent } from '@midscene/core/agent';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport { WebPage as PlaywrightWebPage } from './page';\n\nexport type {\n PlayWrightAiFixtureType,\n PlaywrightAiFixtureOptions,\n} from './ai-fixture';\nexport { PlaywrightAiFixture } from './ai-fixture';\nexport { overrideAIConfig } from '@midscene/shared/env';\nexport { WebPage as PlaywrightWebPage } from './page';\nexport type { WebPageAgentOpt } from '@/web-element';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport { getDebug } from '@midscene/shared/logger';\nimport semver from 'semver';\nimport {\n BROWSER_NAVIGATION_ERROR_PATTERN,\n forceChromeSelectRendering as applyChromeSelectRendering,\n forceClosePopup,\n} from '../puppeteer/base-page';\nimport { getWebpackRequire } from '../utils';\n\nconst debug = getDebug('playwright:agent');\n\n/**\n * Get Playwright version from package.json\n */\nfunction getPlaywrightVersion(): string | null {\n try {\n const playwrightPkg = getWebpackRequire()('playwright/package.json');\n return playwrightPkg.version || null;\n } catch (error) {\n console.error('[midscene:error] Failed to get Playwright version', error);\n return null;\n }\n}\n\nexport class PlaywrightAgent extends PageAgent<PlaywrightWebPage> {\n protected isRetryableContextError(error: unknown): boolean {\n return (\n error instanceof Error &&\n BROWSER_NAVIGATION_ERROR_PATTERN.test(error.message)\n );\n }\n\n constructor(page: PlaywrightPage, opts?: WebPageAgentOpt) {\n if (!page) {\n throw new Error(\n '[midscene] PlaywrightAgent requires a valid Playwright page instance. Please make sure to pass a valid page object.',\n );\n }\n const webPage = new PlaywrightWebPage(page, opts);\n super(webPage, opts);\n\n const { forceSameTabNavigation = true, forceChromeSelectRendering = true } =\n opts ?? {};\n\n if (forceSameTabNavigation) {\n forceClosePopup(page, debug);\n }\n\n if (forceChromeSelectRendering) {\n // Only warn about version requirements when the user explicitly opted in;\n // it is on by default, so we should not nag users on older Playwright.\n if (opts?.forceChromeSelectRendering === true) {\n const playwrightVersion = getPlaywrightVersion();\n if (playwrightVersion && !semver.gte(playwrightVersion, '1.52.0')) {\n console.warn(\n `[midscene:error] forceChromeSelectRendering requires Playwright >= 1.52.0, but current version is ${playwrightVersion}. This feature may not work correctly.`,\n );\n }\n }\n applyChromeSelectRendering(page);\n }\n }\n}\n"],"names":["debug","getDebug","getPlaywrightVersion","playwrightPkg","getWebpackRequire","error","console","PlaywrightAgent","PageAgent","Error","BROWSER_NAVIGATION_ERROR_PATTERN","page","opts","webPage","PlaywrightWebPage","forceSameTabNavigation","forceChromeSelectRendering","forceClosePopup","playwrightVersion","semver","applyChromeSelectRendering"],"mappings":";;;;;;;;AAsBA,MAAMA,QAAQC,SAAS;AAKvB,SAASC;IACP,IAAI;QACF,MAAMC,gBAAgBC,oBAAoB;QAC1C,OAAOD,cAAc,OAAO,IAAI;IAClC,EAAE,OAAOE,OAAO;QACdC,QAAQ,KAAK,CAAC,qDAAqDD;QACnE,OAAO;IACT;AACF;AAEO,MAAME,wBAAwBC;IACzB,wBAAwBH,KAAc,EAAW;QACzD,OACEA,iBAAiBI,SACjBC,iCAAiC,IAAI,CAACL,MAAM,OAAO;IAEvD;IAEA,YAAYM,IAAoB,EAAEC,IAAsB,CAAE;QACxD,IAAI,CAACD,MACH,MAAM,IAAIF,MACR;QAGJ,MAAMI,UAAU,IAAIC,QAAkBH,MAAMC;QAC5C,KAAK,CAACC,SAASD;QAEf,MAAM,EAAEG,yBAAyB,IAAI,EAAEC,6BAA6B,IAAI,EAAE,GACxEJ,QAAQ,CAAC;QAEX,IAAIG,wBACFE,gBAAgBN,MAAMX;QAGxB,IAAIgB,4BAA4B;YAG9B,IAAIJ,MAAM,+BAA+B,MAAM;gBAC7C,MAAMM,oBAAoBhB;gBAC1B,IAAIgB,qBAAqB,CAACC,OAAO,GAAG,CAACD,mBAAmB,WACtDZ,QAAQ,IAAI,CACV,CAAC,kGAAkG,EAAEY,kBAAkB,sCAAsC,CAAC;YAGpK;YACAE,yCAA2BT;QAC7B;IACF;AACF"}