browxai 0.7.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 (520) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/THIRD_PARTY_NOTICES.md +45 -0
  4. package/dist/cli/chrome.d.ts +1 -0
  5. package/dist/cli/chrome.js +130 -0
  6. package/dist/cli/command-registry.d.ts +15 -0
  7. package/dist/cli/command-registry.js +35 -0
  8. package/dist/cli/doctor-plugins.d.ts +18 -0
  9. package/dist/cli/doctor-plugins.js +338 -0
  10. package/dist/cli/doctor.d.ts +9 -0
  11. package/dist/cli/doctor.js +407 -0
  12. package/dist/cli/init.d.ts +1 -0
  13. package/dist/cli/init.js +200 -0
  14. package/dist/cli/register-commands.d.ts +1 -0
  15. package/dist/cli/register-commands.js +22 -0
  16. package/dist/cli/serve.d.ts +14 -0
  17. package/dist/cli/serve.js +151 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.js +129 -0
  20. package/dist/engine/adapters/adb.d.ts +72 -0
  21. package/dist/engine/adapters/adb.js +200 -0
  22. package/dist/engine/adapters/android-cdp.d.ts +54 -0
  23. package/dist/engine/adapters/android-cdp.js +110 -0
  24. package/dist/engine/adapters/android.engine.d.ts +1 -0
  25. package/dist/engine/adapters/android.engine.js +31 -0
  26. package/dist/engine/adapters/chromium.engine.d.ts +1 -0
  27. package/dist/engine/adapters/chromium.engine.js +44 -0
  28. package/dist/engine/adapters/firefox.engine.d.ts +1 -0
  29. package/dist/engine/adapters/firefox.engine.js +43 -0
  30. package/dist/engine/adapters/playwright-chromium.d.ts +43 -0
  31. package/dist/engine/adapters/playwright-chromium.js +56 -0
  32. package/dist/engine/adapters/playwright-firefox.d.ts +52 -0
  33. package/dist/engine/adapters/playwright-firefox.js +97 -0
  34. package/dist/engine/adapters/playwright-webkit.d.ts +40 -0
  35. package/dist/engine/adapters/playwright-webkit.js +79 -0
  36. package/dist/engine/adapters/safari/bidi-client.d.ts +46 -0
  37. package/dist/engine/adapters/safari/bidi-client.js +130 -0
  38. package/dist/engine/adapters/safari/launch.d.ts +56 -0
  39. package/dist/engine/adapters/safari/launch.js +104 -0
  40. package/dist/engine/adapters/safari/webdriver-client.d.ts +102 -0
  41. package/dist/engine/adapters/safari/webdriver-client.js +175 -0
  42. package/dist/engine/adapters/safari.engine.d.ts +1 -0
  43. package/dist/engine/adapters/safari.engine.js +52 -0
  44. package/dist/engine/adapters/safaridriver-hybrid.d.ts +56 -0
  45. package/dist/engine/adapters/safaridriver-hybrid.js +127 -0
  46. package/dist/engine/adapters/webkit.engine.d.ts +1 -0
  47. package/dist/engine/adapters/webkit.engine.js +47 -0
  48. package/dist/engine/capabilities.d.ts +53 -0
  49. package/dist/engine/capabilities.js +122 -0
  50. package/dist/engine/capability-registry.d.ts +9 -0
  51. package/dist/engine/capability-registry.js +20 -0
  52. package/dist/engine/index.d.ts +18 -0
  53. package/dist/engine/index.js +14 -0
  54. package/dist/engine/register-engines.d.ts +5 -0
  55. package/dist/engine/register-engines.js +16 -0
  56. package/dist/engine/registry.d.ts +145 -0
  57. package/dist/engine/registry.js +67 -0
  58. package/dist/engine/select.d.ts +48 -0
  59. package/dist/engine/select.js +128 -0
  60. package/dist/engine/session-cdp.d.ts +13 -0
  61. package/dist/engine/session-cdp.js +22 -0
  62. package/dist/engine/tool-gate.d.ts +19 -0
  63. package/dist/engine/tool-gate.js +226 -0
  64. package/dist/engine/types.d.ts +71 -0
  65. package/dist/engine/types.js +16 -0
  66. package/dist/helper/bridge.d.ts +48 -0
  67. package/dist/helper/bridge.js +200 -0
  68. package/dist/helper/browx-page.d.ts +1 -0
  69. package/dist/helper/browx-page.js +47 -0
  70. package/dist/helper/overlay-hide.d.ts +9 -0
  71. package/dist/helper/overlay-hide.js +49 -0
  72. package/dist/helper/stealth.d.ts +10 -0
  73. package/dist/helper/stealth.js +88 -0
  74. package/dist/index.d.ts +7 -0
  75. package/dist/index.js +15 -0
  76. package/dist/page/a11y.d.ts +81 -0
  77. package/dist/page/a11y.js +219 -0
  78. package/dist/page/action-substrate.d.ts +64 -0
  79. package/dist/page/action-substrate.js +118 -0
  80. package/dist/page/actionresult-blocks.d.ts +99 -0
  81. package/dist/page/actionresult-blocks.js +144 -0
  82. package/dist/page/actionresult-shape.d.ts +48 -0
  83. package/dist/page/actionresult-shape.js +155 -0
  84. package/dist/page/actionresult-types.d.ts +368 -0
  85. package/dist/page/actionresult-types.js +4 -0
  86. package/dist/page/actionresult.d.ts +4 -0
  87. package/dist/page/actionresult.js +299 -0
  88. package/dist/page/actions-probe.d.ts +32 -0
  89. package/dist/page/actions-probe.js +294 -0
  90. package/dist/page/actions-scroll.d.ts +40 -0
  91. package/dist/page/actions-scroll.js +53 -0
  92. package/dist/page/actions.d.ts +132 -0
  93. package/dist/page/actions.js +453 -0
  94. package/dist/page/archive-assets.d.ts +39 -0
  95. package/dist/page/archive-assets.js +187 -0
  96. package/dist/page/archive.d.ts +47 -0
  97. package/dist/page/archive.js +349 -0
  98. package/dist/page/asset-export.d.ts +122 -0
  99. package/dist/page/asset-export.js +376 -0
  100. package/dist/page/await_network.d.ts +16 -0
  101. package/dist/page/await_network.js +23 -0
  102. package/dist/page/bbox.d.ts +37 -0
  103. package/dist/page/bbox.js +115 -0
  104. package/dist/page/canvas-capture.d.ts +82 -0
  105. package/dist/page/canvas-capture.js +257 -0
  106. package/dist/page/canvas-diff.d.ts +51 -0
  107. package/dist/page/canvas-diff.js +131 -0
  108. package/dist/page/canvas-gesture.d.ts +53 -0
  109. package/dist/page/canvas-gesture.js +167 -0
  110. package/dist/page/canvas-transform.d.ts +96 -0
  111. package/dist/page/canvas-transform.js +150 -0
  112. package/dist/page/canvas.d.ts +8 -0
  113. package/dist/page/canvas.js +50 -0
  114. package/dist/page/capture-substrate.d.ts +111 -0
  115. package/dist/page/capture-substrate.js +139 -0
  116. package/dist/page/clipboard.d.ts +25 -0
  117. package/dist/page/clipboard.js +50 -0
  118. package/dist/page/clock.d.ts +36 -0
  119. package/dist/page/clock.js +167 -0
  120. package/dist/page/compose.d.ts +55 -0
  121. package/dist/page/compose.js +169 -0
  122. package/dist/page/console.d.ts +39 -0
  123. package/dist/page/console.js +73 -0
  124. package/dist/page/coverage.d.ts +97 -0
  125. package/dist/page/coverage.js +280 -0
  126. package/dist/page/dom-export.d.ts +41 -0
  127. package/dist/page/dom-export.js +193 -0
  128. package/dist/page/dom-walk.d.ts +91 -0
  129. package/dist/page/dom-walk.js +267 -0
  130. package/dist/page/dom_diff.d.ts +48 -0
  131. package/dist/page/dom_diff.js +121 -0
  132. package/dist/page/downloads.d.ts +80 -0
  133. package/dist/page/downloads.js +244 -0
  134. package/dist/page/drop-files.d.ts +78 -0
  135. package/dist/page/drop-files.js +310 -0
  136. package/dist/page/element-export-discovery.d.ts +64 -0
  137. package/dist/page/element-export-discovery.js +346 -0
  138. package/dist/page/element-export.d.ts +46 -0
  139. package/dist/page/element-export.js +251 -0
  140. package/dist/page/emulation-substrate.d.ts +53 -0
  141. package/dist/page/emulation-substrate.js +87 -0
  142. package/dist/page/emulation.d.ts +60 -0
  143. package/dist/page/emulation.js +162 -0
  144. package/dist/page/export-playwright-script.d.ts +47 -0
  145. package/dist/page/export-playwright-script.js +304 -0
  146. package/dist/page/extract-resolve.d.ts +22 -0
  147. package/dist/page/extract-resolve.js +341 -0
  148. package/dist/page/extract-schema.d.ts +20 -0
  149. package/dist/page/extract-schema.js +200 -0
  150. package/dist/page/extract-types.d.ts +127 -0
  151. package/dist/page/extract-types.js +8 -0
  152. package/dist/page/extract-warnings.d.ts +8 -0
  153. package/dist/page/extract-warnings.js +56 -0
  154. package/dist/page/extract.d.ts +9 -0
  155. package/dist/page/extract.js +174 -0
  156. package/dist/page/fill-form.d.ts +58 -0
  157. package/dist/page/fill-form.js +261 -0
  158. package/dist/page/find.d.ts +158 -0
  159. package/dist/page/find.js +470 -0
  160. package/dist/page/frames.d.ts +45 -0
  161. package/dist/page/frames.js +133 -0
  162. package/dist/page/generate-locator.d.ts +57 -0
  163. package/dist/page/generate-locator.js +136 -0
  164. package/dist/page/gestures.d.ts +128 -0
  165. package/dist/page/gestures.js +198 -0
  166. package/dist/page/har.d.ts +91 -0
  167. package/dist/page/har.js +174 -0
  168. package/dist/page/heap.d.ts +97 -0
  169. package/dist/page/heap.js +285 -0
  170. package/dist/page/inspect.d.ts +34 -0
  171. package/dist/page/inspect.js +75 -0
  172. package/dist/page/layout-thrash.d.ts +34 -0
  173. package/dist/page/layout-thrash.js +232 -0
  174. package/dist/page/learning.d.ts +21 -0
  175. package/dist/page/learning.js +84 -0
  176. package/dist/page/locator.d.ts +54 -0
  177. package/dist/page/locator.js +142 -0
  178. package/dist/page/memory-diff.d.ts +48 -0
  179. package/dist/page/memory-diff.js +105 -0
  180. package/dist/page/network-mask.d.ts +8 -0
  181. package/dist/page/network-mask.js +18 -0
  182. package/dist/page/network-playwright.d.ts +96 -0
  183. package/dist/page/network-playwright.js +353 -0
  184. package/dist/page/network-substrate-select.d.ts +18 -0
  185. package/dist/page/network-substrate-select.js +32 -0
  186. package/dist/page/network-substrate.d.ts +109 -0
  187. package/dist/page/network-substrate.js +161 -0
  188. package/dist/page/network-ws.d.ts +46 -0
  189. package/dist/page/network-ws.js +113 -0
  190. package/dist/page/network.d.ts +194 -0
  191. package/dist/page/network.js +415 -0
  192. package/dist/page/overflow-detect.d.ts +102 -0
  193. package/dist/page/overflow-detect.js +449 -0
  194. package/dist/page/pdf.d.ts +69 -0
  195. package/dist/page/pdf.js +109 -0
  196. package/dist/page/perf-audit-analysers.d.ts +40 -0
  197. package/dist/page/perf-audit-analysers.js +369 -0
  198. package/dist/page/perf-audit-runner.d.ts +20 -0
  199. package/dist/page/perf-audit-runner.js +195 -0
  200. package/dist/page/perf-audit-types.d.ts +41 -0
  201. package/dist/page/perf-audit-types.js +5 -0
  202. package/dist/page/perf-audit.d.ts +37 -0
  203. package/dist/page/perf-audit.js +377 -0
  204. package/dist/page/perf.d.ts +127 -0
  205. package/dist/page/perf.js +373 -0
  206. package/dist/page/plan.d.ts +192 -0
  207. package/dist/page/plan.js +308 -0
  208. package/dist/page/point_probe.d.ts +46 -0
  209. package/dist/page/point_probe.js +99 -0
  210. package/dist/page/recording.d.ts +67 -0
  211. package/dist/page/recording.js +172 -0
  212. package/dist/page/refs.d.ts +92 -0
  213. package/dist/page/refs.js +134 -0
  214. package/dist/page/regions.d.ts +23 -0
  215. package/dist/page/regions.js +32 -0
  216. package/dist/page/routes.d.ts +40 -0
  217. package/dist/page/routes.js +87 -0
  218. package/dist/page/safari-actions.d.ts +12 -0
  219. package/dist/page/safari-actions.js +144 -0
  220. package/dist/page/sample.d.ts +64 -0
  221. package/dist/page/sample.js +216 -0
  222. package/dist/page/screenshot-on.d.ts +51 -0
  223. package/dist/page/screenshot-on.js +150 -0
  224. package/dist/page/screenshot-save.d.ts +36 -0
  225. package/dist/page/screenshot-save.js +53 -0
  226. package/dist/page/screenshot-schedule.d.ts +50 -0
  227. package/dist/page/screenshot-schedule.js +155 -0
  228. package/dist/page/script-substrate.d.ts +32 -0
  229. package/dist/page/script-substrate.js +47 -0
  230. package/dist/page/seed-random.d.ts +45 -0
  231. package/dist/page/seed-random.js +144 -0
  232. package/dist/page/set-of-marks.d.ts +96 -0
  233. package/dist/page/set-of-marks.js +245 -0
  234. package/dist/page/shadow.d.ts +136 -0
  235. package/dist/page/shadow.js +400 -0
  236. package/dist/page/shortcut.d.ts +50 -0
  237. package/dist/page/shortcut.js +147 -0
  238. package/dist/page/snapshot-substrate-safari.d.ts +30 -0
  239. package/dist/page/snapshot-substrate-safari.js +84 -0
  240. package/dist/page/snapshot-substrate-select.d.ts +24 -0
  241. package/dist/page/snapshot-substrate-select.js +34 -0
  242. package/dist/page/snapshot-substrate.d.ts +58 -0
  243. package/dist/page/snapshot-substrate.js +135 -0
  244. package/dist/page/snapshot.d.ts +24 -0
  245. package/dist/page/snapshot.js +162 -0
  246. package/dist/page/solve-captcha.d.ts +76 -0
  247. package/dist/page/solve-captcha.js +286 -0
  248. package/dist/page/storage-substrate-types.d.ts +221 -0
  249. package/dist/page/storage-substrate-types.js +6 -0
  250. package/dist/page/storage-substrate.d.ts +215 -0
  251. package/dist/page/storage-substrate.js +280 -0
  252. package/dist/page/structural.d.ts +9 -0
  253. package/dist/page/structural.js +152 -0
  254. package/dist/page/substrate-bundle-safari.d.ts +8 -0
  255. package/dist/page/substrate-bundle-safari.js +42 -0
  256. package/dist/page/substrate-bundle.d.ts +6 -0
  257. package/dist/page/substrate-bundle.js +53 -0
  258. package/dist/page/text_search.d.ts +44 -0
  259. package/dist/page/text_search.js +90 -0
  260. package/dist/page/upload.d.ts +28 -0
  261. package/dist/page/upload.js +62 -0
  262. package/dist/page/verify.d.ts +63 -0
  263. package/dist/page/verify.js +451 -0
  264. package/dist/page/video.d.ts +115 -0
  265. package/dist/page/video.js +169 -0
  266. package/dist/page/visibility.d.ts +22 -0
  267. package/dist/page/visibility.js +94 -0
  268. package/dist/page/watch.d.ts +29 -0
  269. package/dist/page/watch.js +99 -0
  270. package/dist/page/workers.d.ts +126 -0
  271. package/dist/page/workers.js +490 -0
  272. package/dist/page/ws-interactive.d.ts +82 -0
  273. package/dist/page/ws-interactive.js +318 -0
  274. package/dist/plugin/cli.d.ts +45 -0
  275. package/dist/plugin/cli.js +496 -0
  276. package/dist/plugin/command-registry.d.ts +9 -0
  277. package/dist/plugin/command-registry.js +23 -0
  278. package/dist/plugin/depgraph.d.ts +37 -0
  279. package/dist/plugin/depgraph.js +186 -0
  280. package/dist/plugin/manifest.d.ts +182 -0
  281. package/dist/plugin/manifest.js +219 -0
  282. package/dist/plugin/package-manager.d.ts +22 -0
  283. package/dist/plugin/package-manager.js +40 -0
  284. package/dist/plugin/resolver.d.ts +85 -0
  285. package/dist/plugin/resolver.js +166 -0
  286. package/dist/plugin/runtime.d.ts +77 -0
  287. package/dist/plugin/runtime.js +402 -0
  288. package/dist/plugin/types.d.ts +113 -0
  289. package/dist/plugin/types.js +4 -0
  290. package/dist/policy/confirm.d.ts +76 -0
  291. package/dist/policy/confirm.js +162 -0
  292. package/dist/policy/origin.d.ts +17 -0
  293. package/dist/policy/origin.js +79 -0
  294. package/dist/sdk/client.d.ts +21 -0
  295. package/dist/sdk/client.js +174 -0
  296. package/dist/sdk/index.d.ts +32 -0
  297. package/dist/sdk/index.js +61 -0
  298. package/dist/sdk/plugin-types.d.ts +33 -0
  299. package/dist/sdk/plugin-types.js +22 -0
  300. package/dist/sdk/registry.d.ts +17 -0
  301. package/dist/sdk/registry.js +94 -0
  302. package/dist/sdk/socket-transport.d.ts +20 -0
  303. package/dist/sdk/socket-transport.js +90 -0
  304. package/dist/sdk/tool-types.d.ts +634 -0
  305. package/dist/sdk/tool-types.js +28 -0
  306. package/dist/sdk/transport-in-process.d.ts +21 -0
  307. package/dist/sdk/transport-in-process.js +44 -0
  308. package/dist/sdk/transport-registry.d.ts +19 -0
  309. package/dist/sdk/transport-registry.js +31 -0
  310. package/dist/sdk/transport-socket.d.ts +12 -0
  311. package/dist/sdk/transport-socket.js +77 -0
  312. package/dist/sdk/transport-stdio-child.d.ts +10 -0
  313. package/dist/sdk/transport-stdio-child.js +47 -0
  314. package/dist/sdk/transport.d.ts +10 -0
  315. package/dist/sdk/transport.js +35 -0
  316. package/dist/sdk/types.d.ts +176 -0
  317. package/dist/sdk/types.js +10 -0
  318. package/dist/server.d.ts +33 -0
  319. package/dist/server.js +327 -0
  320. package/dist/session/artifacts.d.ts +52 -0
  321. package/dist/session/artifacts.js +177 -0
  322. package/dist/session/byob-attach.d.ts +26 -0
  323. package/dist/session/byob-attach.js +187 -0
  324. package/dist/session/byob.d.ts +8 -0
  325. package/dist/session/byob.js +20 -0
  326. package/dist/session/cache-storage.d.ts +100 -0
  327. package/dist/session/cache-storage.js +166 -0
  328. package/dist/session/device-emu.d.ts +149 -0
  329. package/dist/session/device-emu.js +545 -0
  330. package/dist/session/device.d.ts +14 -0
  331. package/dist/session/device.js +44 -0
  332. package/dist/session/dialog.d.ts +62 -0
  333. package/dist/session/dialog.js +164 -0
  334. package/dist/session/emulation.d.ts +69 -0
  335. package/dist/session/emulation.js +168 -0
  336. package/dist/session/extensions.d.ts +113 -0
  337. package/dist/session/extensions.js +237 -0
  338. package/dist/session/fs-picker.d.ts +144 -0
  339. package/dist/session/fs-picker.js +666 -0
  340. package/dist/session/idb-storage.d.ts +86 -0
  341. package/dist/session/idb-storage.js +229 -0
  342. package/dist/session/incognito.d.ts +3 -0
  343. package/dist/session/incognito.js +20 -0
  344. package/dist/session/launch-options.d.ts +41 -0
  345. package/dist/session/launch-options.js +200 -0
  346. package/dist/session/managed.d.ts +3 -0
  347. package/dist/session/managed.js +16 -0
  348. package/dist/session/metrics.d.ts +45 -0
  349. package/dist/session/metrics.js +75 -0
  350. package/dist/session/notification.d.ts +122 -0
  351. package/dist/session/notification.js +426 -0
  352. package/dist/session/permission.d.ts +144 -0
  353. package/dist/session/permission.js +600 -0
  354. package/dist/session/playwright-post-wire.d.ts +8 -0
  355. package/dist/session/playwright-post-wire.js +148 -0
  356. package/dist/session/policy-buffer.d.ts +21 -0
  357. package/dist/session/policy-buffer.js +47 -0
  358. package/dist/session/profile-snapshot.d.ts +11 -0
  359. package/dist/session/profile-snapshot.js +53 -0
  360. package/dist/session/registry.d.ts +365 -0
  361. package/dist/session/registry.js +98 -0
  362. package/dist/session/safari-post-wire.d.ts +8 -0
  363. package/dist/session/safari-post-wire.js +28 -0
  364. package/dist/session/safari-session.d.ts +10 -0
  365. package/dist/session/safari-session.js +39 -0
  366. package/dist/session/storage.d.ts +148 -0
  367. package/dist/session/storage.js +350 -0
  368. package/dist/session/types.d.ts +113 -0
  369. package/dist/session/types.js +5 -0
  370. package/dist/session/wedge.d.ts +15 -0
  371. package/dist/session/wedge.js +41 -0
  372. package/dist/tools/action-core-tools.d.ts +13 -0
  373. package/dist/tools/action-core-tools.js +156 -0
  374. package/dist/tools/action-form-tools.d.ts +12 -0
  375. package/dist/tools/action-form-tools.js +179 -0
  376. package/dist/tools/action-gesture-tools.d.ts +9 -0
  377. package/dist/tools/action-gesture-tools.js +115 -0
  378. package/dist/tools/action-history-tools.d.ts +8 -0
  379. package/dist/tools/action-history-tools.js +67 -0
  380. package/dist/tools/action-tool.d.ts +42 -0
  381. package/dist/tools/action-tool.js +58 -0
  382. package/dist/tools/action-tools.d.ts +20 -0
  383. package/dist/tools/action-tools.js +28 -0
  384. package/dist/tools/batch-act-tools.d.ts +10 -0
  385. package/dist/tools/batch-act-tools.js +276 -0
  386. package/dist/tools/batch-human-tools.d.ts +8 -0
  387. package/dist/tools/batch-human-tools.js +148 -0
  388. package/dist/tools/canvas-tools.d.ts +40 -0
  389. package/dist/tools/canvas-tools.js +368 -0
  390. package/dist/tools/capture-report-diagnostics-tools.d.ts +7 -0
  391. package/dist/tools/capture-report-diagnostics-tools.js +318 -0
  392. package/dist/tools/capture-report-element-export-tools.d.ts +8 -0
  393. package/dist/tools/capture-report-element-export-tools.js +197 -0
  394. package/dist/tools/capture-report-export-tools.d.ts +8 -0
  395. package/dist/tools/capture-report-export-tools.js +246 -0
  396. package/dist/tools/capture-report-marks-tools.d.ts +9 -0
  397. package/dist/tools/capture-report-marks-tools.js +221 -0
  398. package/dist/tools/capture-report-upload-tools.d.ts +8 -0
  399. package/dist/tools/capture-report-upload-tools.js +277 -0
  400. package/dist/tools/config-approval-tools.d.ts +8 -0
  401. package/dist/tools/config-approval-tools.js +166 -0
  402. package/dist/tools/deep-coverage-tools.d.ts +8 -0
  403. package/dist/tools/deep-coverage-tools.js +325 -0
  404. package/dist/tools/deep-determinism-tools.d.ts +8 -0
  405. package/dist/tools/deep-determinism-tools.js +276 -0
  406. package/dist/tools/deep-perf-tools.d.ts +19 -0
  407. package/dist/tools/deep-perf-tools.js +324 -0
  408. package/dist/tools/device-emulation-tools.d.ts +9 -0
  409. package/dist/tools/device-emulation-tools.js +137 -0
  410. package/dist/tools/extensions-batch-tools.d.ts +18 -0
  411. package/dist/tools/extensions-batch-tools.js +24 -0
  412. package/dist/tools/extensions-rebuild.d.ts +22 -0
  413. package/dist/tools/extensions-rebuild.js +208 -0
  414. package/dist/tools/extensions-tools.d.ts +2 -0
  415. package/dist/tools/extensions-tools.js +331 -0
  416. package/dist/tools/forms-fill-tools.d.ts +8 -0
  417. package/dist/tools/forms-fill-tools.js +109 -0
  418. package/dist/tools/forms-plan-tools.d.ts +7 -0
  419. package/dist/tools/forms-plan-tools.js +159 -0
  420. package/dist/tools/forms-recording-mode-tools.d.ts +8 -0
  421. package/dist/tools/forms-recording-mode-tools.js +71 -0
  422. package/dist/tools/forms-recording-tools.d.ts +14 -0
  423. package/dist/tools/forms-recording-tools.js +22 -0
  424. package/dist/tools/forms-refs-tools.d.ts +8 -0
  425. package/dist/tools/forms-refs-tools.js +90 -0
  426. package/dist/tools/gesture-coord-tools.d.ts +8 -0
  427. package/dist/tools/gesture-coord-tools.js +168 -0
  428. package/dist/tools/gesture-emulation-tools.d.ts +8 -0
  429. package/dist/tools/gesture-emulation-tools.js +135 -0
  430. package/dist/tools/gesture-network-tools.d.ts +17 -0
  431. package/dist/tools/gesture-network-tools.js +27 -0
  432. package/dist/tools/gesture-route-tools.d.ts +8 -0
  433. package/dist/tools/gesture-route-tools.js +142 -0
  434. package/dist/tools/gesture-websocket-tools.d.ts +8 -0
  435. package/dist/tools/gesture-websocket-tools.js +122 -0
  436. package/dist/tools/gesture-worker-tools.d.ts +9 -0
  437. package/dist/tools/gesture-worker-tools.js +200 -0
  438. package/dist/tools/host-build.d.ts +76 -0
  439. package/dist/tools/host-build.js +516 -0
  440. package/dist/tools/host.d.ts +287 -0
  441. package/dist/tools/host.js +1 -0
  442. package/dist/tools/input-tools.d.ts +10 -0
  443. package/dist/tools/input-tools.js +176 -0
  444. package/dist/tools/live-emulation-tools.d.ts +9 -0
  445. package/dist/tools/live-emulation-tools.js +353 -0
  446. package/dist/tools/plugin-runtime.d.ts +36 -0
  447. package/dist/tools/plugin-runtime.js +274 -0
  448. package/dist/tools/read-observe-buffer-tools.d.ts +9 -0
  449. package/dist/tools/read-observe-buffer-tools.js +385 -0
  450. package/dist/tools/read-observe-capture-tools.d.ts +12 -0
  451. package/dist/tools/read-observe-capture-tools.js +376 -0
  452. package/dist/tools/read-observe-dom-tools.d.ts +8 -0
  453. package/dist/tools/read-observe-dom-tools.js +308 -0
  454. package/dist/tools/read-observe-extract-tools.d.ts +8 -0
  455. package/dist/tools/read-observe-extract-tools.js +232 -0
  456. package/dist/tools/read-observe-verify-tools.d.ts +8 -0
  457. package/dist/tools/read-observe-verify-tools.js +316 -0
  458. package/dist/tools/schemas.d.ts +29 -0
  459. package/dist/tools/schemas.js +58 -0
  460. package/dist/tools/secrets-captcha-tools.d.ts +9 -0
  461. package/dist/tools/secrets-captcha-tools.js +231 -0
  462. package/dist/tools/session-dialog-permission-tools.d.ts +9 -0
  463. package/dist/tools/session-dialog-permission-tools.js +287 -0
  464. package/dist/tools/session-lifecycle-tools.d.ts +8 -0
  465. package/dist/tools/session-lifecycle-tools.js +314 -0
  466. package/dist/tools/session-notification-device-tools.d.ts +9 -0
  467. package/dist/tools/session-notification-device-tools.js +156 -0
  468. package/dist/tools/session-policy-tools.d.ts +16 -0
  469. package/dist/tools/session-policy-tools.js +22 -0
  470. package/dist/tools/session-registry.d.ts +28 -0
  471. package/dist/tools/session-registry.js +427 -0
  472. package/dist/tools/storage-artifact-har-video-tools.d.ts +8 -0
  473. package/dist/tools/storage-artifact-har-video-tools.js +311 -0
  474. package/dist/tools/storage-cache-idb-tools.d.ts +8 -0
  475. package/dist/tools/storage-cache-idb-tools.js +347 -0
  476. package/dist/tools/storage-state-cookies-tools.d.ts +8 -0
  477. package/dist/tools/storage-state-cookies-tools.js +223 -0
  478. package/dist/tools/storage-tools.d.ts +17 -0
  479. package/dist/tools/storage-tools.js +25 -0
  480. package/dist/tools/storage-web-auth-tools.d.ts +10 -0
  481. package/dist/tools/storage-web-auth-tools.js +230 -0
  482. package/dist/tools/tool-metadata.d.ts +8 -0
  483. package/dist/tools/tool-metadata.js +185 -0
  484. package/dist/util/batch.d.ts +83 -0
  485. package/dist/util/batch.js +191 -0
  486. package/dist/util/capabilities.d.ts +504 -0
  487. package/dist/util/capabilities.js +254 -0
  488. package/dist/util/config-store.d.ts +103 -0
  489. package/dist/util/config-store.js +206 -0
  490. package/dist/util/config.d.ts +11 -0
  491. package/dist/util/config.js +28 -0
  492. package/dist/util/credentials.d.ts +136 -0
  493. package/dist/util/credentials.js +622 -0
  494. package/dist/util/deadline.d.ts +22 -0
  495. package/dist/util/deadline.js +62 -0
  496. package/dist/util/diagnostics.d.ts +161 -0
  497. package/dist/util/diagnostics.js +579 -0
  498. package/dist/util/egress-sanitiser.d.ts +29 -0
  499. package/dist/util/egress-sanitiser.js +52 -0
  500. package/dist/util/failure.d.ts +8 -0
  501. package/dist/util/failure.js +50 -0
  502. package/dist/util/flake-check.d.ts +109 -0
  503. package/dist/util/flake-check.js +342 -0
  504. package/dist/util/invariant.d.ts +25 -0
  505. package/dist/util/invariant.js +66 -0
  506. package/dist/util/logging.d.ts +6 -0
  507. package/dist/util/logging.js +12 -0
  508. package/dist/util/predicates.d.ts +62 -0
  509. package/dist/util/predicates.js +340 -0
  510. package/dist/util/secrets.d.ts +104 -0
  511. package/dist/util/secrets.js +219 -0
  512. package/dist/util/tokens.d.ts +6 -0
  513. package/dist/util/tokens.js +24 -0
  514. package/dist/util/url-sanitizer.d.ts +19 -0
  515. package/dist/util/url-sanitizer.js +70 -0
  516. package/dist/util/version.d.ts +2 -0
  517. package/dist/util/version.js +21 -0
  518. package/dist/util/workspace.d.ts +7 -0
  519. package/dist/util/workspace.js +22 -0
  520. package/package.json +120 -0
@@ -0,0 +1,244 @@
1
+ // Per-session download-capture pipeline. The reverse of `upload_file`:
2
+ // intercept Playwright `download` events, persist the artifact to a
3
+ // workspace-rooted slot, and surface it on `ActionResult.downloads[]` plus
4
+ // `download_get` for byte-level retrieval.
5
+ //
6
+ // Design notes:
7
+ // - **Off by default.** A session's listener is always attached at creation,
8
+ // but it only persists files when `DownloadsRegistry.captureOn` is true
9
+ // (toggled by the `downloads_capture` MCP tool). When off the listener
10
+ // deletes Playwright's temp artifact and records nothing — keeps the
11
+ // no-trace posture for sessions that never opted in.
12
+ // - **Workspace-rooted paths only.** Captured files land in
13
+ // `$BROWX_WORKSPACE/.downloads/<sessionId>/<id>-<sanitised-name>`. The
14
+ // suggested filename from the page is *sanitised* (no path separators, no
15
+ // traversal, no NULs, no control bytes, length-capped) before composing the
16
+ // on-disk name. Same posture as `upload.ts`'s workspace-escape rejection.
17
+ // - **Per-session.** Each `SessionEntry` owns a registry; entries don't
18
+ // cross sessions. Ids are session-local and monotonic.
19
+ // - Gated by the existing `file-io` capability (no new capability), same as
20
+ // `upload_file`.
21
+ import { mkdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
22
+ import { join, resolve, sep } from "node:path";
23
+ import { log } from "../util/logging.js";
24
+ /** Per-session download registry. One instance per SessionEntry. */
25
+ export class DownloadsRegistry {
26
+ storageDir;
27
+ /** Toggled by the `downloads_capture` MCP tool. */
28
+ captureOn = false;
29
+ /** Active captures, keyed by id. Bounded to MAX_ENTRIES — oldest evicted. */
30
+ entries = new Map();
31
+ nextId = 1;
32
+ /** Captures that have fired and are not yet sliced into an ActionResult. */
33
+ pendingSince = [];
34
+ /** Max captures kept in memory + on disk per session before LRU-evicting
35
+ * the oldest. Prevents an unbounded download loop from filling the disk. */
36
+ static MAX_ENTRIES = 100;
37
+ constructor(
38
+ /** Per-session storage dir: `$BROWX_WORKSPACE/.downloads/<sessionId>/`. */
39
+ storageDir) {
40
+ this.storageDir = storageDir;
41
+ }
42
+ /** List all captured downloads for this session (most-recent first). */
43
+ list() {
44
+ return [...this.entries.values()].reverse();
45
+ }
46
+ /** Look up a capture by id; undefined if not present. */
47
+ get(id) {
48
+ return this.entries.get(id);
49
+ }
50
+ /** Slice captures that fired after `tsMs` (action-window slice). Returns a
51
+ * snapshot of the captures so the action-window can include them on the
52
+ * ActionResult without exposing the live registry. */
53
+ since(tsMs) {
54
+ return [...this.entries.values()].filter((d) => d.capturedAt >= tsMs);
55
+ }
56
+ /** Record a capture. Caller has already persisted the file at `path`. */
57
+ record(record) {
58
+ const id = `d${this.nextId++}`;
59
+ const entry = { id, ...record };
60
+ this.entries.set(id, entry);
61
+ this.pendingSince.push(entry);
62
+ // LRU evict oldest if over the cap.
63
+ while (this.entries.size > DownloadsRegistry.MAX_ENTRIES) {
64
+ const oldestKey = this.entries.keys().next().value;
65
+ if (oldestKey === undefined)
66
+ break;
67
+ const victim = this.entries.get(oldestKey);
68
+ this.entries.delete(oldestKey);
69
+ if (victim) {
70
+ // unlink under the workspace-rooted storageDir (BROWX_WORKSPACE).
71
+ try {
72
+ unlinkSync(victim.path);
73
+ }
74
+ catch {
75
+ /* best-effort cleanup */
76
+ }
77
+ }
78
+ }
79
+ return entry;
80
+ }
81
+ }
82
+ /** Sanitise a page-supplied filename for safe on-disk use. Rules (mirrors the
83
+ * workspace-escape posture in `upload.ts`):
84
+ * - strip path separators (`/`, `\`) and NUL/control bytes — collapses any
85
+ * traversal attempt to a flat filename.
86
+ * - reject leading dots so we never write to a hidden `.foo` file.
87
+ * - cap at 200 chars (leaves room for the `<id>-` prefix on filesystems
88
+ * with 255-byte name limits).
89
+ * - empty / all-stripped → fall back to `"download"`.
90
+ * Exported for unit tests. */
91
+ export function sanitiseFilename(raw) {
92
+ if (typeof raw !== "string")
93
+ return "download";
94
+ // strip NUL + control bytes (0x00-0x1f, 0x7f) and path separators.
95
+ // eslint-disable-next-line no-control-regex
96
+ let name = raw.replace(/[\x00-\x1f\x7f/\\]/g, "_");
97
+ // collapse runs of dots ("../.." → ".") so the literal substring `..`
98
+ // never survives — eliminates "looks-like-traversal" appearance even though
99
+ // the lack of separators already makes traversal impossible.
100
+ name = name.replace(/\.{2,}/g, ".");
101
+ // collapse repeated underscores from the strip pass.
102
+ name = name.replace(/_+/g, "_");
103
+ // strip leading dots so we don't write a hidden file.
104
+ name = name.replace(/^\.+/, "");
105
+ // strip leading/trailing whitespace, dots, underscores — these only exist
106
+ // because of the strip passes above; without them the filename "/" would
107
+ // remain as "_" which is unhelpful + tests as if it carried information.
108
+ name = name.replace(/^[._\s]+|[._\s]+$/g, "");
109
+ // cap length.
110
+ if (name.length > 200)
111
+ name = name.slice(0, 200);
112
+ if (!name)
113
+ return "download";
114
+ return name;
115
+ }
116
+ /** Best-effort MIME type from a filename extension. Tiny built-in table; this
117
+ * is metadata only (we never reject on it), so an unknown extension just
118
+ * yields `undefined`. */
119
+ export function mimeTypeFromName(name) {
120
+ const m = /\.([a-zA-Z0-9]+)$/.exec(name);
121
+ if (!m)
122
+ return undefined;
123
+ const ext = m[1].toLowerCase();
124
+ const map = {
125
+ pdf: "application/pdf",
126
+ csv: "text/csv",
127
+ tsv: "text/tab-separated-values",
128
+ txt: "text/plain",
129
+ json: "application/json",
130
+ xml: "application/xml",
131
+ html: "text/html",
132
+ htm: "text/html",
133
+ png: "image/png",
134
+ jpg: "image/jpeg",
135
+ jpeg: "image/jpeg",
136
+ gif: "image/gif",
137
+ webp: "image/webp",
138
+ svg: "image/svg+xml",
139
+ zip: "application/zip",
140
+ tar: "application/x-tar",
141
+ gz: "application/gzip",
142
+ mp3: "audio/mpeg",
143
+ mp4: "video/mp4",
144
+ webm: "video/webm",
145
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
146
+ xls: "application/vnd.ms-excel",
147
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
148
+ doc: "application/msword",
149
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
150
+ ppt: "application/vnd.ms-powerpoint",
151
+ };
152
+ return map[ext];
153
+ }
154
+ /** Attach the Playwright `download` listener to a context. The listener fires
155
+ * for every download on every page in the context (now or later). When
156
+ * capture is OFF the artifact is silently deleted; when ON it's persisted and
157
+ * the registry records it. Errors during capture never propagate — they
158
+ * surface as warnings on the session's log only. */
159
+ export function attachDownloadCapture(context, registry) {
160
+ context.on("download", (download) => {
161
+ void handleDownload(download, registry).catch((err) => {
162
+ log.warn("downloads: capture failed", {
163
+ error: err instanceof Error ? err.message : String(err),
164
+ });
165
+ });
166
+ });
167
+ }
168
+ async function handleDownload(download, registry) {
169
+ if (!registry.captureOn) {
170
+ // Capture disabled for this session: discard the artifact and record
171
+ // nothing. Playwright still buffers downloads to a temp file when
172
+ // `acceptDownloads: true`; cancelling drops the temp file.
173
+ await download.cancel().catch(() => undefined);
174
+ return;
175
+ }
176
+ const raw = download.suggestedFilename() ?? "download";
177
+ const safe = sanitiseFilename(raw);
178
+ // Ensure storage dir exists (created lazily; sessions that never opt in
179
+ // never create it). The dir is workspace-rooted (BROWX_WORKSPACE/.downloads
180
+ // via the SessionEntry factory in server.ts — never cwd).
181
+ mkdirSync(registry.storageDir, { recursive: true });
182
+ // Compose final on-disk path. We can't know the id yet (registry assigns
183
+ // it on `record`); use a millisecond+random prefix to disambiguate
184
+ // simultaneous downloads, then we'll record once persisted.
185
+ const prefix = `${Date.now()}-${Math.floor(Math.random() * 1e6).toString(36)}`;
186
+ const target = join(registry.storageDir, `${prefix}-${safe}`);
187
+ // Reject any composed path that doesn't resolve INSIDE storageDir. Defence
188
+ // in depth — sanitiseFilename already strips separators, but if a future
189
+ // change loosens it the workspace-escape guard catches it.
190
+ const resolved = resolve(target);
191
+ const root = resolve(registry.storageDir);
192
+ if (resolved !== root && !resolved.startsWith(root + sep)) {
193
+ await download.cancel().catch(() => undefined);
194
+ log.warn("downloads: refusing to persist outside storage dir", { resolved, root });
195
+ return;
196
+ }
197
+ try {
198
+ await download.saveAs(resolved);
199
+ }
200
+ catch (err) {
201
+ log.warn("downloads: saveAs failed", {
202
+ error: err instanceof Error ? err.message : String(err),
203
+ });
204
+ return;
205
+ }
206
+ let sizeBytes = 0;
207
+ try {
208
+ sizeBytes = statSync(resolved).size;
209
+ }
210
+ catch {
211
+ /* best-effort */
212
+ }
213
+ const rawDifferedFromSafe = raw !== safe;
214
+ registry.record({
215
+ suggestedFilename: safe,
216
+ ...(rawDifferedFromSafe ? { rawSuggestedFilename: raw } : {}),
217
+ mimeType: mimeTypeFromName(safe),
218
+ sizeBytes,
219
+ path: resolved,
220
+ capturedAt: Date.now(),
221
+ });
222
+ }
223
+ /** Read a captured download's bytes. Returns base64. Throws if the id is
224
+ * unknown or the file vanished. */
225
+ export function readCapturedBytes(reg, id) {
226
+ const entry = reg.get(id);
227
+ if (!entry) {
228
+ throw new Error(`download_get: unknown id "${id}". Call downloads_capture({on:true}) before the action that triggers the download, then read the id from ActionResult.downloads[]`);
229
+ }
230
+ let buf;
231
+ try {
232
+ buf = readFileSync(entry.path);
233
+ }
234
+ catch (err) {
235
+ throw new Error(`download_get: file vanished for id "${id}" at ${entry.path} (${err instanceof Error ? err.message : String(err)})`);
236
+ }
237
+ return {
238
+ base64: buf.toString("base64"),
239
+ bytes: buf.length,
240
+ path: entry.path,
241
+ mimeType: entry.mimeType,
242
+ suggestedFilename: entry.suggestedFilename,
243
+ };
244
+ }
@@ -0,0 +1,78 @@
1
+ import type { Page } from "playwright-core";
2
+ import type { RefRegistry } from "./refs.js";
3
+ import { type ActionTarget } from "./locator.js";
4
+ export interface DropFileInputPath {
5
+ /** Workspace-rooted file path. Mutually exclusive with `contents`. */
6
+ path: string;
7
+ /** Filename presented to the page. Defaults to the basename of `path`. */
8
+ name?: string;
9
+ /** MIME type. Defaults to "application/octet-stream". */
10
+ mimeType?: string;
11
+ }
12
+ export interface DropFileInputContents {
13
+ /** base64 file content. Mutually exclusive with `path`. */
14
+ contents: string;
15
+ /** Filename presented to the page. Required in `contents`-mode. */
16
+ name: string;
17
+ /** MIME type. Defaults to "application/octet-stream". */
18
+ mimeType?: string;
19
+ }
20
+ export type DropFileInput = DropFileInputPath | DropFileInputContents;
21
+ export interface DropFilesArgs {
22
+ target: ActionTarget;
23
+ files: DropFileInput[];
24
+ }
25
+ export interface DropFilesResult {
26
+ ok: boolean;
27
+ /** Resolved target description for debugging. */
28
+ target: string;
29
+ /** One entry per file with the resolved mode + bytes that were dropped. */
30
+ files: Array<{
31
+ name: string;
32
+ mode: "path" | "contents";
33
+ bytes: number;
34
+ mimeType: string;
35
+ }>;
36
+ /** Total bytes dispatched. */
37
+ totalBytes: number;
38
+ /** Number of files dropped. */
39
+ fileCount: number;
40
+ /** Which DOM events the page-side script actually fired. */
41
+ eventsFired: string[];
42
+ /** True when the `drop` event was dispatched (regardless of whether the
43
+ * page handler called preventDefault — we report the dispatch, not the
44
+ * app-side acceptance, since drop-zone apps vary wildly in how they
45
+ * signal "I took it"). */
46
+ dropDispatched: boolean;
47
+ }
48
+ export interface DropPayload {
49
+ files: Array<{
50
+ base64: string;
51
+ name: string;
52
+ mimeType: string;
53
+ }>;
54
+ /** Viewport-relative coords for the dispatched events. We compute these
55
+ * on the Node side (bounding-box centre for ref/selector; literal for
56
+ * coords). The page-side script writes them onto every event so apps
57
+ * that read `event.clientX`/`clientY` see consistent values. */
58
+ clientX: number;
59
+ clientY: number;
60
+ /** When the target was a coords target we have no Locator handle, so the
61
+ * page-side script does `document.elementFromPoint(clientX, clientY)`
62
+ * to find the drop target. For ref/selector targets we pass the
63
+ * pre-resolved element via the Locator's evaluate-handle. */
64
+ byCoords: boolean;
65
+ }
66
+ export interface DropEvalResult {
67
+ eventsFired: string[];
68
+ dropDispatched: boolean;
69
+ /** Tag of the element we landed on — surfaced for debugging. */
70
+ hitTag?: string;
71
+ /** Any error from the in-page side. */
72
+ error?: string;
73
+ }
74
+ export declare const dropFilesPageScript: (args: {
75
+ el: unknown;
76
+ payload: DropPayload;
77
+ }) => DropEvalResult;
78
+ export declare function dropFiles(page: Page, refs: RefRegistry, workspaceRoot: string, args: DropFilesArgs): Promise<DropFilesResult>;
@@ -0,0 +1,310 @@
1
+ /// <reference lib="dom" />
2
+ // `drop_files` — synthesize an HTML5 drag-drop of one or more files onto a
3
+ // page element.
4
+ //
5
+ // Modern uploaders are no longer `<input type=file>` — they're drop zones
6
+ // listening for `dragenter` / `dragover` / `drop` with a populated
7
+ // `DataTransfer.files`. Playwright's `setInputFiles` only drives the
8
+ // `<input>` shape, so today's only path to drive a drop-zone uploader is
9
+ // `eval_js` with hand-rolled DataTransfer plumbing. This tool is the
10
+ // first-class alternative.
11
+ //
12
+ // Two file sources, same posture as `upload_file`:
13
+ // - `contents` — base64 inline; no filesystem read at all.
14
+ // - `path` — resolved **inside `$BROWX_WORKSPACE` only**; a path
15
+ // escaping the workspace is rejected.
16
+ //
17
+ // In-page construction approach: we route bytes to the page through a
18
+ // single `page.evaluate(fn, payload)` call at drop time. The page-side
19
+ // function (defined inline so it closes-over nothing) builds `File`
20
+ // objects from base64 payloads, populates a `DataTransfer`, then
21
+ // dispatches `dragenter` → `dragover` → `drop` on the target element. We
22
+ // deliberately do NOT use `addInitScript` to install a helper: each drop
23
+ // is one-shot, the data shape varies per call, and a boot-time injection
24
+ // would leak page-side identifiers.
25
+ //
26
+ // Why base64 over the Node→page boundary and not `Uint8Array`: Playwright's
27
+ // `evaluate` round-trips a `Uint8Array` arg as a per-byte object array
28
+ // (~10× the wire footprint of base64), and we'd still need to materialise
29
+ // it as a Uint8Array in-page anyway — `atob` once on the page side is
30
+ // cheaper than the structured-clone explosion.
31
+ import { readFileSync } from "node:fs";
32
+ import { resolve, sep } from "node:path";
33
+ import { resolveTarget } from "./locator.js";
34
+ function targetSummary(t) {
35
+ if (t.ref)
36
+ return `ref ${t.ref}`;
37
+ if (t.selector)
38
+ return `selector ${t.selector}`;
39
+ if (t.coords)
40
+ return `coords ${t.coords.x},${t.coords.y}`;
41
+ return "(unknown)";
42
+ }
43
+ function basename(p) {
44
+ const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
45
+ return i >= 0 ? p.slice(i + 1) : p;
46
+ }
47
+ /** Prepare a path-mode file: workspace-escape guarded, read off disk, base64'd. */
48
+ function preparePathFile(workspaceRoot, f, i) {
49
+ const fp = f.path;
50
+ const resolved = resolve(workspaceRoot, fp);
51
+ if (resolved !== workspaceRoot && !resolved.startsWith(workspaceRoot + sep)) {
52
+ throw new Error(`drop_files: files[${i}].path must resolve inside $BROWX_WORKSPACE — stage the file there, or use \`contents\` (base64)`);
53
+ }
54
+ let buf;
55
+ try {
56
+ buf = readFileSync(resolved);
57
+ }
58
+ catch (err) {
59
+ throw new Error(`drop_files: files[${i}].path: ${err.message}`);
60
+ }
61
+ return {
62
+ base64: buf.toString("base64"),
63
+ name: f.name ?? basename(fp),
64
+ mimeType: f.mimeType ?? "application/octet-stream",
65
+ bytes: buf.length,
66
+ mode: "path",
67
+ };
68
+ }
69
+ /** Prepare a contents-mode file (base64 inline; `name` required). */
70
+ function prepareContentsFile(f, i) {
71
+ if (!f.name || f.name.length === 0) {
72
+ throw new Error(`drop_files: files[${i}]: \`name\` is required in contents-mode`);
73
+ }
74
+ return {
75
+ base64: f.contents,
76
+ name: f.name,
77
+ mimeType: f.mimeType ?? "application/octet-stream",
78
+ bytes: Buffer.from(f.contents, "base64").length,
79
+ mode: "contents",
80
+ };
81
+ }
82
+ function prepareFiles(workspaceRoot, files) {
83
+ if (!Array.isArray(files) || files.length === 0) {
84
+ throw new Error("drop_files: `files` must be a non-empty array");
85
+ }
86
+ return files.map((f, i) => {
87
+ const hasPath = typeof f.path === "string" && f.path.length > 0;
88
+ const hasContents = typeof f.contents === "string";
89
+ if (hasPath && hasContents) {
90
+ throw new Error(`drop_files: files[${i}]: pass exactly one of \`path\` or \`contents\``);
91
+ }
92
+ if (!hasPath && !hasContents) {
93
+ throw new Error(`drop_files: files[${i}]: requires \`path\` or \`contents\``);
94
+ }
95
+ return hasPath
96
+ ? preparePathFile(workspaceRoot, f, i)
97
+ : prepareContentsFile(f, i);
98
+ });
99
+ }
100
+ export const dropFilesPageScript = function PAGE_DROP_FILES_FN(args) {
101
+ const g = globalThis;
102
+ const W = g.window ?? g;
103
+ const D = g.document ?? W.document;
104
+ if (!W || !D)
105
+ return { eventsFired: [], dropDispatched: false, error: "no window/document" };
106
+ // 1. Resolve the target element. For ref/selector mode the caller passed
107
+ // `el` directly via Locator.evaluate; for coords mode `el` is null
108
+ // and we look it up via elementFromPoint. The narrowing is inlined (NOT a
109
+ // module-level helper): this function is serialized to the page by
110
+ // Locator.evaluate, so any sibling-scope reference is a ReferenceError in
111
+ // the page realm.
112
+ const elCandidate = args.el;
113
+ let target = typeof elCandidate === "object" &&
114
+ elCandidate !== null &&
115
+ typeof elCandidate.dispatchEvent === "function"
116
+ ? elCandidate
117
+ : null;
118
+ if (args.payload.byCoords || !target) {
119
+ target = D.elementFromPoint(args.payload.clientX, args.payload.clientY);
120
+ }
121
+ if (!target)
122
+ return {
123
+ eventsFired: [],
124
+ dropDispatched: false,
125
+ error: "no target element at the requested point",
126
+ };
127
+ // Bind a non-null alias so the closure below sees a narrowed element type.
128
+ const targetEl = target;
129
+ // 2. Materialise File objects from base64 payloads. `atob` is universal,
130
+ // Uint8Array → File is the canonical drop-zone construction.
131
+ const fileObjs = [];
132
+ const rawAtob = W.atob ?? g.atob;
133
+ // `atob` is a universal global (browser + Node); the captured lookups above
134
+ // only matter for unusual page contexts. Bind to `W` when present, else fall
135
+ // back to the ambient global — the same function the original `g.atob` path
136
+ // resolved to (never a silent identity map).
137
+ const atobFn = rawAtob ? rawAtob.bind(W) : atob;
138
+ const U8 = W.Uint8Array ?? g.Uint8Array ?? Uint8Array;
139
+ const FileCtor = W.File ?? g.File;
140
+ for (let i = 0; i < args.payload.files.length; i++) {
141
+ const f = args.payload.files[i];
142
+ const bin = atobFn(f.base64);
143
+ const len = bin.length;
144
+ const u8 = new U8(len);
145
+ for (let j = 0; j < len; j++)
146
+ u8[j] = bin.charCodeAt(j) & 0xff;
147
+ if (FileCtor)
148
+ fileObjs.push(new FileCtor([u8], f.name, { type: f.mimeType }));
149
+ }
150
+ // 3. Build the DataTransfer. `new DataTransfer()` is the public API
151
+ // listeners read `event.dataTransfer.files` from. `items.add(file)`
152
+ // implicitly registers the "Files" type, which apps gate on (React-
153
+ // DnD's NativeTypes.FILE, e.g.).
154
+ const DTCtor = W.DataTransfer ?? g.DataTransfer;
155
+ const dt = DTCtor ? new DTCtor() : undefined;
156
+ if (dt) {
157
+ for (const file of fileObjs) {
158
+ if (dt.items && typeof dt.items.add === "function") {
159
+ dt.items.add(file);
160
+ }
161
+ else {
162
+ // Older shims expose `files` as a settable FileList-like. Fall back
163
+ // by reassigning via Object.defineProperty so the value is enumerable
164
+ // and `length`/index access both work — best-effort for ancient
165
+ // browsers; modern Chromium hits the `items.add` path above.
166
+ try {
167
+ const arr = Array.from(dt.files);
168
+ arr.push(file);
169
+ const fakeList = arr;
170
+ fakeList.item = (idx) => arr[idx] ?? null;
171
+ Object.defineProperty(dt, "files", { value: fakeList, configurable: true });
172
+ }
173
+ catch {
174
+ /* swallow — items.add path covers Chromium */
175
+ }
176
+ }
177
+ }
178
+ }
179
+ const eventsFired = [];
180
+ const DragEv = W.DragEvent ?? g.DragEvent;
181
+ const Ev = W.Event ?? g.Event ?? Event;
182
+ const fireOne = (kind) => {
183
+ // DragEvent is preferred (carries `dataTransfer` natively); some
184
+ // older browsers (jsdom in tests) don't expose it as a constructor.
185
+ // Fall back to a regular Event with dataTransfer assigned through a
186
+ // property descriptor — every real Chromium hits the DragEvent path.
187
+ let ev;
188
+ try {
189
+ if (!DragEv)
190
+ throw new Error("no DragEvent");
191
+ ev = new DragEv(kind, {
192
+ bubbles: true,
193
+ cancelable: true,
194
+ composed: true,
195
+ clientX: args.payload.clientX,
196
+ clientY: args.payload.clientY,
197
+ dataTransfer: dt,
198
+ });
199
+ }
200
+ catch {
201
+ const base = new Ev(kind, { bubbles: true, cancelable: true, composed: true });
202
+ ev = base;
203
+ try {
204
+ Object.defineProperty(base, "dataTransfer", { value: dt, configurable: true });
205
+ }
206
+ catch {
207
+ /* best-effort */
208
+ }
209
+ base.clientX = args.payload.clientX;
210
+ base.clientY = args.payload.clientY;
211
+ }
212
+ // Modern Chromium DragEvent may have a read-only dataTransfer that
213
+ // ignores the constructor option. Defensive: if it didn't take, force
214
+ // it via descriptor.
215
+ try {
216
+ if (ev.dataTransfer !== dt) {
217
+ Object.defineProperty(ev, "dataTransfer", { value: dt, configurable: true });
218
+ }
219
+ }
220
+ catch {
221
+ /* best-effort */
222
+ }
223
+ targetEl.dispatchEvent(ev);
224
+ eventsFired.push(kind);
225
+ };
226
+ // 4. Dispatch the standard HTML5 drop sequence. `dragenter` → `dragover`
227
+ // → `drop`. We send `dragover` once; a real human drag would emit it
228
+ // continuously, but most drop-zone listeners only need one to set
229
+ // `event.dataTransfer.dropEffect` / call `preventDefault` so the
230
+ // subsequent `drop` is accepted.
231
+ fireOne("dragenter");
232
+ fireOne("dragover");
233
+ fireOne("drop");
234
+ const tag = target.tagName ? String(target.tagName).toLowerCase() : "?";
235
+ return {
236
+ eventsFired,
237
+ dropDispatched: eventsFired.indexOf("drop") >= 0,
238
+ hitTag: tag,
239
+ };
240
+ };
241
+ /** Resolve the viewport-relative drop point: coords mode uses the literal point;
242
+ * element mode uses the target's bounding-box centre (throwing when unrendered). */
243
+ async function computeDropPoint(resolved) {
244
+ if (resolved.kind === "coords") {
245
+ return { clientX: resolved.x, clientY: resolved.y, byCoords: true };
246
+ }
247
+ const box = await resolved.loc.boundingBox().catch(() => null);
248
+ if (!box || box.width <= 0 || box.height <= 0) {
249
+ throw new Error("drop_files: target element has no rendered box — scroll into view or wait for it to mount");
250
+ }
251
+ return { clientX: box.x + box.width / 2, clientY: box.y + box.height / 2, byCoords: false };
252
+ }
253
+ export async function dropFiles(page, refs, workspaceRoot, args) {
254
+ if (!args || !args.target) {
255
+ throw new Error("drop_files: requires `target` (ref/selector/named/coords)");
256
+ }
257
+ const prepared = prepareFiles(workspaceRoot, args.files);
258
+ // Compute the viewport-relative click point on the Node side so apps reading
259
+ // `event.clientX`/`clientY` see realistic values.
260
+ const resolved = resolveTarget(page, refs, args.target);
261
+ const { clientX, clientY, byCoords } = await computeDropPoint(resolved);
262
+ const payload = {
263
+ clientX,
264
+ clientY,
265
+ byCoords,
266
+ files: prepared.map((p) => ({ base64: p.base64, name: p.name, mimeType: p.mimeType })),
267
+ };
268
+ // Inline the page-side script as a string so the closure-over-
269
+ // `dropFilesPageScript` survives the CDP boundary. Playwright's
270
+ // `evaluate(fn, arg)` accepts the function source as a string, parses
271
+ // it in the page, and invokes it with the cloned argument bag — exactly
272
+ // what we want, no addInitScript indirection required.
273
+ const scriptSource = dropFilesPageScript.toString();
274
+ // Dispatch. For ref/selector targets we route through the Locator so
275
+ // the page-side script receives the element directly as `el`. For coords
276
+ // we run on `page` and the script re-resolves via `elementFromPoint`.
277
+ const result = resolved.kind === "coords"
278
+ ? await page.evaluate((a) => {
279
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
280
+ const factory = new Function("return (" + a.src + ");");
281
+ const fn = factory();
282
+ return fn({ el: null, payload: a.payload });
283
+ }, { payload, src: scriptSource })
284
+ : await resolved.loc.evaluate(
285
+ // Locator.evaluate signature: (element, arg).
286
+ (el, a) => {
287
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
288
+ const factory = new Function("return (" + a.src + ");");
289
+ const fn = factory();
290
+ return fn({ el, payload: a.payload });
291
+ }, { payload, src: scriptSource });
292
+ if (result.error) {
293
+ throw new Error(`drop_files: in-page dispatch failed — ${result.error}`);
294
+ }
295
+ const totalBytes = prepared.reduce((acc, p) => acc + p.bytes, 0);
296
+ return {
297
+ ok: true,
298
+ target: targetSummary(args.target),
299
+ files: prepared.map((p) => ({
300
+ name: p.name,
301
+ mode: p.mode,
302
+ bytes: p.bytes,
303
+ mimeType: p.mimeType,
304
+ })),
305
+ totalBytes,
306
+ fileCount: prepared.length,
307
+ eventsFired: result.eventsFired,
308
+ dropDispatched: result.dropDispatched,
309
+ };
310
+ }