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,666 @@
1
+ // Per-session File System Access API policy. Sibling of `dialog_policy` /
2
+ // `permission_policy`. Plugs the FS-picker blind spot: modern web editors
3
+ // (VSCode for the web, Figma, anything calling `showOpenFilePicker` /
4
+ // `showSaveFilePicker` / `showDirectoryPicker`) deadlock under a headless
5
+ // session — the picker dialog blocks every subsequent browser event until
6
+ // the human clicks a real OS file chooser that doesn't exist in headless,
7
+ // and even on attached Chrome the human can't see the picker through the
8
+ // agent's session.
9
+ //
10
+ // Policy modes (mirror `permission_policy`):
11
+ // - "allow" — the agent provides files server-side via `fs_picker_respond`;
12
+ // the init-script stub returns synthetic FileSystem*Handle
13
+ // objects to the page. For showSaveFilePicker, the agent
14
+ // supplies a workspace-rooted destination path and writes
15
+ // from `createWritable()` are persisted there.
16
+ // - "deny" — the stub throws `NotAllowedError` ("user dismissed the
17
+ // picker"). The page sees the standard rejection path.
18
+ // - "raise" — DEFAULT (deterministic anti-deadlock). Stub throws
19
+ // `NotAllowedError` AND records the request as
20
+ // `handledAs:"raised"`. The next ActionResult flips
21
+ // `ok:false` with a stable hint pointing at
22
+ // `set_fs_picker_policy`. The page never blocks (the
23
+ // picker rejects immediately), but a picker call never
24
+ // silently changes app state under an unaware caller
25
+ // either.
26
+ // - "ask-human" — server records the request, blocks on the bridge
27
+ // (`await_human({kind:"input"})` mechanism), then either
28
+ // resolves with agent-provided files or denies per the
29
+ // human's response.
30
+ //
31
+ // Per-API override map. The top-level `mode` is the default; the per-API
32
+ // map (`perAPI: { showSaveFilePicker: "allow", … }`) overrides it for a
33
+ // specific picker. Mirrors `permission_policy.perPermission`.
34
+ //
35
+ // Per-action capture. Every page-side picker call is appended to a buffer
36
+ // with a timestamp. `since(ts)` slices for the action window — same pattern
37
+ // as `dialog_policy` / `permission_policy`; `raisedSince(ts)` drives the
38
+ // `ok:false` flip.
39
+ //
40
+ // Why one layer (init-script stubs) instead of two (CDP + init-script):
41
+ // - There is no CDP analogue for the File System Access API — Chromium
42
+ // exposes the picker UX only via the real OS file chooser, which
43
+ // headless can't drive and which (on attached Chrome) wouldn't route
44
+ // through the agent. The init-script stub IS the policy enforcement
45
+ // point. The native `window.show*FilePicker` is replaced before any
46
+ // page script runs, so the original is never called.
47
+ //
48
+ // FileSystemFileHandle.createWritable() handling:
49
+ // - In `allow` mode for `showSaveFilePicker`, the agent supplies a
50
+ // workspace-rooted `path` via `fs_picker_respond`. The init-script
51
+ // stub returns a synthetic `FileSystemFileHandle` whose
52
+ // `createWritable()` returns a stub `FileSystemWritableFileStream`
53
+ // that routes every `write(chunk)` / `truncate()` / `close()` /
54
+ // `abort()` through a server-side binding (`__browx_fs_picker_write`),
55
+ // which append-writes to the workspace path. Workspace-escape on the
56
+ // path is rejected at `fs_picker_respond` time (the agent never
57
+ // supplies a path to the page-side stub directly).
58
+ // - In `allow` mode for `showOpenFilePicker`, the agent supplies either
59
+ // inline `{contents, name}` (base64 file bytes the page reads back
60
+ // via `getFile()`) or a workspace-rooted `{path}` (server reads the
61
+ // file once at respond time and inlines the bytes). The handle's
62
+ // `createWritable()` returns a no-op stub — open-pickers are
63
+ // read-side; the agent didn't supply a destination.
64
+ // - `showDirectoryPicker` returns a minimal directory handle: `.name`
65
+ // is the basename of the agent-supplied path (or "browxai-virtual"
66
+ // when synthetic); `entries()` / `values()` / `keys()` iterate empty.
67
+ // Best-effort by construction — a real directory tree would require
68
+ // either reading the workspace path recursively (heavy + a footgun
69
+ // when the workspace holds artefacts the page shouldn't see) or
70
+ // synthesising one from agent input (complex). MVP scope is "the
71
+ // picker dialog doesn't deadlock and the page can check that it got
72
+ // a directory" — most modern editors will then re-prompt for
73
+ // individual files.
74
+ import { writeFileSync, appendFileSync, existsSync, mkdirSync } from "node:fs";
75
+ import { dirname, resolve, sep, basename } from "node:path";
76
+ import { log } from "../util/logging.js";
77
+ import { PolicyRecordBuffer } from "./policy-buffer.js";
78
+ /** The three File System Access API entry points browxai governs. v1 set.
79
+ * Re-exported in `fs_picker_policy` tool docs + tool-reference.md. */
80
+ export const SUPPORTED_FS_PICKER_APIS = [
81
+ "showOpenFilePicker",
82
+ "showSaveFilePicker",
83
+ "showDirectoryPicker",
84
+ ];
85
+ /** Hint emitted on `ActionResult.failure.hint` when `raise` mode fired.
86
+ * Stable, agent-facing string — referenced in docs/tool-reference.md. */
87
+ export const UNHANDLED_FS_PICKER_HINT = "unhandled File System Access picker — set fsPickerPolicy (open_session/set_fs_picker_policy) " +
88
+ 'to "allow", "deny", or "ask-human" before driving an action that may trigger one. ' +
89
+ "The picker was rejected page-side (NotAllowedError) so the page is not deadlocked, but " +
90
+ "the app effect is the user-dismissed branch. In `allow` mode, supply the file(s) with " +
91
+ "`fs_picker_respond` before (or in parallel with) the action that triggers the picker.";
92
+ /** Mutable per-session state. The page-side stubs read `current(api)` on
93
+ * every call, so a `set_fs_picker_policy` call takes effect on the very
94
+ * next picker without page reload. */
95
+ export class FsPickerPolicyState {
96
+ policy;
97
+ /** Bounded record ring (shared `PolicyRecordBuffer` — the hard cap so a chatty
98
+ * page can't grow this without bound; the per-action slice is the only
99
+ * consumer, older records are noise). */
100
+ records;
101
+ /** Per-API response queue. `fs_picker_respond` pushes; the binding
102
+ * dequeues on the next matching picker call. Per-API so a queued
103
+ * open-file response doesn't satisfy a save-file picker (different
104
+ * semantics, different agent intent). */
105
+ responses = {
106
+ showOpenFilePicker: [],
107
+ showSaveFilePicker: [],
108
+ showDirectoryPicker: [],
109
+ };
110
+ /** Contexts we've already installed the init-script + binding on.
111
+ * Idempotent install guard — BYOB reconnect / context rebuild MUST not
112
+ * double-wire. */
113
+ wired = new WeakSet();
114
+ constructor(initial = { mode: "raise" }, cap = 200) {
115
+ this.policy = normalise(initial);
116
+ this.records = new PolicyRecordBuffer(cap);
117
+ }
118
+ /** Resolved policy snapshot. */
119
+ current() {
120
+ return {
121
+ mode: this.policy.mode,
122
+ ...(this.policy.perAPI ? { perAPI: { ...this.policy.perAPI } } : {}),
123
+ };
124
+ }
125
+ /** Effective mode for one API — per-API map wins, else top-level. */
126
+ modeFor(api) {
127
+ return this.policy.perAPI?.[api] ?? this.policy.mode;
128
+ }
129
+ set(next) {
130
+ this.policy = normalise(next);
131
+ return this.current();
132
+ }
133
+ /** Append a request record. Caps the buffer at `cap`. */
134
+ record(rec) {
135
+ this.records.record(rec);
136
+ }
137
+ /** Slice records with `ts >= since`. Used by the action-window. */
138
+ since(since) {
139
+ return this.records.since(since);
140
+ }
141
+ /** True if any record in `[since, now]` was handled in `raise` mode.
142
+ * When true, the action-window flips the result to `ok:false`. */
143
+ raisedSince(since) {
144
+ return this.records.matchedSince(since, (r) => r.handledAs === "raised");
145
+ }
146
+ /** Queue an agent-supplied response for the next picker of `api`.
147
+ * Pushed by `fs_picker_respond`; consumed by the binding on the next
148
+ * matching call. */
149
+ pushResponse(api, files) {
150
+ this.responses[api].push(files);
151
+ }
152
+ /** Dequeue the next agent-supplied response for `api`. Returns
153
+ * `undefined` when the queue is empty (the binding falls back to a
154
+ * best-effort empty file list so the page still gets a usable
155
+ * shape — not deadlocking is the contract). */
156
+ dequeueResponse(api) {
157
+ return this.responses[api].shift();
158
+ }
159
+ /** Has this context already been wired? Idempotent install guard. */
160
+ hasContext(c) {
161
+ return this.wired.has(c);
162
+ }
163
+ /** Mark a context as wired. */
164
+ markContext(c) {
165
+ this.wired.add(c);
166
+ }
167
+ }
168
+ /** Idempotent normaliser. Rejects unknown top-level modes; per-API map is
169
+ * validated per-entry (an unknown API name in the map throws so the caller
170
+ * gets a fast error instead of silent fallthrough). */
171
+ function normalise(p) {
172
+ if (!isFsPickerMode(p.mode)) {
173
+ throw new Error(`fsPickerPolicy: invalid mode "${String(p.mode)}" — expected "allow" | "deny" | "raise" | "ask-human"`);
174
+ }
175
+ if (p.perAPI) {
176
+ const cleaned = {};
177
+ for (const [name, mode] of Object.entries(p.perAPI)) {
178
+ if (!SUPPORTED_FS_PICKER_APIS.includes(name)) {
179
+ throw new Error(`fsPickerPolicy.perAPI: unknown API "${name}" — supported: ${SUPPORTED_FS_PICKER_APIS.join(", ")}`);
180
+ }
181
+ if (mode === undefined)
182
+ continue;
183
+ if (!isFsPickerMode(mode)) {
184
+ throw new Error(`fsPickerPolicy.perAPI["${name}"]: invalid mode "${String(mode)}" — expected "allow" | "deny" | "raise" | "ask-human"`);
185
+ }
186
+ cleaned[name] = mode;
187
+ }
188
+ return { mode: p.mode, perAPI: cleaned };
189
+ }
190
+ return { mode: p.mode };
191
+ }
192
+ function isFsPickerMode(m) {
193
+ return m === "allow" || m === "deny" || m === "raise" || m === "ask-human";
194
+ }
195
+ /** Parse the spec's compact string form for the top-level mode, or accept
196
+ * the object form. Idempotent. */
197
+ export function parseFsPickerPolicyArg(v) {
198
+ if (!v)
199
+ return { mode: "raise" };
200
+ if (typeof v === "object")
201
+ return normalise(v);
202
+ if (isFsPickerMode(v))
203
+ return { mode: v };
204
+ throw new Error(`fsPickerPolicy: invalid value "${v}" — expected "allow" | "deny" | "raise" | "ask-human"`);
205
+ }
206
+ /** Resolve + validate a workspace-rooted file path. Mirrors the workspace-
207
+ * escape rejection in `src/page/upload.ts`. Exported for tests; used by
208
+ * the binding install + by `fs_picker_respond`. */
209
+ export function resolveWorkspaceFsPath(workspaceRoot, path) {
210
+ const resolved = resolve(workspaceRoot, path);
211
+ if (resolved !== workspaceRoot && !resolved.startsWith(workspaceRoot + sep)) {
212
+ throw new Error("fs_picker_respond: `path` must resolve inside $BROWX_WORKSPACE — stage the file there, or use `contents` (base64)");
213
+ }
214
+ return resolved;
215
+ }
216
+ /** Init script that replaces the page-side File System Access entry points
217
+ * with stubs that route through the per-session policy. Stringified so it
218
+ * can be passed to `addInitScript` and `page.evaluate`. Keep browser-only
219
+ * JS — no TS-only syntax. Re-injected on `framenavigated` (idempotent:
220
+ * guards on `window.__browx_fs_picker_installed`).
221
+ *
222
+ * The stubs consult `window.__browx_fs_picker_check({api, suggestedName})`
223
+ * (an exposeBinding callable from page context) — it returns one of:
224
+ * - `{decision:"allow", files:[{handleId, name, mimeType, contents?}]}`:
225
+ * the agent staged file(s); the stub builds synthetic
226
+ * `FileSystemFileHandle` / `FileSystemDirectoryHandle` objects whose
227
+ * `getFile()` returns a synthetic `File` and `createWritable()` returns
228
+ * a synthetic writable stream routed through
229
+ * `__browx_fs_picker_write({handleId, op, data?})`.
230
+ * - `{decision:"deny"}`: the stub throws `NotAllowedError`.
231
+ *
232
+ * Stubs are written to be browser-only JS (no TS-only syntax). The
233
+ * install guards on `window.__browx_fs_picker_installed`. */
234
+ export const FS_PICKER_PAGE_SCRIPT = `(() => {
235
+ if (window.__browx_fs_picker_installed) return;
236
+ window.__browx_fs_picker_installed = true;
237
+
238
+ function check(api, suggestedName) {
239
+ try {
240
+ if (typeof window.__browx_fs_picker_check === "function") {
241
+ return Promise.resolve(window.__browx_fs_picker_check(JSON.stringify({
242
+ api: api, suggestedName: suggestedName,
243
+ })));
244
+ }
245
+ } catch (_) {}
246
+ // Binding missing — safe-by-default deny so the page never deadlocks.
247
+ return Promise.resolve(JSON.stringify({ decision: "deny" }));
248
+ }
249
+
250
+ function notAllowed(msg) {
251
+ var e = new Error(msg || "The user aborted a request.");
252
+ try { e.name = "NotAllowedError"; } catch (_) {}
253
+ return e;
254
+ }
255
+
256
+ function b64ToBytes(b64) {
257
+ try {
258
+ var binary = atob(b64);
259
+ var len = binary.length;
260
+ var bytes = new Uint8Array(len);
261
+ for (var i = 0; i < len; i++) bytes[i] = binary.charCodeAt(i);
262
+ return bytes;
263
+ } catch (_) {
264
+ return new Uint8Array(0);
265
+ }
266
+ }
267
+
268
+ function syntheticFile(spec) {
269
+ var name = spec.name || "browxai-virtual";
270
+ var mimeType = spec.mimeType || "application/octet-stream";
271
+ var bytes = spec.contents ? b64ToBytes(spec.contents) : new Uint8Array(0);
272
+ try {
273
+ return new File([bytes], name, { type: mimeType });
274
+ } catch (_) {
275
+ // Fallback Blob-shaped object for environments without File (test
276
+ // pages); the constructor is universally available on real browsers.
277
+ var blob = new Blob([bytes], { type: mimeType });
278
+ blob.name = name;
279
+ return blob;
280
+ }
281
+ }
282
+
283
+ function syntheticWritable(handleId) {
284
+ // Route every operation through the server-side binding. Each call is
285
+ // ack'd by the binding so we surface back-pressure to a determined page
286
+ // (await stream.write(buf) resolves only after the write hit disk).
287
+ function call(op, data) {
288
+ try {
289
+ if (typeof window.__browx_fs_picker_write === "function") {
290
+ return Promise.resolve(window.__browx_fs_picker_write(JSON.stringify({
291
+ handleId: handleId, op: op, data: data == null ? null : data,
292
+ })));
293
+ }
294
+ } catch (_) {}
295
+ return Promise.resolve(undefined);
296
+ }
297
+ return {
298
+ write: function (data) {
299
+ // Accept BufferSource | Blob | string | { type:"write"|"seek"|"truncate", … }
300
+ if (data == null) return Promise.resolve(undefined);
301
+ if (typeof data === "string") return call("write", data);
302
+ if (data.type === "seek") return call("seek", String(data.position || 0));
303
+ if (data.type === "truncate") return call("truncate", String(data.size || 0));
304
+ if (data.type === "write" && data.data != null) return call("write", encodeForBinding(data.data));
305
+ return call("write", encodeForBinding(data));
306
+ },
307
+ seek: function (position) { return call("seek", String(position || 0)); },
308
+ truncate: function (size) { return call("truncate", String(size || 0)); },
309
+ close: function () { return call("close"); },
310
+ abort: function () { return call("abort"); },
311
+ };
312
+ }
313
+
314
+ function encodeForBinding(data) {
315
+ // base64-encode any BufferSource / Blob / string for binding transport.
316
+ // exposeBinding payloads are strings; we wrap as "b64:<base64>" so the
317
+ // server side can distinguish from a literal text write.
318
+ function bytesToB64(bytes) {
319
+ var s = "";
320
+ for (var i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
321
+ try { return "b64:" + btoa(s); } catch (_) { return "b64:"; }
322
+ }
323
+ if (typeof data === "string") return data;
324
+ if (data instanceof ArrayBuffer) return bytesToB64(new Uint8Array(data));
325
+ if (ArrayBuffer.isView && ArrayBuffer.isView(data)) {
326
+ return bytesToB64(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
327
+ }
328
+ if (data instanceof Blob) {
329
+ // Synchronous-from-page Promise; the binding awaits.
330
+ return data.arrayBuffer().then(function (ab) { return bytesToB64(new Uint8Array(ab)); });
331
+ }
332
+ return String(data);
333
+ }
334
+
335
+ function syntheticFileHandle(spec) {
336
+ var handleId = spec.handleId;
337
+ var name = spec.name || "browxai-virtual";
338
+ return {
339
+ kind: "file",
340
+ name: name,
341
+ getFile: function () { return Promise.resolve(syntheticFile(spec)); },
342
+ createWritable: function () { return Promise.resolve(syntheticWritable(handleId)); },
343
+ // Minimal queryPermission / requestPermission — the page often probes
344
+ // before reading. Always "granted" for our virtual handles.
345
+ queryPermission: function () { return Promise.resolve("granted"); },
346
+ requestPermission: function () { return Promise.resolve("granted"); },
347
+ // Comparison helper expected by some libraries.
348
+ isSameEntry: function (other) { return Promise.resolve(other === this); },
349
+ };
350
+ }
351
+
352
+ function syntheticDirectoryHandle(spec) {
353
+ var name = spec.name || "browxai-virtual";
354
+ // MVP scope: empty directory. Most editors will fall back to per-file
355
+ // pickers when iteration yields nothing.
356
+ var empty = {
357
+ next: function () { return Promise.resolve({ value: undefined, done: true }); },
358
+ return: function () { return Promise.resolve({ value: undefined, done: true }); },
359
+ };
360
+ var emptyIter = { __asyncIterator__: true };
361
+ emptyIter[Symbol.asyncIterator] = function () { return empty; };
362
+ return {
363
+ kind: "directory",
364
+ name: name,
365
+ entries: function () { return emptyIter; },
366
+ values: function () { return emptyIter; },
367
+ keys: function () { return emptyIter; },
368
+ getFileHandle: function () { return Promise.reject(notAllowed("Not found in virtual directory")); },
369
+ getDirectoryHandle: function () { return Promise.reject(notAllowed("Not found in virtual directory")); },
370
+ removeEntry: function () { return Promise.resolve(undefined); },
371
+ resolve: function () { return Promise.resolve(null); },
372
+ queryPermission: function () { return Promise.resolve("granted"); },
373
+ requestPermission: function () { return Promise.resolve("granted"); },
374
+ isSameEntry: function (other) { return Promise.resolve(other === this); },
375
+ [Symbol.asyncIterator]: function () { return empty; },
376
+ };
377
+ }
378
+
379
+ function installStub(apiName, isDirectory, isMulti) {
380
+ Object.defineProperty(window, apiName, {
381
+ configurable: true,
382
+ writable: true,
383
+ value: function (options) {
384
+ var suggestedName = options && options.suggestedName ? String(options.suggestedName) : undefined;
385
+ return check(apiName, suggestedName).then(function (raw) {
386
+ var resp;
387
+ try { resp = typeof raw === "string" ? JSON.parse(raw) : (raw || {}); } catch (_) { resp = { decision: "deny" }; }
388
+ if (resp.decision !== "allow") {
389
+ throw notAllowed("The user aborted a " + apiName + " request.");
390
+ }
391
+ var files = Array.isArray(resp.files) ? resp.files : [];
392
+ if (isDirectory) {
393
+ var dirSpec = files[0] || { handleId: resp.handleIdFallback || "dir-0", name: "browxai-virtual" };
394
+ return syntheticDirectoryHandle(dirSpec);
395
+ }
396
+ if (isMulti) {
397
+ return files.map(function (f) { return syntheticFileHandle(f); });
398
+ }
399
+ var spec = files[0] || { handleId: resp.handleIdFallback || "file-0", name: "browxai-virtual" };
400
+ return syntheticFileHandle(spec);
401
+ });
402
+ },
403
+ });
404
+ }
405
+
406
+ // showOpenFilePicker: returns Array<FileSystemFileHandle> (multi by default).
407
+ installStub("showOpenFilePicker", false, true);
408
+ // showSaveFilePicker: returns FileSystemFileHandle (single).
409
+ installStub("showSaveFilePicker", false, false);
410
+ // showDirectoryPicker: returns FileSystemDirectoryHandle (single).
411
+ installStub("showDirectoryPicker", true, false);
412
+ })();`;
413
+ /** Server-side wire-up. Installs:
414
+ * - `__browx_fs_picker_check` exposeBinding: synchronous-from-page consult
415
+ * that records the request, dequeues an agent-staged response when the
416
+ * policy is `allow` (or routes ask-human via `askHandler`), and returns
417
+ * `{decision, files?}`. Files carry a `handleId` the page-side stub
418
+ * uses to route subsequent `createWritable()` ops back to the right
419
+ * target.
420
+ * - `__browx_fs_picker_write` exposeBinding: target of every
421
+ * `createWritable()`-driven write. Writes append-mode to the
422
+ * workspace-rooted path the agent supplied. `close` / `abort` clean
423
+ * up the per-handle target.
424
+ * - The page-side init script (see above), re-injected by Playwright on
425
+ * every new document.
426
+ *
427
+ * Idempotent on the same context. Errors during install are logged and
428
+ * swallowed — the page-side stub falls back to safe-by-default deny when
429
+ * the binding is missing, so the page still doesn't deadlock.
430
+ */
431
+ export async function attachFsPickerPolicy(context, state, workspaceRoot, askHandler) {
432
+ if (state.hasContext(context))
433
+ return;
434
+ state.markContext(context);
435
+ // Per-context handle map. Each `allow`-mode response mints a fresh
436
+ // handle id; the page stub round-trips it on every write op so we
437
+ // route writes to the right target file.
438
+ const handles = new Map();
439
+ let handleCounter = 0;
440
+ try {
441
+ await context.exposeBinding("__browx_fs_picker_check", async (_source, payload) => {
442
+ try {
443
+ const o = JSON.parse(payload);
444
+ const api = o.api;
445
+ if (!SUPPORTED_FS_PICKER_APIS.includes(api)) {
446
+ // Unknown API — safe-by-default deny.
447
+ return JSON.stringify({ decision: "deny" });
448
+ }
449
+ const suggestedName = o.suggestedName;
450
+ const mode = state.modeFor(api);
451
+ const ts = Date.now();
452
+ const baseRec = {
453
+ api,
454
+ ts,
455
+ ...(suggestedName ? { suggestedName } : {}),
456
+ };
457
+ switch (mode) {
458
+ case "allow": {
459
+ const files = state.dequeueResponse(api) ?? [];
460
+ const prepared = prepareAllowResponse(api, files, workspaceRoot, handles, () => `h${++handleCounter}`);
461
+ state.record({ ...baseRec, handledAs: "allowed" });
462
+ return JSON.stringify({ decision: "allow", files: prepared });
463
+ }
464
+ case "deny": {
465
+ state.record({ ...baseRec, handledAs: "denied" });
466
+ return JSON.stringify({ decision: "deny" });
467
+ }
468
+ case "ask-human": {
469
+ const askResult = await askHandler(api, suggestedName).catch(() => null);
470
+ state.record({ ...baseRec, handledAs: "asked-human" });
471
+ if (!askResult)
472
+ return JSON.stringify({ decision: "deny" });
473
+ const prepared = prepareAllowResponse(api, askResult, workspaceRoot, handles, () => `h${++handleCounter}`);
474
+ return JSON.stringify({ decision: "allow", files: prepared });
475
+ }
476
+ case "raise":
477
+ default: {
478
+ state.record({ ...baseRec, handledAs: "raised" });
479
+ return JSON.stringify({ decision: "deny" });
480
+ }
481
+ }
482
+ }
483
+ catch (err) {
484
+ log.warn("session.fs-picker: check handler error", {
485
+ error: err instanceof Error ? err.message : String(err),
486
+ });
487
+ return JSON.stringify({ decision: "deny" });
488
+ }
489
+ });
490
+ await context.exposeBinding("__browx_fs_picker_write", (_source, payload) => {
491
+ try {
492
+ const o = JSON.parse(payload);
493
+ const id = o.handleId;
494
+ const op = o.op;
495
+ if (!id || !op)
496
+ return undefined;
497
+ const target = handles.get(id);
498
+ if (!target)
499
+ return undefined;
500
+ if (target.closed && op !== "close" && op !== "abort")
501
+ return undefined;
502
+ if (target.path === null) {
503
+ // Open-picker read-side; the page is writing back to a virtual
504
+ // handle. Drop on the floor with a one-time warning so the page
505
+ // doesn't see an error mid-flight.
506
+ if (op === "write") {
507
+ log.warn("session.fs-picker: write to a read-only virtual handle dropped — open-picker responses don't carry a writable destination; use showSaveFilePicker for writes");
508
+ }
509
+ if (op === "close" || op === "abort")
510
+ target.closed = true;
511
+ return undefined;
512
+ }
513
+ // `target.path` was validated against `workspace.root` (workspace-
514
+ // rooted; workspace-escape rejected at fs_picker_respond time via
515
+ // `resolveWorkspaceFsPath(workspaceRoot, …)` — see prepareAllow-
516
+ // Response). Every mutation below routes through this validated
517
+ // path, never cwd.
518
+ const path = target.path;
519
+ switch (op) {
520
+ case "write": {
521
+ const bytes = decodeChunk(o.data);
522
+ // workspace-rooted write — `path` came from workspace.root.
523
+ if (!target.truncated) {
524
+ const dir = dirname(path);
525
+ if (!existsSync(dir))
526
+ mkdirSync(dir, { recursive: true });
527
+ writeFileSync(path, bytes);
528
+ target.truncated = true;
529
+ }
530
+ else {
531
+ // workspace-rooted append — `path` came from workspace.root.
532
+ appendFileSync(path, bytes);
533
+ }
534
+ target.bytesWritten += bytes.length;
535
+ return undefined;
536
+ }
537
+ case "truncate": {
538
+ // Best-effort: rewrite empty up to the requested size.
539
+ // workspace-rooted write — `path` came from workspace.root.
540
+ const size = Number(o.data ?? 0);
541
+ const dir = dirname(path);
542
+ if (!existsSync(dir))
543
+ mkdirSync(dir, { recursive: true });
544
+ writeFileSync(path, Buffer.alloc(Math.max(0, size)));
545
+ target.truncated = true;
546
+ target.bytesWritten = Math.max(0, size);
547
+ return undefined;
548
+ }
549
+ case "seek": {
550
+ // No-op in MVP: Node fs has no native seek-and-overwrite for
551
+ // append-mode; would need fd APIs. Most save-picker flows do
552
+ // a single write+close sequence, so seek is rare.
553
+ return undefined;
554
+ }
555
+ case "close": {
556
+ target.closed = true;
557
+ // If the page never wrote anything, ensure an empty file
558
+ // exists so callers see a deterministic artefact.
559
+ // workspace-rooted write — `path` came from workspace.root.
560
+ if (!target.truncated) {
561
+ const dir = dirname(path);
562
+ if (!existsSync(dir))
563
+ mkdirSync(dir, { recursive: true });
564
+ writeFileSync(path, Buffer.alloc(0));
565
+ target.truncated = true;
566
+ }
567
+ return undefined;
568
+ }
569
+ case "abort": {
570
+ target.closed = true;
571
+ return undefined;
572
+ }
573
+ default:
574
+ return undefined;
575
+ }
576
+ }
577
+ catch (err) {
578
+ log.warn("session.fs-picker: write handler error", {
579
+ error: err instanceof Error ? err.message : String(err),
580
+ });
581
+ return undefined;
582
+ }
583
+ });
584
+ }
585
+ catch (err) {
586
+ log.warn("session.fs-picker: exposeBinding install failed; page-side stub falls back to deny", {
587
+ error: err instanceof Error ? err.message : String(err),
588
+ });
589
+ }
590
+ try {
591
+ await context.addInitScript({ content: FS_PICKER_PAGE_SCRIPT });
592
+ for (const page of context.pages()) {
593
+ await page.evaluate(FS_PICKER_PAGE_SCRIPT).catch(() => undefined);
594
+ }
595
+ }
596
+ catch (err) {
597
+ log.warn("session.fs-picker: addInitScript failed", {
598
+ error: err instanceof Error ? err.message : String(err),
599
+ });
600
+ }
601
+ }
602
+ /** Build the page-side response shape for an `allow` / `ask-human` decision.
603
+ * Each file gets a fresh handleId; save-picker `path` entries register a
604
+ * write target so subsequent `createWritable()` writes land in the
605
+ * workspace. Workspace-escape on `path` is rejected here (the agent's
606
+ * response is the only place a path enters the system; `fs_picker_respond`
607
+ * is the validation gate). */
608
+ function prepareAllowResponse(api, files, workspaceRoot, handles, nextId) {
609
+ // Directory picker: agent supplies (at most) one entry; we honour the
610
+ // basename of `path` as `.name` if supplied.
611
+ if (api === "showDirectoryPicker") {
612
+ const entry = files[0];
613
+ const handleId = nextId();
614
+ const path = entry?.path !== undefined ? resolveWorkspaceFsPath(workspaceRoot, entry.path) : null;
615
+ const name = path ? basename(path) : (entry?.name ?? "browxai-virtual");
616
+ handles.set(handleId, {
617
+ api,
618
+ path: null, // directory handle: writes go to per-file children, not the dir itself
619
+ truncated: false,
620
+ closed: false,
621
+ bytesWritten: 0,
622
+ });
623
+ return [{ handleId, name, mimeType: "" }];
624
+ }
625
+ // showSaveFilePicker is single-file; showOpenFilePicker is multi.
626
+ const slice = api === "showSaveFilePicker" ? files.slice(0, 1) : files;
627
+ // Empty-list edge: still hand back ONE virtual file so the page's
628
+ // promise resolves with a usable shape instead of an empty array
629
+ // (the spec says showSaveFilePicker returns a single handle; an empty
630
+ // result would force a downstream NPE on most page code).
631
+ const effective = slice.length > 0 ? slice : [{ name: "browxai-virtual" }];
632
+ return effective.map((f) => {
633
+ const handleId = nextId();
634
+ let path = null;
635
+ let name = f.name ?? "browxai-virtual";
636
+ if (f.path !== undefined) {
637
+ path = resolveWorkspaceFsPath(workspaceRoot, f.path);
638
+ name = basename(path);
639
+ }
640
+ const target = {
641
+ api,
642
+ path: api === "showSaveFilePicker" ? path : null,
643
+ truncated: false,
644
+ closed: false,
645
+ bytesWritten: 0,
646
+ };
647
+ handles.set(handleId, target);
648
+ const out = {
649
+ handleId,
650
+ name,
651
+ mimeType: f.mimeType ?? "application/octet-stream",
652
+ };
653
+ if (f.contents !== undefined)
654
+ out.contents = f.contents;
655
+ return out;
656
+ });
657
+ }
658
+ /** Decode a page-side write payload. Either `"b64:<base64>"` for binary
659
+ * data or a literal string for text writes. Returns a Buffer. */
660
+ function decodeChunk(raw) {
661
+ if (raw === null || raw === undefined)
662
+ return Buffer.alloc(0);
663
+ if (raw.startsWith("b64:"))
664
+ return Buffer.from(raw.slice("b64:".length), "base64");
665
+ return Buffer.from(raw, "utf8");
666
+ }