@mirasim/computer-use 0.0.0-stage → 0.1.0-rc.3

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 (341) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +154 -2
  4. package/THIRD_PARTY_NOTICES +488 -0
  5. package/bin/mirasim-cu.mjs +30 -0
  6. package/dist/entries/cli.d.ts +2 -0
  7. package/dist/entries/cli.js +32 -0
  8. package/dist/entries/commands/approvals.d.ts +1 -0
  9. package/dist/entries/commands/approvals.js +17 -0
  10. package/dist/entries/commands/bridge-host.d.ts +1 -0
  11. package/dist/entries/commands/bridge-host.js +7 -0
  12. package/dist/entries/commands/bridge.d.ts +1 -0
  13. package/dist/entries/commands/bridge.js +16 -0
  14. package/dist/entries/commands/browser-state.d.ts +1 -0
  15. package/dist/entries/commands/browser-state.js +8 -0
  16. package/dist/entries/commands/capture-server.d.ts +1 -0
  17. package/dist/entries/commands/capture-server.js +5 -0
  18. package/dist/entries/commands/help.d.ts +2 -0
  19. package/dist/entries/commands/help.js +1 -0
  20. package/dist/entries/commands/native-host-options.d.ts +32 -0
  21. package/dist/entries/commands/native-host-options.js +7 -0
  22. package/dist/entries/commands/setup-macos.d.ts +1 -0
  23. package/dist/entries/commands/setup-macos.js +4 -0
  24. package/dist/entries/commands/setup.d.ts +1 -0
  25. package/dist/entries/commands/setup.js +15 -0
  26. package/dist/entries/mcp.d.ts +1 -0
  27. package/dist/entries/mcp.js +6 -0
  28. package/dist/entries/version.d.ts +1 -0
  29. package/dist/entries/version.js +1 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.js +1 -0
  32. package/dist/platform/browser/acquire.d.ts +15 -0
  33. package/dist/platform/browser/acquire.js +1 -0
  34. package/dist/platform/browser/bridge/acquire-bridge.d.ts +7 -0
  35. package/dist/platform/browser/bridge/acquire-bridge.js +3 -0
  36. package/dist/platform/browser/bridge/client.d.ts +69 -0
  37. package/dist/platform/browser/bridge/client.js +1 -0
  38. package/dist/platform/browser/bridge/extension-channel.d.ts +12 -0
  39. package/dist/platform/browser/bridge/extension-channel.js +1 -0
  40. package/dist/platform/browser/bridge/extension-id.d.ts +8 -0
  41. package/dist/platform/browser/bridge/extension-id.js +1 -0
  42. package/dist/platform/browser/bridge/extension-updates.d.ts +37 -0
  43. package/dist/platform/browser/bridge/extension-updates.js +1 -0
  44. package/dist/platform/browser/bridge/model-commands.d.ts +20 -0
  45. package/dist/platform/browser/bridge/model-commands.js +1 -0
  46. package/dist/platform/browser/bridge/model-core.d.ts +59 -0
  47. package/dist/platform/browser/bridge/model-core.js +1 -0
  48. package/dist/platform/browser/bridge/model.d.ts +4 -0
  49. package/dist/platform/browser/bridge/model.js +1 -0
  50. package/dist/platform/browser/bridge/native-frames.d.ts +9 -0
  51. package/dist/platform/browser/bridge/native-frames.js +1 -0
  52. package/dist/platform/browser/bridge/native-host.d.ts +24 -0
  53. package/dist/platform/browser/bridge/native-host.js +5 -0
  54. package/dist/platform/browser/bridge/runtime-contexts.d.ts +12 -0
  55. package/dist/platform/browser/bridge/runtime-contexts.js +1 -0
  56. package/dist/platform/browser/bridge/server-core.d.ts +66 -0
  57. package/dist/platform/browser/bridge/server-core.js +1 -0
  58. package/dist/platform/browser/bridge/server.d.ts +13 -0
  59. package/dist/platform/browser/bridge/server.js +1 -0
  60. package/dist/platform/browser/bridge-tabs.d.ts +22 -0
  61. package/dist/platform/browser/bridge-tabs.js +1 -0
  62. package/dist/platform/browser/cdp-connection.d.ts +3 -0
  63. package/dist/platform/browser/cdp-connection.js +1 -0
  64. package/dist/platform/browser/chrome-binary.d.ts +6 -0
  65. package/dist/platform/browser/chrome-binary.js +1 -0
  66. package/dist/platform/browser/config.d.ts +29 -0
  67. package/dist/platform/browser/config.js +1 -0
  68. package/dist/platform/browser/devtools-port.d.ts +3 -0
  69. package/dist/platform/browser/devtools-port.js +2 -0
  70. package/dist/platform/browser/downloads.d.ts +22 -0
  71. package/dist/platform/browser/downloads.js +1 -0
  72. package/dist/platform/browser/driver-connect.d.ts +13 -0
  73. package/dist/platform/browser/driver-connect.js +1 -0
  74. package/dist/platform/browser/driver-core.d.ts +50 -0
  75. package/dist/platform/browser/driver-core.js +1 -0
  76. package/dist/platform/browser/driver-tabs.d.ts +16 -0
  77. package/dist/platform/browser/driver-tabs.js +1 -0
  78. package/dist/platform/browser/driver-verbs.d.ts +13 -0
  79. package/dist/platform/browser/driver-verbs.js +2 -0
  80. package/dist/platform/browser/driver.d.ts +38 -0
  81. package/dist/platform/browser/driver.js +3 -0
  82. package/dist/platform/browser/focus-guard.d.ts +2 -0
  83. package/dist/platform/browser/focus-guard.js +1 -0
  84. package/dist/platform/browser/journal.d.ts +21 -0
  85. package/dist/platform/browser/journal.js +2 -0
  86. package/dist/platform/browser/keep-in-tab.d.ts +1 -0
  87. package/dist/platform/browser/keep-in-tab.js +1 -0
  88. package/dist/platform/browser/messages.d.ts +25 -0
  89. package/dist/platform/browser/messages.js +8 -0
  90. package/dist/platform/browser/navigate.d.ts +4 -0
  91. package/dist/platform/browser/navigate.js +2 -0
  92. package/dist/platform/browser/occupancy.d.ts +2 -0
  93. package/dist/platform/browser/occupancy.js +2 -0
  94. package/dist/platform/browser/overlay-guard.d.ts +3 -0
  95. package/dist/platform/browser/overlay-guard.js +1 -0
  96. package/dist/platform/browser/save-picker.d.ts +2 -0
  97. package/dist/platform/browser/save-picker.js +1 -0
  98. package/dist/platform/browser/session-files.d.ts +11 -0
  99. package/dist/platform/browser/session-files.js +1 -0
  100. package/dist/platform/browser/state.d.ts +25 -0
  101. package/dist/platform/browser/state.js +2 -0
  102. package/dist/platform/browser/tabs.d.ts +15 -0
  103. package/dist/platform/browser/tabs.js +2 -0
  104. package/dist/platform/browser/window-placement.d.ts +8 -0
  105. package/dist/platform/browser/window-placement.js +1 -0
  106. package/dist/platform/chromium/actions-value.d.ts +11 -0
  107. package/dist/platform/chromium/actions-value.js +5 -0
  108. package/dist/platform/chromium/actions.d.ts +20 -0
  109. package/dist/platform/chromium/actions.js +2 -0
  110. package/dist/platform/chromium/checkable-target.d.ts +1 -0
  111. package/dist/platform/chromium/checkable-target.js +1 -0
  112. package/dist/platform/chromium/collect-viewport.d.ts +1 -0
  113. package/dist/platform/chromium/collect-viewport.js +1 -0
  114. package/dist/platform/chromium/collect.d.ts +11 -0
  115. package/dist/platform/chromium/collect.js +1 -0
  116. package/dist/platform/chromium/coverer-label.d.ts +8 -0
  117. package/dist/platform/chromium/coverer-label.js +1 -0
  118. package/dist/platform/chromium/frames.d.ts +2 -0
  119. package/dist/platform/chromium/frames.js +1 -0
  120. package/dist/platform/chromium/headings.d.ts +1 -0
  121. package/dist/platform/chromium/headings.js +1 -0
  122. package/dist/platform/chromium/locate.d.ts +2 -0
  123. package/dist/platform/chromium/locate.js +1 -0
  124. package/dist/platform/chromium/page-acts.d.ts +9 -0
  125. package/dist/platform/chromium/page-acts.js +1 -0
  126. package/dist/platform/chromium/page-base.d.ts +33 -0
  127. package/dist/platform/chromium/page-base.js +1 -0
  128. package/dist/platform/chromium/page-driver.d.ts +18 -0
  129. package/dist/platform/chromium/page-driver.js +2 -0
  130. package/dist/platform/chromium/page-refs.d.ts +4 -0
  131. package/dist/platform/chromium/page-refs.js +1 -0
  132. package/dist/platform/chromium/page-text.d.ts +15 -0
  133. package/dist/platform/chromium/page-text.js +1 -0
  134. package/dist/platform/chromium/page-waits.d.ts +7 -0
  135. package/dist/platform/chromium/page-waits.js +1 -0
  136. package/dist/platform/chromium/preview-stream.d.ts +37 -0
  137. package/dist/platform/chromium/preview-stream.js +1 -0
  138. package/dist/platform/chromium/preview.d.ts +3 -0
  139. package/dist/platform/chromium/preview.js +1 -0
  140. package/dist/platform/chromium/read-page-script.d.ts +1 -0
  141. package/dist/platform/chromium/read-page-script.js +1 -0
  142. package/dist/platform/chromium/read-page.d.ts +11 -0
  143. package/dist/platform/chromium/read-page.js +6 -0
  144. package/dist/platform/chromium/run-js.d.ts +4 -0
  145. package/dist/platform/chromium/run-js.js +2 -0
  146. package/dist/platform/chromium/scroll-regions.d.ts +1 -0
  147. package/dist/platform/chromium/scroll-regions.js +1 -0
  148. package/dist/platform/chromium/scroll-scripts.d.ts +3 -0
  149. package/dist/platform/chromium/scroll-scripts.js +1 -0
  150. package/dist/platform/chromium/scroll.d.ts +18 -0
  151. package/dist/platform/chromium/scroll.js +5 -0
  152. package/dist/platform/chromium/select-text.d.ts +4 -0
  153. package/dist/platform/chromium/select-text.js +2 -0
  154. package/dist/platform/chromium/shot.d.ts +10 -0
  155. package/dist/platform/chromium/shot.js +2 -0
  156. package/dist/platform/chromium/unreached.d.ts +8 -0
  157. package/dist/platform/chromium/unreached.js +1 -0
  158. package/dist/platform/cli-command.d.ts +1 -0
  159. package/dist/platform/cli-command.js +1 -0
  160. package/dist/platform/driver-base.d.ts +83 -0
  161. package/dist/platform/driver-base.js +1 -0
  162. package/dist/platform/driver-diagnostics.d.ts +30 -0
  163. package/dist/platform/driver-diagnostics.js +4 -0
  164. package/dist/platform/driver-lines.d.ts +15 -0
  165. package/dist/platform/driver-lines.js +2 -0
  166. package/dist/platform/driver-narrate.d.ts +26 -0
  167. package/dist/platform/driver-narrate.js +3 -0
  168. package/dist/platform/driver-refs.d.ts +16 -0
  169. package/dist/platform/driver-refs.js +1 -0
  170. package/dist/platform/driver-render.d.ts +26 -0
  171. package/dist/platform/driver-render.js +4 -0
  172. package/dist/platform/driver-select.d.ts +31 -0
  173. package/dist/platform/driver-select.js +2 -0
  174. package/dist/platform/driver-settle.d.ts +22 -0
  175. package/dist/platform/driver-settle.js +4 -0
  176. package/dist/platform/driver-verdict.d.ts +11 -0
  177. package/dist/platform/driver-verdict.js +5 -0
  178. package/dist/platform/driver-wait.d.ts +9 -0
  179. package/dist/platform/driver-wait.js +1 -0
  180. package/dist/platform/driver-waits.d.ts +10 -0
  181. package/dist/platform/driver-waits.js +1 -0
  182. package/dist/platform/driver.d.ts +39 -0
  183. package/dist/platform/driver.js +15 -0
  184. package/dist/platform/electron/driver.d.ts +30 -0
  185. package/dist/platform/electron/driver.js +7 -0
  186. package/dist/platform/electron/preflight.d.ts +9 -0
  187. package/dist/platform/electron/preflight.js +1 -0
  188. package/dist/platform/electron/verbs.d.ts +3 -0
  189. package/dist/platform/electron/verbs.js +1 -0
  190. package/dist/platform/home.d.ts +2 -0
  191. package/dist/platform/home.js +1 -0
  192. package/dist/platform/macos/act-element.d.ts +27 -0
  193. package/dist/platform/macos/act-element.js +1 -0
  194. package/dist/platform/macos/app-names.d.ts +2 -0
  195. package/dist/platform/macos/app-names.js +2 -0
  196. package/dist/platform/macos/app-notes.d.ts +2 -0
  197. package/dist/platform/macos/app-notes.js +86 -0
  198. package/dist/platform/macos/approvals.d.ts +10 -0
  199. package/dist/platform/macos/approvals.js +2 -0
  200. package/dist/platform/macos/ax-client.d.ts +53 -0
  201. package/dist/platform/macos/ax-client.js +3 -0
  202. package/dist/platform/macos/ax-errors.d.ts +16 -0
  203. package/dist/platform/macos/ax-errors.js +1 -0
  204. package/dist/platform/macos/ax-shapes.d.ts +53 -0
  205. package/dist/platform/macos/ax-shapes.js +1 -0
  206. package/dist/platform/macos/capture-client.d.ts +11 -0
  207. package/dist/platform/macos/capture-client.js +2 -0
  208. package/dist/platform/macos/click-verb.d.ts +4 -0
  209. package/dist/platform/macos/click-verb.js +1 -0
  210. package/dist/platform/macos/driver-apps.d.ts +59 -0
  211. package/dist/platform/macos/driver-apps.js +2 -0
  212. package/dist/platform/macos/driver-read.d.ts +47 -0
  213. package/dist/platform/macos/driver-read.js +1 -0
  214. package/dist/platform/macos/driver-targets.d.ts +9 -0
  215. package/dist/platform/macos/driver-targets.js +1 -0
  216. package/dist/platform/macos/driver.d.ts +65 -0
  217. package/dist/platform/macos/driver.js +2 -0
  218. package/dist/platform/macos/element-lines.d.ts +12 -0
  219. package/dist/platform/macos/element-lines.js +1 -0
  220. package/dist/platform/macos/element-shapes.d.ts +54 -0
  221. package/dist/platform/macos/element-shapes.js +0 -0
  222. package/dist/platform/macos/element-text.d.ts +3 -0
  223. package/dist/platform/macos/element-text.js +2 -0
  224. package/dist/platform/macos/elements.d.ts +26 -0
  225. package/dist/platform/macos/elements.js +1 -0
  226. package/dist/platform/macos/extra-verbs.d.ts +2 -0
  227. package/dist/platform/macos/extra-verbs.js +1 -0
  228. package/dist/platform/macos/interaction-notes.d.ts +2 -0
  229. package/dist/platform/macos/interaction-notes.js +1 -0
  230. package/dist/platform/macos/interaction-policy.d.ts +5 -0
  231. package/dist/platform/macos/interaction-policy.js +1 -0
  232. package/dist/platform/macos/menu-commands.d.ts +22 -0
  233. package/dist/platform/macos/menu-commands.js +5 -0
  234. package/dist/platform/macos/mouse.d.ts +23 -0
  235. package/dist/platform/macos/mouse.js +1 -0
  236. package/dist/platform/macos/narrate.d.ts +20 -0
  237. package/dist/platform/macos/narrate.js +1 -0
  238. package/dist/platform/macos/permissions.d.ts +5 -0
  239. package/dist/platform/macos/permissions.js +1 -0
  240. package/dist/platform/macos/press-rungs.d.ts +49 -0
  241. package/dist/platform/macos/press-rungs.js +5 -0
  242. package/dist/platform/macos/preview-stream.d.ts +15 -0
  243. package/dist/platform/macos/preview-stream.js +1 -0
  244. package/dist/platform/macos/prompt-fragment.d.ts +1 -0
  245. package/dist/platform/macos/prompt-fragment.js +1 -0
  246. package/dist/platform/macos/scroll-pages.d.ts +1 -0
  247. package/dist/platform/macos/scroll-pages.js +1 -0
  248. package/dist/platform/macos/scroll-verb.d.ts +5 -0
  249. package/dist/platform/macos/scroll-verb.js +1 -0
  250. package/dist/platform/macos/select-text-verb.d.ts +8 -0
  251. package/dist/platform/macos/select-text-verb.js +1 -0
  252. package/dist/platform/macos/type-verb.d.ts +5 -0
  253. package/dist/platform/macos/type-verb.js +1 -0
  254. package/dist/platform/ownership.d.ts +20 -0
  255. package/dist/platform/ownership.js +1 -0
  256. package/dist/platform/platform-package.d.ts +1 -0
  257. package/dist/platform/platform-package.js +1 -0
  258. package/dist/platform/preview.d.ts +39 -0
  259. package/dist/platform/preview.js +1 -0
  260. package/dist/platform/profile.d.ts +20 -0
  261. package/dist/platform/profile.js +1 -0
  262. package/dist/platform/registry.d.ts +12 -0
  263. package/dist/platform/registry.js +1 -0
  264. package/dist/platform/tools/api-contract.d.ts +33 -0
  265. package/dist/platform/tools/api-contract.js +3 -0
  266. package/dist/platform/tools/api-expect.d.ts +3 -0
  267. package/dist/platform/tools/api-expect.js +1 -0
  268. package/dist/platform/tools/api-run.d.ts +6 -0
  269. package/dist/platform/tools/api-run.js +1 -0
  270. package/dist/platform/tools/api-target.d.ts +22 -0
  271. package/dist/platform/tools/api-target.js +1 -0
  272. package/dist/platform/tools/api-wait.d.ts +3 -0
  273. package/dist/platform/tools/api-wait.js +1 -0
  274. package/dist/platform/tools/api.d.ts +7 -0
  275. package/dist/platform/tools/api.js +1 -0
  276. package/dist/platform/tools/find-target.d.ts +54 -0
  277. package/dist/platform/tools/find-target.js +1 -0
  278. package/dist/platform/tools/index.d.ts +8 -0
  279. package/dist/platform/tools/index.js +1 -0
  280. package/dist/platform/tools/normalize.d.ts +2 -0
  281. package/dist/platform/tools/normalize.js +1 -0
  282. package/dist/platform/tools/repl-budget.d.ts +8 -0
  283. package/dist/platform/tools/repl-budget.js +4 -0
  284. package/dist/platform/tools/repl-code.d.ts +1 -0
  285. package/dist/platform/tools/repl-code.js +4 -0
  286. package/dist/platform/tools/repl-docs.d.ts +5 -0
  287. package/dist/platform/tools/repl-docs.js +4 -0
  288. package/dist/platform/tools/repl-state.d.ts +21 -0
  289. package/dist/platform/tools/repl-state.js +0 -0
  290. package/dist/platform/tools/repl.d.ts +8 -0
  291. package/dist/platform/tools/repl.js +19 -0
  292. package/dist/platform/tools/shot-file.d.ts +7 -0
  293. package/dist/platform/tools/shot-file.js +1 -0
  294. package/dist/platform/tools/validate.d.ts +2 -0
  295. package/dist/platform/tools/validate.js +1 -0
  296. package/dist/platform/types.d.ts +220 -0
  297. package/dist/platform/types.js +1 -0
  298. package/dist/platform/windows/app-gate.d.ts +24 -0
  299. package/dist/platform/windows/app-gate.js +2 -0
  300. package/dist/platform/windows/background-verb.d.ts +13 -0
  301. package/dist/platform/windows/background-verb.js +1 -0
  302. package/dist/platform/windows/dates.d.ts +2 -0
  303. package/dist/platform/windows/dates.js +1 -0
  304. package/dist/platform/windows/driver-apps.d.ts +9 -0
  305. package/dist/platform/windows/driver-apps.js +1 -0
  306. package/dist/platform/windows/driver-read.d.ts +12 -0
  307. package/dist/platform/windows/driver-read.js +3 -0
  308. package/dist/platform/windows/driver-targets.d.ts +58 -0
  309. package/dist/platform/windows/driver-targets.js +1 -0
  310. package/dist/platform/windows/driver-yield.d.ts +10 -0
  311. package/dist/platform/windows/driver-yield.js +1 -0
  312. package/dist/platform/windows/driver.d.ts +39 -0
  313. package/dist/platform/windows/driver.js +2 -0
  314. package/dist/platform/windows/elements.d.ts +66 -0
  315. package/dist/platform/windows/elements.js +2 -0
  316. package/dist/platform/windows/launch.d.ts +23 -0
  317. package/dist/platform/windows/launch.js +1 -0
  318. package/dist/platform/windows/menu-verb.d.ts +7 -0
  319. package/dist/platform/windows/menu-verb.js +6 -0
  320. package/dist/platform/windows/prompt-fragment.d.ts +2 -0
  321. package/dist/platform/windows/prompt-fragment.js +1 -0
  322. package/dist/platform/windows/text-verbs.d.ts +10 -0
  323. package/dist/platform/windows/text-verbs.js +5 -0
  324. package/dist/platform/windows/uia-client.d.ts +32 -0
  325. package/dist/platform/windows/uia-client.js +3 -0
  326. package/dist/platform/windows/verbs.d.ts +2 -0
  327. package/dist/platform/windows/verbs.js +1 -0
  328. package/extension/background.js +280 -0
  329. package/extension/connect.html +27 -0
  330. package/extension/connect.js +28 -0
  331. package/extension/group.js +283 -0
  332. package/extension/icons/icon-128.png +0 -0
  333. package/extension/icons/icon-16.png +0 -0
  334. package/extension/icons/icon-32.png +0 -0
  335. package/extension/icons/icon-48.png +0 -0
  336. package/extension/manifest.json +36 -0
  337. package/extension/relay.js +361 -0
  338. package/extension/runtime-info.js +9 -0
  339. package/extension/transport.js +81 -0
  340. package/package.json +76 -4
  341. package/scripts/extension.mjs +111 -0
@@ -0,0 +1,2 @@
1
+ const a=15e3;function i(e){const t=e instanceof Error?e.message:String(e);return/Timeout.*exceeded|TimeoutError/i.test(t)?{ok:!0,mayHaveActed:!0,note:`navigation still loading after ${15e3/1e3}s \u2014 the observation shows what has rendered so far`}:{ok:!1,mayHaveActed:!0,note:t.split(`
2
+ `)[0]??"navigation failed"}}function s(e,t,r){const o=n=>n.replace(/\/+$/,"");return o(t)===o(r)?`${e} ${r}`:`${t} redirected to ${r}`}export{a as NAV_TIMEOUT_MS,s as landedNote,i as navVerdict};
@@ -0,0 +1,2 @@
1
+ export declare function browserPids(profileDir: string): Promise<number[]>;
2
+ export declare function killByProfileDir(profileDir: string): Promise<void>;
@@ -0,0 +1,2 @@
1
+ import{execFile as h}from"node:child_process";import{existsSync as u,realpathSync as w}from"node:fs";import{delimiter as x,join as f}from"node:path";const y=o=>new Promise(e=>setTimeout(e,o));async function P(o){return C(await g(),o)}const S=["Get-CimInstance Win32_Process",`-Filter "Name='chrome.exe' OR Name='msedge.exe' OR Name='chromium.exe'"`,'| ForEach-Object { "$($_.ProcessId) $($_.CommandLine)" }'].join(" ");function I(){const o=process.env.SystemRoot||process.env.windir||"C:\\Windows",e=f(o,"System32","WindowsPowerShell","v1.0","powershell.exe");if(u(e))return e;for(const t of(process.env.PATH??"").split(x))for(const n of["powershell.exe","pwsh.exe"])if(t&&u(f(t,n)))return f(t,n);return"powershell.exe"}async function g(){const o=process.platform==="win32",e=o?I():"ps",t=o?["-NoProfile","-NonInteractive","-Command",S]:["ax","-o","pid=,command="];return await new Promise(n=>{h(e,t,{timeout:o?2e4:4e3,maxBuffer:16*1024*1024,windowsHide:!0},(s,r)=>n(s?"":r))})}function C(o,e,t=L(e)){const n=process.platform==="win32",s=`--user-data-dir=${n?e.toLowerCase():e}`,r=[];for(const m of o.split(`
2
+ `)){const i=m.replace(/"/g,""),c=n?i.toLowerCase():i;if(/--type=/.test(c))continue;const a=c.indexOf(s),p=a<0?void 0:c[a+s.length];if(!(a>=0&&(p===void 0||/\s/.test(p)))){const d=/--user-data-dir=(.+?)(?=\s+--|\s*$)/.exec(i)?.[1];if(!d||!t(d))continue}const l=Number.parseInt(m.trim().split(/\s+/)[0]??"",10);Number.isInteger(l)&&l>1&&r.push(l)}return r}function L(o){const e=n=>process.platform==="win32"?n.toLowerCase():n;let t;try{t=e(w(o))}catch{return()=>!1}return n=>{try{return e(w(n))===t}catch{return!1}}}async function O(o){const e=await P(o);if(e.length){for(const t of e)try{process.kill(t,"SIGTERM")}catch{}await y(300);for(const t of e)try{process.kill(t,0),process.kill(t,"SIGKILL")}catch{}}}export{P as browserPids,O as killByProfileDir};
@@ -0,0 +1,3 @@
1
+ import type { Page } from "playwright-core";
2
+ export declare function overlaySensitive(kind: string): boolean;
3
+ export declare function detectInterference(page: Page): Promise<string | null>;
@@ -0,0 +1 @@
1
+ const n=new Set(["click","set_text","keys","scroll","hover","focus","wait","paste","select_option","select_text","drag","upload","dialog"]);function s(e){return n.has(e)}const r=("((()=>{if(!document.hasFocus())return\"document focus was lost (a native panel or another window holds it)\";const a=Math.max(1,Math.floor(window.innerWidth/2)),s=Math.max(1,Math.floor(window.innerHeight/2));let t=document.elementFromPoint(a,s);for(let n=0;n<8&&t;n++){const e=getComputedStyle(t);if(e.position===\"fixed\"&&e.pointerEvents!==\"none\"&&e.visibility!==\"hidden\"&&e.display!==\"none\"){const i=e.zIndex===\"auto\"?0:parseInt(e.zIndex,10);if(i>=2147483640){const o=t.getBoundingClientRect(),r=o.width*o.height/(window.innerWidth*window.innerHeight);if(r>.1){const c=t.getAttribute&&(t.getAttribute(\"aria-label\")||t.getAttribute(\"id\")||t.getAttribute(\"class\"))||t.tagName.toLowerCase();return'a fixed overlay (\"'+String(c).slice(0,40)+'\") at z='+i+\" covers \"+Math.round(r*100)+\"% of the viewport over the center\"}}}t=t.parentElement}return null})())");async function a(e){try{const t=await Promise.race([e.evaluate(r),new Promise(o=>{setTimeout(()=>o(null),1500).unref?.()})]);return typeof t=="string"&&t?t:null}catch{return null}}export{a as detectInterference,s as overlaySensitive};
@@ -0,0 +1,2 @@
1
+ export declare const SAVE_PICKER_REGISTRY = "mirasim.savePicker";
2
+ export declare const SAVE_PICKER_JS: string;
@@ -0,0 +1 @@
1
+ const e="mirasim.savePicker",t=("((()=>{const KEY=Symbol.for(("+(JSON.stringify(e))+"));if(window[KEY]||typeof window.showSaveFilePicker!==\"function\")return;const urls=new Set;Object.defineProperty(window,KEY,{value:urls});const isData=c=>typeof c===\"string\"||c instanceof Blob||c instanceof ArrayBuffer||ArrayBuffer.isView(c);const withExtension=(name,types)=>{if(/\\.[^.]+$/.test(name))return name;const accept=types&&types[0]&&types[0].accept;const ext=accept&&[].concat(Object.values(accept)[0]||[])[0];return typeof ext===\"string\"&&ext.startsWith(\".\")?name+ext:name};const download=(blob,name)=>{const url=URL.createObjectURL(blob);urls.add(url);const a=document.createElement(\"a\");a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),6e4)};const handleFor=name=>{let file=new File([],name);const handle={kind:\"file\",name,getFile:async()=>file,isSameEntry:async other=>other===handle,queryPermission:async()=>\"granted\",requestPermission:async()=>\"granted\",createWritable:async(options={})=>{let content=options.keepExistingData?file:new Blob([]);let cursor=0;const put=(data,at)=>{const piece=new Blob([data]);const gap=Math.max(0,at-content.size);content=new Blob([content.slice(0,at),new Uint8Array(gap),piece,content.slice(at+piece.size)]);cursor=at+piece.size};const apply=chunk=>{if(isData(chunk))return put(chunk,cursor);if(!chunk||typeof chunk!==\"object\")throw new TypeError(\"Invalid write data\");if(chunk.type===\"write\")return put(chunk.data,chunk.position??cursor);if(chunk.type===\"seek\"){cursor=chunk.position;return}if(chunk.type===\"truncate\"){content=chunk.size<=content.size?content.slice(0,chunk.size):new Blob([content,new Uint8Array(chunk.size-content.size)]);cursor=Math.min(cursor,chunk.size);return}throw new TypeError(\"Invalid write data\")};const stream=new WritableStream({write:apply,close:()=>{file=new File([content],name);download(content,name)}});const send=async chunk=>{const writer=stream.getWriter();try{await writer.write(chunk)}finally{writer.releaseLock()}};stream.write=send;stream.seek=position=>send({type:\"seek\",position});stream.truncate=size=>send({type:\"truncate\",size});return stream}};return handle};window.showSaveFilePicker=async function showSaveFilePicker(options={}){if(navigator.userActivation&&!navigator.userActivation.isActive){throw new DOMException(\"Must be handling a user gesture to show a file picker.\",\"SecurityError\")}return handleFor(withExtension(String(options.suggestedName||\"download\"),options.types))}})())");export{t as SAVE_PICKER_JS,e as SAVE_PICKER_REGISTRY};
@@ -0,0 +1,11 @@
1
+ import { type BrowserConfig } from "./config.ts";
2
+ export declare function endpointFileFor(profileDir: string, cfg?: BrowserConfig): string;
3
+ export declare function actionsFileFor(profileDir: string, cfg?: BrowserConfig): string;
4
+ export declare function shotsDirFor(profileDir: string, cfg?: BrowserConfig): string;
5
+ export declare function publishEndpoint(profileDir: string, port: number, knownPid: number | null, cfg: BrowserConfig): Promise<void>;
6
+ export declare function publishTabIdentity(profileDir: string, tab: {
7
+ key: string;
8
+ isolated: boolean;
9
+ ctxId: string;
10
+ targetId: string;
11
+ }): void;
@@ -0,0 +1 @@
1
+ import{mkdirSync as a,readFileSync as m,writeFileSync as l}from"node:fs";import{basename as f,dirname as i,join as s}from"node:path";import{browserConfigFromEnv as c}from"./config.js";import{browserPids as d}from"./occupancy.js";import{computerUseHome as y}from"../home.js";function o(t,n=c()){const r=n.runSlug?.replace(/[^\w.-]+/g,"-")||f(t).replace(/[^\w.-]+/g,"-")||"default";return s(y(),"run",r,"endpoint.json")}function b(t,n=c()){return s(i(o(t,n)),"actions.jsonl")}function x(t,n=c()){return s(i(o(t,n)),"shots")}async function O(t,n,r,e){try{const u=o(t,e);a(i(u),{recursive:!0});const p=r??(process.platform==="win32"?null:(await d(t))[0]??null);l(u,JSON.stringify({cdpUrl:`http://127.0.0.1:${n}`,port:n,pid:p,profileDir:t,updatedAt:new Date().toISOString()},null,2))}catch{}}function j(t,n){try{const r=o(t),e=JSON.parse(m(r,"utf8"));l(r,JSON.stringify({...e,tab:n},null,2))}catch{}}export{b as actionsFileFor,o as endpointFileFor,O as publishEndpoint,j as publishTabIdentity,x as shotsDirFor};
@@ -0,0 +1,25 @@
1
+ import type { Browser } from "playwright-core";
2
+ interface StorageStateCookie {
3
+ name: string;
4
+ value: string;
5
+ domain: string;
6
+ path: string;
7
+ expires: number;
8
+ httpOnly: boolean;
9
+ secure: boolean;
10
+ sameSite: "Strict" | "Lax" | "None";
11
+ }
12
+ interface StorageStateOrigin {
13
+ origin: string;
14
+ localStorage: Array<{
15
+ name: string;
16
+ value: string;
17
+ }>;
18
+ }
19
+ export interface BrowserStorageState {
20
+ cookies: StorageStateCookie[];
21
+ origins: StorageStateOrigin[];
22
+ }
23
+ export declare function exportStorageState(browser: Browser): Promise<BrowserStorageState>;
24
+ export declare function importStorageState(browser: Browser, state: BrowserStorageState): Promise<void>;
25
+ export {};
@@ -0,0 +1,2 @@
1
+ import{newBackgroundPage as m}from"./tabs.js";function p(o){try{const t=new URL(o);return t.protocol==="http:"||t.protocol==="https:"?t.origin:null}catch{return null}}function f(o){const t=o.contexts()[0];if(!t)throw new Error("mirasim-cu: the attached browser exposes no context to read state from.");return t}async function S(o){const t=f(o),i=await t.cookies(),e=new Map;for(const r of t.pages()){if(r.isClosed())continue;const a=p(r.url());a&&!e.has(a)&&e.set(a,r)}const n=[];for(const[r,a]of e)try{const c=await a.evaluate(()=>{const s=[];for(let l=0;l<localStorage.length;l++){const u=localStorage.key(l);u!==null&&s.push({name:u,value:localStorage.getItem(u)??""})}return s});c.length&&n.push({origin:r,localStorage:c})}catch{}return{cookies:i,origins:n}}async function d(o,t){const i=f(o);if(t.cookies?.length)try{await i.addCookies(t.cookies)}catch(e){throw new Error(`mirasim-cu: cookie import failed: ${g(e)}`)}for(const e of t.origins??[])!e?.origin||!e.localStorage?.length||await h(i,e.origin,e.localStorage)}async function h(o,t,i){const e=new Set(o.pages()),n=await m(o),r=!e.has(n);try{await n.route("**/*",a=>a.fulfill({status:200,contentType:"text/html",body:"<!doctype html><title>mirasim-cu state import</title>"})),await n.goto(`${t}/`,{waitUntil:"domcontentloaded",timeout:1e4}),await n.evaluate(a=>{for(const{name:c,value:s}of a)localStorage.setItem(c,s)},i)}catch(a){throw new Error(`mirasim-cu: localStorage import failed for ${t}: ${g(a)}`)}finally{await n.unroute("**/*").catch(()=>{}),r?await n.close().catch(()=>{}):await n.goto("about:blank").catch(()=>{})}}function g(o){const t=o instanceof Error?o.message:String(o);return t.split(`
2
+ `)[0]??t}export{S as exportStorageState,d as importStorageState};
@@ -0,0 +1,15 @@
1
+ import type { Browser, BrowserContext, Page } from "playwright-core";
2
+ import { type BrowserConfig } from "./config.ts";
3
+ export declare function newBackgroundPage(ctx: BrowserContext): Promise<Page>;
4
+ export declare function claimNamedTab(ctx: BrowserContext, key: string): Promise<Page>;
5
+ export declare function claimIsolatedTab(browser: Browser, key: string): Promise<{
6
+ page: Page;
7
+ ctxId: string;
8
+ }>;
9
+ export declare function pageTargetInfo(page: Page): Promise<{
10
+ targetId: string;
11
+ ctxId: string;
12
+ }>;
13
+ export declare function pageContextId(page: Page): Promise<string>;
14
+ export declare function claimSessionTab(ctx: BrowserContext, namedKey: string | null, cfg?: BrowserConfig): Promise<Page | null>;
15
+ export declare function ensureBackgroundTab(browser: Browser): Promise<void>;
@@ -0,0 +1,2 @@
1
+ import{browserConfigFromEnv as p}from"./config.js";const u=t=>new Promise(e=>setTimeout(e,t));async function w(t){const e=(await h(t))[0];if(!e)return await t.newPage();const a=new Set(t.pages()),n=await t.newCDPSession(e);let r;try{({targetId:r}=await n.send("Target.createTarget",{url:"about:blank",background:!0,newWindow:!0}))}finally{await n.detach().catch(()=>{})}return await b(t,r,a)??e}async function b(t,e,a){for(let n=0;n<40;n++){for(const r of t.pages())if(!(a.has(r)||r.isClosed())&&(await f(r).catch(()=>null))?.targetId===e)return r;await u(25)}return null}const l="__mirasim_computer_use_tab:";async function d(t,e){return await m(t)===l+e}async function m(t,e=1e3){let a;const n=new Promise(r=>{a=setTimeout(()=>r(""),e),a.unref?.()});try{return String(await Promise.race([t.evaluate("window.name"),n]).catch(()=>""))}finally{clearTimeout(a)}}async function y(t,e){for(const n of t.pages())if(!n.isClosed()&&await d(n,e))return n;const a=await w(t);return await a.evaluate(`window.name = ${JSON.stringify(l+e)}`).catch(()=>{}),a}async function C(t,e){const a=()=>t.contexts().flatMap(o=>o.pages()).filter(o=>!o.isClosed());for(const o of a())if(await d(o,e))return{page:o,ctxId:await g(o)};const n=new Set(a()),r=await t.newBrowserCDPSession();let s,i;try{({browserContextId:s}=await r.send("Target.createBrowserContext")),{targetId:i}=await r.send("Target.createTarget",{url:"about:blank",background:!0,browserContextId:s})}catch(o){throw new Error(`this endpoint cannot create isolated browser contexts (${o instanceof Error?o.message.split(`
2
+ `)[0]:String(o)}), so the tab claim "ctx:${e}" is unavailable here \u2014 use a named tab ("name:${e}") for a tab of your own in the same browser, which is the same isolation everywhere except cookies and storage`)}finally{await r.detach().catch(()=>{})}for(let o=0;o<40;o++){for(const c of a())if(!n.has(c)&&(await f(c).catch(()=>null))?.targetId===i)return await c.evaluate(`window.name = ${JSON.stringify(l+e)}`).catch(()=>{}),{page:c,ctxId:s};await u(25)}throw new Error("isolated-context tab was created but never surfaced over CDP")}async function f(t){const e=await t.context().newCDPSession(t);try{const{targetInfo:a}=await e.send("Target.getTargetInfo");return{targetId:a.targetId,ctxId:a.browserContextId??""}}finally{await e.detach().catch(()=>{})}}async function g(t){return(await f(t)).ctxId}async function I(t,e,a=p()){if(e)return y(t,e);const n=await h(t);return a.cdpUrl?t.browser()?.version()==="mirasim-bridge"?null:await w(t):(a.tabReuse?n.filter(s=>s.url()!=="about:blank").at(-1):void 0)??n.find(s=>s.url()==="about:blank")??await w(t)}async function h(t){const e=t.pages().filter(i=>!i.isClosed()),a=t.browser();if(!a||e.length===0)return e;let n;const r=await a.newBrowserCDPSession();try{n=new Set((await r.send("Target.getBrowserContexts")).browserContextIds)}catch{return e}finally{await r.detach().catch(()=>{})}if(n.size===0)return e;const s=[];for(const i of e)n.has(await g(i).catch(()=>""))||s.push(i);return s}async function x(t){try{const e=t.contexts()[0]??await t.newContext();if(e.pages().some(n=>!n.isClosed()))return;const a=await t.newBrowserCDPSession();try{await a.send("Target.createTarget",{url:"about:blank",background:!0})}finally{await a.detach().catch(()=>{})}for(let n=0;n<40;n++){if(e.pages().some(r=>!r.isClosed()))return;await u(25)}}catch{}}export{C as claimIsolatedTab,y as claimNamedTab,I as claimSessionTab,x as ensureBackgroundTab,w as newBackgroundPage,g as pageContextId,f as pageTargetInfo};
@@ -0,0 +1,8 @@
1
+ import type { Browser, BrowserContext, Page } from "playwright-core";
2
+ import { type BrowserConfig } from "./config.ts";
3
+ export declare const OFFSCREEN_POSITION = "-4000,200";
4
+ export declare function userWantsVisible(profileDir: string): boolean;
5
+ export declare function revealFlagFor(profileDir: string): string;
6
+ export declare function parkWindowOffscreen(browser: Browser): Promise<void>;
7
+ export declare function unparkWindow(browser: Browser): Promise<void>;
8
+ export declare function keepInBackground(ctx: BrowserContext, page: Page, profileDir: string, cfg?: BrowserConfig): Promise<void>;
@@ -0,0 +1 @@
1
+ import{existsSync as c}from"node:fs";import{basename as w,join as d}from"node:path";import{browserConfigFromEnv as f}from"./config.js";import{computerUseHome as u}from"../home.js";const v="-4000,200",l={left:80,top:80},h=-1e3;function p(e){return c(g(e))}function g(e){const t=w(e).replace(/[^\w.-]+/g,"-")||"default";return d(u(),"run",t,"revealed")}async function C(e){try{const t=e.contexts()[0]??await e.newContext(),s=t.pages()[0]??await t.newPage(),i=await t.newCDPSession(s),{windowId:n}=await i.send("Browser.getWindowForTarget");await i.send("Browser.setWindowBounds",{windowId:n,bounds:{left:-4e3,top:200}}),await i.detach()}catch{}}function m(e,t){return e.left<t.left+t.width&&t.left<e.left+e.width&&e.top<t.top+t.height&&t.top<e.top+e.height}const y=("({left:screen.availLeft||0,top:screen.availTop||0,width:screen.availWidth,height:screen.availHeight})");async function S(e){try{const t=await e.evaluate(y);return t&&Number.isFinite(t.width)&&Number.isFinite(t.height)?t:null}catch{return null}}async function I(e){try{const t=e.contexts()[0],s=t?.pages().find(n=>!n.isClosed());if(!s)return;const i=await t.newCDPSession(s);try{const{windowId:n,bounds:o}=await i.send("Browser.getWindowForTarget"),r=await S(s);if(r&&o.width&&o.height){const a={left:o.left??0,top:o.top??0,width:o.width,height:o.height};if(m(a,r))return}else if((o.left??0)>h)return;await i.send("Browser.setWindowBounds",{windowId:n,bounds:l})}finally{await i.detach().catch(()=>{})}}catch{}}async function P(e,t,s,i=f()){if(i.window==="offscreen"&&!p(s))try{const n=await e.newCDPSession(t);try{const{windowId:o,bounds:r}=await n.send("Browser.getWindowForTarget");r.windowState==="minimized"&&await n.send("Browser.setWindowBounds",{windowId:o,bounds:{windowState:"normal"}}),(r.left??0)>-200&&await n.send("Browser.setWindowBounds",{windowId:o,bounds:{left:-4e3,top:200}})}finally{await n.detach().catch(()=>{})}}catch{}}export{v as OFFSCREEN_POSITION,P as keepInBackground,C as parkWindowOffscreen,g as revealFlagFor,I as unparkWindow,p as userWantsVisible};
@@ -0,0 +1,11 @@
1
+ import type { Locator, Page } from "playwright-core";
2
+ import type { ActResult, VerbSpec } from "../types.ts";
3
+ export declare function setTextWithFallbacks(page: Page, loc: Locator, a: Record<string, any>, relocate: () => Promise<Locator | null>): Promise<ActResult & {
4
+ verified?: boolean;
5
+ }>;
6
+ export declare const SELECT_OPTION_VERB: VerbSpec;
7
+ export declare const PASTE_VERB: VerbSpec;
8
+ export declare function performPaste(page: Page, a: Record<string, any>, locate: () => Promise<Locator | null>): Promise<ActResult & {
9
+ verified?: boolean;
10
+ }>;
11
+ export declare function performSelectOption(page: Page, a: Record<string, any>, locate: () => Promise<Locator | null>): Promise<ActResult>;
@@ -0,0 +1,5 @@
1
+ import{Type as a}from"typebox";import{pageFn as g}from"./collect.js";import{refuseIfGated as w}from"./actions.js";function f(s){if(/Human input resumed|connection closed while waiting for input|was not started/.test(String(s)))throw s}const p=s=>new Promise(t=>setTimeout(t,s));async function E(s,t,r,o){const n=(e,d)=>b(e,r.text,d);let l="";try{return await t.fill(r.text,{timeout:8e3}),await n(t,null)}catch(e){f(e),l=(e instanceof Error?e.message:String(e)).split(`
2
+ `)[0]??"",l=l.replace(/^locator\.fill:?\s*/i,"").slice(0,140)}const i=l?` (fill rejected: ${l})`:"";if(/is not an? <input>/i.test(l))return{ok:!1,note:`set_text did nothing: this element is not a text field (${l}). Nothing was clicked and nothing was typed. Locate the field itself \u2014 find(name, {role: 'textbox'}) \u2014 and if the handle came from a find() marked incidental, nothing on this surface is named that`};try{await t.click({timeout:4e3}),await p(350);const e=await o();if(e)return await e.fill(r.text,{timeout:4e3}),await n(e,"filled after click-to-activate")}catch(e){f(e)}try{return await s.keyboard.type(r.text,{delay:20}),await n(t,`typed via keyboard fallback${i}`)}catch(e){return{ok:!1,note:`set_text failed${i}: ${(e instanceof Error?e.message:String(e)).split(`
3
+ `)[0]}`}}}async function b(s,t,r){let o=null;try{o=await s.evaluate(e=>typeof e.value=="string"?e.value:e.isContentEditable?e.innerText??"":null,void 0,{timeout:2e3})}catch{}if(o===null)return r?{ok:!0,note:r}:{ok:!0};const n=e=>e.length>60?`${e.slice(0,60)}\u2026`:e;if(o===t){const e=`the field now contains \xAB${n(o)}\xBB`;return{ok:!0,note:r?`${r}; ${e}`:e,verified:!0}}const i=`WARNING: the field ${o?`now shows \xAB${n(o)}\xBB`:"is still empty"}, not the text you sent \u2014 it did not take; try a different format or element`;return{ok:!0,note:r?`${r}; ${i}`:i,verified:!1}}const S={kind:"select_option",schema:a.Object({kind:a.Literal("select_option"),ref:a.String({description:"Element ref from the latest observe() (e.g. e12)"}),name:a.Optional(a.String({description:"Visible name of the element \u2014 identity check + fallback locator"})),option:a.String({description:"Option label or value. Native <select> must use this, not click/fill."})})},T={kind:"paste",schema:a.Object({kind:a.Literal("paste"),text:a.String({description:"The content as plain text (Markdown goes here too \u2014 editors that understand it read this)"}),html:a.Optional(a.String({description:"The same content as HTML, for editors that accept rich text; they take this and everything else falls back to text"})),ref:a.Optional(a.String({description:"Paste into this element. Omit to paste at the page's current caret \u2014 where select_text left it."})),name:a.Optional(a.String({description:"Visible name of that element \u2014 identity check + fallback locator"}))},{description:"Paste content at the caret, over any selection \u2014 the way \u2318V does, including rich text via html. Does NOT touch the user's real clipboard."})},k=("((o=>{const u=o[0],t=o[1],e=u||document.activeElement;if(!e||e===document.body&&!t.intoBody)return{noTarget:!0};typeof e.focus==\"function\"&&e.focus();function r(n){return typeof n.value==\"string\"?n.value:n.isContentEditable?n.innerText||\"\":null}const a=r(e),l=new DataTransfer;if(l.setData(\"text/plain\",t.text),t.html&&l.setData(\"text/html\",t.html),!e.dispatchEvent(new ClipboardEvent(\"paste\",{clipboardData:l,bubbles:!0,cancelable:!0})))return{handled:\"the editor's own paste handler\",after:r(e),before:a};if(typeof e.value==\"string\"&&typeof e.setSelectionRange==\"function\"){const n=e.selectionStart==null?e.value.length:e.selectionStart,f=e.selectionEnd==null?n:e.selectionEnd,i=e.value.slice(0,n)+t.text+e.value.slice(f),s=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(e),\"value\");s&&s.set?s.set.call(e,i):e.value=i;const c=n+t.text.length;return e.setSelectionRange(c,c),e.dispatchEvent(new Event(\"input\",{bubbles:!0})),{handled:\"inserted at the caret\",after:r(e),before:a,plainOnly:!!t.html}}return e.isContentEditable?document.execCommand(t.html?\"insertHTML\":\"insertText\",!1,t.html||t.text)?{handled:t.html?\"inserted as HTML\":\"inserted at the caret\",after:r(e),before:a}:{refused:!0,editable:!0}:{refused:!0,editable:!1}}))");async function O(s,t,r){const o=String(t.text??"");if(!o&&!t.html)return{ok:!1,note:"paste needs text (and optionally html)"};let n=null;if(t.ref){const c=await r();if(!c)return{ok:!1,note:`no element for ref ${t.ref}`};const h=await w(c,t.ref,t.name);if(h)return h;if(n=await c.elementHandle().catch(()=>null),!n)return{ok:!1,note:`${t.ref} is no longer in the page \u2014 re-observe`}}const l={text:o,html:typeof t.html=="string"?t.html:""},i=await s.evaluate(g(k),[n,l]).catch(c=>({error:c instanceof Error?c.message:String(c)}));if(i.error)return{ok:!1,note:`paste failed: ${String(i.error).split(`
4
+ `)[0]}`};if(i.noTarget)return{ok:!1,note:"nothing is focused to paste into \u2014 pass ref for the field or editor, or click into it first"};if(i.refused)return{ok:!1,note:i.editable?"the editor refused the insert \u2014 click into it, then paste again":"this element is not editable, so there is nowhere for the content to land \u2014 pass ref for a field or an editor"};const e=i.after??null,d=e===null?void 0:e!==(i.before??null)&&e.includes(o.slice(0,40)),u=String(i.handled??"pasted"),m=i.plainOnly?" (this field takes plain text only, so the html was not used)":"";return d===!1?{ok:!0,verified:!1,note:`WARNING: ${u}, but the content is not in the field afterwards \u2014 the page may have rejected or reformatted it; read it back before relying on it`}:{ok:!0,...d===!0?{verified:!0}:{},note:`${u}${m}. The user's own clipboard was not touched \u2014 if the page pastes from its own button instead, this content will not be there`}}async function $(s,t,r){const o=await r();if(!o)return{ok:!1,note:`no select for ref ${t.ref}`};try{return await o.selectOption({label:t.option},{timeout:8e3}),{ok:!0}}catch(n){f(n)}try{return await o.selectOption(t.option,{timeout:8e3}),{ok:!0}}catch(n){f(n)}try{return await o.click({timeout:4e3}),await p(400),await s.getByRole("option",{name:t.option,exact:!1}).first().click({timeout:4e3}),{ok:!0,note:"selected via open-and-click (custom dropdown)"}}catch(n){return{ok:!1,note:`could not select "${t.option}": not by label or value, and opening the control revealed no option with that name \u2014 ${(n instanceof Error?n.message:String(n)).split(`
5
+ `)[0]}`}}}export{T as PASTE_VERB,S as SELECT_OPTION_VERB,O as performPaste,$ as performSelectOption,E as setTextWithFallbacks};
@@ -0,0 +1,20 @@
1
+ import type { ElementHandle, Locator, Page } from "playwright-core";
2
+ import type { ActResult, VerbSpec } from "../types.ts";
3
+ export declare function refuseIfGated(loc: Locator, ref?: string, name?: string): Promise<ActResult | null>;
4
+ export declare function clickNamingCover(target: Pick<Locator, "click"> | Pick<ElementHandle, "click">, loc: Locator, opts: {
5
+ button: "left" | "right" | "middle";
6
+ clickCount: number;
7
+ }, ref?: string, name?: string): Promise<ActResult>;
8
+ export declare const DIALOG_VERB: VerbSpec;
9
+ export declare function refuseMissingFiles(paths: string[]): ActResult | null;
10
+ export declare function fileInputFor(loc: Locator): Promise<Locator | null>;
11
+ export declare function performUploadToRef(paths: string[], ref: string, locate: () => Promise<Locator | null>, missing: () => Promise<ActResult>): Promise<ActResult>;
12
+ export declare const UPLOAD_VERB: VerbSpec;
13
+ export declare const DRAG_VERB: VerbSpec;
14
+ export declare function performDrag(page: Page, a: Record<string, any>, locate: (ref: string, name: string | undefined) => Promise<Locator | null>): Promise<{
15
+ res: ActResult;
16
+ point: {
17
+ x: number;
18
+ y: number;
19
+ } | null;
20
+ }>;
@@ -0,0 +1,2 @@
1
+ import{existsSync as $}from"node:fs";import{Type as e}from"typebox";import{coverOf as v,isEditorProxy as S}from"./coverer-label.js";async function T(n,t,s){let r;try{r=await n.evaluate(i=>{const l=i.matches&&i.matches(":disabled")?"natively disabled":i.getAttribute&&i.getAttribute("aria-disabled")==="true"?"aria-disabled":null,h=i.closest&&i.closest("form")||i.ownerDocument,y=!!(h&&h.querySelector&&h.querySelector("input:invalid, select:invalid, textarea:invalid"));return{why:l,unmet:y}},void 0,{timeout:2e3})}catch{return null}const{why:a,unmet:c}=r;if(!a)return null;const u=`${t??"the target"}${s?` "${s}"`:""}`;return{ok:!1,journal:{refused:a,gate:c?"fields":"state"},note:`${u} is ${a} \u2014 ${a==="aria-disabled"?"the page only LABELS it disabled, so a click by x/y from a screenshot may still reach it":"the browser delivers no input to it"}. The page is gating this control, so acting on a different element will not unlock it: ${c?"fill what it is waiting on (the [empty] / [invalid] fields in the list), then re-observe":"no unfilled field is waiting here, so the gate is a STATE, not a form: some step has not happened yet (nothing selected, an empty history, a mode not entered). Do that step, then re-observe"} \u2014 the [disabled] mark disappears once it becomes usable`}}const w=2e3,k=8e3;async function D(n,t,s,r,a){try{return await n.click({...s,timeout:w}),{ok:!0}}catch(u){if(!R(u))throw u}if(await S(t))return await n.click({...s,force:!0,timeout:k-w}),{ok:!0};const c=await b(t,r,a);if(c)return{ok:!1,journal:{covered:!0},note:c};try{return await n.click({...s,timeout:k-w}),{ok:!0}}catch(u){const d=await b(t,r,a);if(!d)throw u;return{ok:!1,journal:{covered:!0},note:d}}}function R(n){const t=n;return t?.name==="TimeoutError"&&!/performing \w+ action/.test(String(t.message??""))}async function b(n,t,s){const r=await v(n);if(!r)return null;const a=`${t??"the target"}${s?` "${s}"`:""}`;return r.pseudo?`${a} is covered by the ${r.pseudo} layer of \xAB${r.who}\xBB, which takes the click instead \u2014 that is the page's own styling, not an element: it is in no element list and there is nothing to close, so a user's mouse click does not reach this control either. The keyboard still does (Tab to it, then Enter or Space)`:`${a} is covered by \xAB${r.who}\xBB, which took the click instead \u2014 the element is the right one, so acting on a different one will not help: close or dismiss what is on top (find('close') reaches its close control when the page marks one; an unmarked one shows only in a screenshot), or `+(t?`scroll({into:'${t}'}) to bring it clear`:"scroll until this control sits clear of it")+", then act on this ref again"}const I={kind:"dialog",schema:e.Object({kind:e.Literal("dialog"),answer:e.Union([e.Literal("accept"),e.Literal("dismiss")],{description:'"accept" or "dismiss" for the next JS dialog. Default without this verb: alert accepted, confirm/prompt dismissed.'}),text:e.Optional(e.String({description:"Text to type into a prompt() before accepting"}))})};function A(n){const t=n.filter(s=>!$(s));return t.length?{ok:!1,note:`no such file: ${t.join(", ")} \u2014 give an absolute path to a file that exists on this machine`}:null}async function L(n){if(await n.evaluate(r=>r.tagName==="INPUT"&&r.type==="file").catch(()=>!1))return n;const s=n.locator('xpath=ancestor-or-self::*[.//input[@type="file"]][1]//input[@type="file"]');return await s.count().catch(()=>0)===1?s:null}async function N(n,t,s,r){const a=await s();if(!a)return await r();try{await a.setInputFiles(n,{timeout:8e3})}catch(c){const u=c instanceof Error?c.message.split(`
2
+ `)[0]??"":String(c);return{ok:!1,note:`${t} did not take the files (${u}) \u2014 pass ref only for an element the list shows as \`file\``}}return{ok:!0,note:`set ${n.length} file${n.length===1?"":"s"} on ${t}`}}const _={kind:"upload",schema:e.Object({kind:e.Literal("upload"),paths:e.Array(e.String(),{description:"Absolute path(s) of the file(s) to upload. They must already exist on this machine.",minItems:1}),ref:e.Optional(e.String({description:"A visible file input to fill directly. Omit to answer the next file chooser a click opens."}))})},j={kind:"drag",schema:e.Object({kind:e.Literal("drag"),ref:e.Optional(e.String({description:"Element to drag \u2014 REQUIRED unless x/y are given (this pair is drag-only; click/hover still require ref or x/y)"})),name:e.Optional(e.String({description:"The ref's expected name \u2014 an identity check on ref, never a start of its own; a non-control (a list row) has no ref, so take its x/y off a screenshot"})),x:e.Optional(e.Number({description:"Drag start x (instead of ref)"})),y:e.Optional(e.Number({description:"Drag start y"})),toRef:e.Optional(e.String({description:"Element to drop onto \u2014 REQUIRED unless toX/toY are given"})),toX:e.Optional(e.Number({description:"Destination x (instead of toRef)"})),toY:e.Optional(e.Number({description:"Destination y"}))},{description:"Press, drag, release: move sliders, resize handles, reorder/sort items, drag into bins, or select text by dragging across it. Needs a start (ref or x/y) AND a destination (toRef or toX/toY)."})};async function B(n,t,s){const r=async(o,p,g)=>{if(typeof o!="string")return null;const f=await s(o,p);return f?(await f.scrollIntoViewIfNeeded({timeout:4e3}).catch(()=>{}),f):{ok:!1,note:`no element for ${g} ${o}`}},a=async(o,p,g)=>{const f=await o.boundingBox().catch(()=>null);return f?{x:f.x+f.width/2,y:f.y+f.height/2}:{ok:!1,note:`no element for ${p} ${String(g)}`}},c=typeof t.x=="number"&&typeof t.y=="number",u=typeof t.toX=="number"&&typeof t.toY=="number";if(!c&&typeof t.ref!="string")return{res:{ok:!1,note:`drag needs a start: ref or x/y${typeof t.name=="string"?" \u2014 name only checks a ref, it does not locate one; find() it for a ref, or read x/y off a screenshot for a non-control":""}`},point:null};if(!u&&typeof t.toRef!="string")return{res:{ok:!1,note:"drag needs a destination: toRef or toX/toY"},point:null};const d=c?null:await r(t.ref,t.name,"ref");if(d&&"ok"in d)return{res:d,point:null};const m=u?null:await r(t.toRef,void 0,"toRef");if(m&&"ok"in m)return{res:m,point:null};let i;if(c)i={x:t.x,y:t.y};else{const o=await a(d,"ref",t.ref);if("ok"in o)return{res:o,point:null};i=o}let l;if(u)l={x:t.toX,y:t.toY};else{const o=await a(m,"toRef",t.toRef);if("ok"in o)return{res:o,point:null};l=o}const h=await n.evaluate("({ w: innerWidth, h: innerHeight })").catch(()=>null),y=o=>!!h&&(o.x<0||o.y<0||o.x>h.w||o.y>h.h);if(y(i)||y(l))return{res:{ok:!1,note:`drag needs both ends on screen at once, and (${Math.round(i.x)},${Math.round(i.y)}) \u2192 (${Math.round(l.x)},${Math.round(l.y)}) is not \u2014 scroll until both are visible, then drag`},point:null};await n.mouse.move(i.x,i.y),await n.mouse.down(),await n.mouse.move(l.x,l.y,{steps:12}),await n.mouse.up();let x="";if(d){const o=await a(d,"ref",t.ref).catch(()=>null);if(o&&!("ok"in o)){const p=Math.round(Math.hypot(o.x-l.x,o.y-l.y));x=Math.round(Math.hypot(o.x-i.x,o.y-i.y))<2?` \u2014 ${String(t.ref)} did not move`:p<2?` \u2014 ${String(t.ref)} followed to the release point`:` \u2014 ${String(t.ref)} now sits at (${Math.round(o.x)},${Math.round(o.y)}), ${p}px short of the release point`}}return{res:{ok:!0,note:`dragged (${Math.round(i.x)},${Math.round(i.y)}) \u2192 (${Math.round(l.x)},${Math.round(l.y)})${x}`,journal:{fromX:Math.round(i.x),fromY:Math.round(i.y)}},point:l}}export{I as DIALOG_VERB,j as DRAG_VERB,_ as UPLOAD_VERB,D as clickNamingCover,L as fileInputFor,B as performDrag,N as performUploadToRef,T as refuseIfGated,A as refuseMissingFiles};
@@ -0,0 +1 @@
1
+ export declare const CHECKABLE_TARGET_JS: string;
@@ -0,0 +1 @@
1
+ const e=("((i=>{if(!i||i.tagName!==\"INPUT\"||![\"checkbox\",\"radio\"].includes(i.type)||i.closest('[hidden], [inert], [aria-hidden=\"true\"]'))return null;const s=n=>{if(n.closest('[hidden], [inert], [aria-hidden=\"true\"]')||n.checkVisibility&&!n.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}))return!1;const l=n.getBoundingClientRect(),c=getComputedStyle(n);if(l.width<=1||l.height<=1||c.display===\"none\"||c.visibility!==\"visible\")return!1;for(let u=n;u;u=u.parentElement)if(Number(getComputedStyle(u).opacity)===0)return!1;return!0},e=getComputedStyle(i),t=i.getBoundingClientRect();if(s(i)&&e.clipPath===\"none\"&&e.clip===\"auto\")return null;const a=Array.from(i.labels||[]).filter(s);if(a.length){const n=a[0],l=n.getBoundingClientRect();return t.width>1&&t.height>1&&t.left<l.right&&t.right>l.left&&t.top<l.bottom&&t.bottom>l.top&&e.display!==\"none\"&&e.visibility===\"visible\"&&e.clipPath===\"none\"&&e.clip===\"auto\"?i:n}const r=i.nextElementSibling;if(!r||r.tagName!==\"LABEL\"||r.control||!s(r)||e.display===\"none\"||e.visibility!==\"visible\"||e.clipPath!==\"none\"||e.clip!==\"auto\"||t.width<=1||t.height<=1)return null;const o=r.getBoundingClientRect();return t.left<o.right&&t.right>o.left&&t.top<o.bottom&&t.bottom>o.top?i:null}))");export{e as CHECKABLE_TARGET_JS};
@@ -0,0 +1 @@
1
+ export declare const VIEWPORT_JS: string;
@@ -0,0 +1 @@
1
+ import{BROWSER_ROLES as e,GROUP_CAP as t,GROUP_UP as n,NAME_JS as o,ROW_CAP as i,ROW_UP as s,STATE_CLASS_JS as r}from"./collect.js";import{CHECKABLE_TARGET_JS as l}from"./checkable-target.js";import{SCROLL_REGION_JS as a}from"./scroll-regions.js";import{COVERER_LABEL_JS as c,EDITOR_PROXY_JS as d}from"./coverer-label.js";import{HEADINGS_JS as h}from"./headings.js";import{PAGE_REFS_JS as u}from"./page-refs.js";const y=("((startK=>{const ROLES=new Set((__ROLES__));const refs=(__PAGE_REFS__)();for(const[id,entry]of refs.elements)if(!entry.deref()?.isConnected)refs.elements.delete(id);const editableRoot=el=>el.isContentEditable&&!(el.parentElement&&el.parentElement.isContentEditable);const roleOf=el=>el.getAttribute(\"role\")||(editableRoot(el)?\"textbox\":\"\")||({A:\"link\",BUTTON:\"button\",SELECT:\"combobox\",TEXTAREA:\"textbox\",SUMMARY:el.parentElement&&el.parentElement.tagName===\"DETAILS\"?\"button\":\"\",INPUT:el.type===\"checkbox\"?\"checkbox\":el.type===\"radio\"?\"radio\":el.type===\"button\"||el.type===\"submit\"?\"button\":el.type===\"range\"?\"slider\":el.type===\"file\"?\"file\":\"textbox\"}[el.tagName]||\"\");const nameOf=(__NAME_OF__);const checkableTarget=(__CHECKABLE_TARGET__);const scrollRegion=(__SCROLL_REGION__);const covererLabel=(__COVERER_LABEL__);const editorProxy=(__EDITOR_PROXY__);const headingsOf=(__HEADINGS__);const NON_TEXT=new Set([\"button\",\"submit\",\"reset\",\"image\",\"file\",\"hidden\",\"range\",\"color\"]);const stateOf=el=>{const cut=s=>{s=(typeof s===\"string\"?s:\"\").trim().replace(/\\s+/g,\" \");return s.length>60?s.slice(0,60)+\"\\u2026\":s};const tag=(el.tagName||\"\").toUpperCase();const base=(()=>{if(el.isContentEditable){const v=cut(el.innerText);return v?\"filled:\"+v:\"empty\"}const ac=el.getAttribute&&el.getAttribute(\"aria-checked\");if(ac===\"true\"||ac===\"false\")return ac===\"true\"?\"checked\":\"unchecked\";if(tag===\"INPUT\"&&(el.type===\"checkbox\"||el.type===\"radio\"))return el.checked?\"checked\":\"unchecked\";if(tag===\"SELECT\"){const o=el.selectedOptions&&el.selectedOptions[0];const t=cut(o&&(o.label||o.textContent));return t?\"selected:\"+t:\"unselected\"}if(tag===\"INPUT\"&&el.type===\"file\"){const names=Array.from(el.files||[]).map(f=>f.name);return names.length?\"file:\"+cut(names.join(\", \")):\"no file\"}if(tag===\"INPUT\"&&el.type===\"range\"){return\"value:\"+el.value+\" (\"+(el.min||\"0\")+\"\\u2013\"+(el.max||\"100\")+\")\"}if(tag===\"TEXTAREA\"||tag===\"INPUT\"&&!NON_TEXT.has(el.type)){const v=el.value==null?\"\":String(el.value);const FMT={date:\"YYYY-MM-DD\",\"datetime-local\":\"YYYY-MM-DDThh:mm\",month:\"YYYY-MM\",week:\"YYYY-Www\",time:\"hh:mm\"};const fmt=FMT[el.type]?\" (\"+el.type+\", needs \"+FMT[el.type]+\")\":\"\";if(!v)return\"empty\"+fmt;const ws=v!==v.trim()?\" (value has edge whitespace \\u2014 exact value via page({ref}))\":\"\";return el.type===\"password\"?\"filled:\"+v.length+\" chars\":\"filled:\"+cut(v)+fmt+ws}return\"\"})();const cls=typeof el.className===\"string\"?el.className:\"\";const inv=el.getAttribute&&el.getAttribute(\"aria-invalid\")===\"true\"||base!==\"\"&&/(^|[\\s_-])(error|invalid)([\\s_-]|$)/i.test(cls);const focused=(el.getRootNode?el.getRootNode():document).activeElement===el;const disabled=el.matches&&el.matches(\":disabled\")||el.getAttribute&&el.getAttribute(\"aria-disabled\")===\"true\"||/(^|[\\s_-])disabled(\\s|$)/i.test(cls);const ax=el.getAttribute&&el.getAttribute(\"aria-expanded\");const det=el.tagName===\"SUMMARY\"&&el.parentElement&&el.parentElement.tagName===\"DETAILS\"?el.parentElement.open?\"expanded\":\"collapsed\":\"\";const exp=ax===\"true\"?\"expanded\":ax===\"false\"?\"collapsed\":det;const cur=el.getAttribute&&el.getAttribute(\"aria-current\");let sel=!el.getAttribute?\"\":el.getAttribute(\"aria-selected\")===\"true\"?\"selected\":el.getAttribute(\"aria-pressed\")===\"true\"?\"pressed\":cur&&cur!==\"false\"?\"current\":\"\";if(!sel&&base===\"\"){const m=("+(r)+")(cls);if(m)sel=m}let out=base;if(exp)out=out?exp+\", \"+out:exp;if(sel)out=out?sel+\", \"+out:sel;if(inv)out=out?\"invalid, \"+out:\"invalid\";if(disabled)out=out?\"disabled, \"+out:\"disabled\";if(focused)out=out?\"focused, \"+out:\"focused\";return out};const visible=el=>{const s=getComputedStyle(el);if(s.visibility===\"hidden\"||s.display===\"none\"||parseFloat(s.opacity||\"1\")<=0)return false;try{if(el.checkVisibility&&!el.checkVisibility({checkOpacity:true}))return false}catch(e){}return true};const ptrInteractive=el=>{try{const name=nameOf(el);if(!name)return false;if(getComputedStyle(el).cursor===\"pointer\")return true;return el.tabIndex>=0&&el.hasAttribute(\"tabindex\")&&name.length<=40&&(el.textContent||\"\").trim().length<=40}catch(e){return false}};let coveredCount=0;const coverers={};let modal=null;try{const all=document.querySelectorAll('[aria-modal=\"true\"], dialog:modal');modal=all[all.length-1]||null}catch(e){}let behindModal=0;const coveredItems=[];const coveredHits=[];const clipCache=new Map;const clipOf=n=>{if(clipCache.has(n))return clipCache.get(n);const s=getComputedStyle(n);const info={box:s.overflowX!==\"visible\"||s.overflowY!==\"visible\"?n.getBoundingClientRect():null,fixed:s.position===\"fixed\"};clipCache.set(n,info);return info};const clipBox=n=>clipOf(n).box;const clipsOf=el=>{const out=[];if(clipOf(el).fixed)return out;for(let n=el.parentElement;n&&n!==document.body&&n!==document.documentElement;n=n.parentElement){const c=clipOf(n);if(c.box)out.push(c.box);if(c.fixed)break}return out};const scrollerOf=el=>{if(clipOf(el).fixed)return null;for(let n=el.parentElement;n&&n!==document.body&&n!==document.documentElement;n=n.parentElement){if(clipBox(n)&&(n.scrollHeight>n.clientHeight+1||n.scrollWidth>n.clientWidth+1))return n;if(clipOf(n).fixed)return null}return null};const stickyWithin=(hit,scroller)=>{for(let n=hit;n&&n!==scroller;n=n.parentElement){if(getComputedStyle(n).position===\"sticky\")return true}return false};let lastCoverer=\"\";const clickable=el=>{if(el.getRootNode&&el.getRootNode()instanceof ShadowRoot)return true;let px=-1,py=-1,inViewport=false;const quads=el.getClientRects&&el.getClientRects().length?Array.from(el.getClientRects()):[el.getBoundingClientRect()];for(const q of quads){let l=Math.max(q.left,0),t=Math.max(q.top,0);let rr=Math.min(q.right,innerWidth),bb=Math.min(q.bottom,innerHeight);if(!(rr-l>0&&bb-t>0))continue;inViewport=true;for(const c of clipsOf(el)){l=Math.max(l,c.left);t=Math.max(t,c.top);rr=Math.min(rr,c.right);bb=Math.min(bb,c.bottom)}if(rr-l>0&&bb-t>0){px=(l+rr)/2;py=(t+bb)/2;break}}if(px<0)return inViewport?\"clipped\":true;let hit;try{hit=document.elementFromPoint(px,py)}catch(e){return true}if(!hit||hit===el||el.contains(hit)||hit.contains(el))return true;if(editorProxy(el,hit))return true;const scroller=scrollerOf(el);if(scroller&&scroller.contains(hit)&&stickyWithin(hit,scroller))return\"clipped\";const label=covererLabel(hit,nameOf,el);lastCoverer=label;coverers[label]=(coverers[label]||0)+1;coveredCount++;if(modal&&!modal.contains(el))behindModal++;return false};const items=[];const hits=[];let k=startK;const walk=root=>{const nodes=root.querySelectorAll(\"*\");for(const el of nodes){if(el.shadowRoot)walk(el.shadowRoot);let role=roleOf(el);const region=!ROLES.has(role)?scrollRegion(el):null;if(region)role=\"region\";if(role===\"separator\"&&!nameOf(el))continue;if(!role&&!ptrInteractive(el))continue;if(role&&!ROLES.has(role)&&!region){if(!ptrInteractive(el))continue;role=role||\"\"}const visual=checkableTarget(el);if(!visual&&!visible(el))continue;if(el.closest('[hidden], [inert], [aria-hidden=\"true\"]'))continue;const target=visual||el;const r=target.getBoundingClientRect();if(r.width<=0||r.height<=0)continue;const ref=refs.byElement.get(el);const item={ref,role:role||\"button\",name:region?region.name:nameOf(el),val:region?\"scrollable \"+[region.vertical?\"vertically\":\"\",region.horizontal?\"horizontally\":\"\"].filter(Boolean).join(\" and \"):stateOf(el),top:r.top,left:r.left,width:r.width,height:r.height,ptrOnly:region||ROLES.has(role)?0:1};const reach=clickable(target);if(!reach){item.by=lastCoverer;coveredItems.push(item);coveredHits.push(el);continue}if(reach===\"clipped\")item.clipped=1;items.push(item);hits.push(el)}};walk(document);const byEl=new Map;for(let i=0;i<items.length;i++)byEl.set(hits[i],i);const drop=new Set;for(let i=0;i<items.length;i++){const node=hits[i];if(items[i].ptrOnly&&node.tagName===\"LABEL\"){const input=node.control||node.previousElementSibling;if(byEl.has(input)&&checkableTarget(input))drop.add(i)}for(let n=hits[i].parentElement;n;n=n.parentElement){const j=byEl.get(n);if(j===void 0)continue;if(items[j].role===\"region\")continue;if(items[i].ptrOnly)drop.add(i);else if(items[j].ptrOnly&&items[j].name===items[i].name)drop.add(j)}}for(let i=items.length-1;i>=0;i--)if(drop.has(i)){items.splice(i,1);hits.splice(i,1)}const mintRefs=(list,nodes)=>{for(let i=0;i<list.length;i++){if(!list[i].ref){list[i].ref=\"e\"+k++;refs.byElement.set(nodes[i],list[i].ref)}refs.elements.set(list[i].ref,new WeakRef(nodes[i]))}};mintRefs(items,hits);mintRefs(coveredItems,coveredHits);const SEG_SKIP=new Set([\"SCRIPT\",\"STYLE\",\"NOSCRIPT\",\"TEMPLATE\"]);const segsOf=el=>{const out=[];let total=0;const push=raw=>{for(const part of((raw||\"\")+\"\").split(/[\\n\\t]+/)){const q=part.trim().replace(/\\s+/g,\" \");if(q){out.push(q);total+=q.length+3}}};const descend=(node,depth)=>{for(const c of node.childNodes){if(total>(__ROW_CAP__)+16)return;if(c.nodeType===3){push(c.nodeValue);continue}if(c.nodeType!==1||SEG_SKIP.has(c.tagName))continue;if(c.getClientRects&&c.getClientRects().length===0)continue;if(depth>0&&c.children&&c.children.length){descend(c,depth-1);continue}push(c.innerText!==void 0?c.innerText:c.textContent)}};descend(el,3);return out};const groups=new Map;for(let i=0;i<items.length;i++){const key=items[i].role+\"\\0\"+items[i].name;const g=groups.get(key);if(g)g.push(i);else groups.set(key,[i])}for(const idxs of groups.values()){if(idxs.length<2&&items[idxs[0]].name)continue;const wide=idxs.length>1;const chains=idxs.map(i=>{const c=[];let n=hits[i];for(let up=0;up<(__ROW_UP__)&&n.parentElement;up++){n=n.parentElement;c.push(n)}return c});const spread=new Map;for(const c of chains)for(const a of c)spread.set(a,(spread.get(a)||0)+1);for(let j=0;j<idxs.length;j++){const own=items[idxs[j]].name;let row=\"\";let head=\"\";const base=spread.get(chains[j][0])||1;for(const a of chains[j]){if(spread.get(a)>base||spread.get(a)>=idxs.length)break;const t=segsOf(a).filter(x=>x!==own).join(\" \\xB7 \");if(!t)continue;if(t.length>(__ROW_CAP__)){if(!row){row=t.slice(0,(__ROW_CAP__))+\"\\u2026\";break}const lead=t.split(\" \\xB7 \")[0]||\"\";if(lead&&row.indexOf(lead)<0)head=lead;continue}row=t;if(!wide)break}if(row&&head&&row.indexOf(head)<0&&head.length+3+row.length<=(__ROW_CAP__)){row=head+\" \\xB7 \"+row}if(row)items[idxs[j]].row=row;else items[idxs[j]].rowBlank=1}}const listed=new Set(hits);const byNode=new Map;for(let i=0;i<hits.length;i++){let node=hits[i].parentElement,up=0;while(node&&up<(__GROUP_UP__)){const label=!listed.has(node)&&node.getAttribute?(node.getAttribute(\"aria-label\")||\"\").trim():\"\";if(label){const members=byNode.get(node);if(members)members.push(items[i].ref);else byNode.set(node,[items[i].ref]);break}node=node.parentElement;up++}}const labelGroups=[];for(const entry of byNode){const label=(entry[0].getAttribute(\"aria-label\")||\"\").trim().replace(/\\s+/g,\" \");labelGroups.push({label:label.slice(0,(__GROUP_CAP__)),members:entry[1]})}let top=\"\",topN=0;for(const key in coverers)if(coverers[key]>topN){topN=coverers[key];top=key}const de=document.documentElement;return{items,coveredItems,groups:labelGroups,headings:headingsOf(visible,modal),nextK:k,vw:window.innerWidth,vh:window.innerHeight,scrollY:window.scrollY,docH:de?de.scrollHeight:0,covered:coveredCount?{count:coveredCount,top,topCount:topN,...modal&&behindModal===coveredCount?{modal:(nameOf(modal)||\"dialog\").slice(0,40)}:{}}:null}}))").replace("__ROLES__",JSON.stringify(e)).replace("__PAGE_REFS__",u).replace("__NAME_OF__",o).replace("__CHECKABLE_TARGET__",l).replace("__SCROLL_REGION__",a).replace("__COVERER_LABEL__",c).replace("__EDITOR_PROXY__",d).replace("__HEADINGS__",h).replace(/__ROW_CAP__/g,String(i)).replace(/__ROW_UP__/g,String(s)).replace(/__GROUP_CAP__/g,String(t)).replace(/__GROUP_UP__/g,String(n));export{y as VIEWPORT_JS};
@@ -0,0 +1,11 @@
1
+ export declare const BROWSER_ROLES: string[];
2
+ export declare const MAX_REFS = 320;
3
+ export declare const ROW_CAP = 72;
4
+ export declare const ROW_UP = 8;
5
+ export declare const GROUP_CAP = 200;
6
+ export declare const GROUP_UP = 12;
7
+ export declare const ROW_CONTEXT_BUDGET = 6000;
8
+ export declare const STATE_CLASS_JS: string;
9
+ export declare const NAME_JS: string;
10
+ export declare const ELEM_NAME_JS: string;
11
+ export declare function pageFn(src: string): (arg?: unknown) => unknown;
@@ -0,0 +1 @@
1
+ import{CHECKABLE_TARGET_JS as n}from"./checkable-target.js";const l=["link","button","textbox","searchbox","checkbox","radio","combobox","menuitem","menuitemcheckbox","menuitemradio","tab","switch","option","slider","spinbutton","file","separator"],s=320,r=72,a=8,i=200,c=12,b=6e3,u=("((n=>{for(const t of String(n||\"\").split(/\\s+/)){if(!t||t.indexOf(\":\")>=0||t.indexOf(\"[\")>=0)continue;const e=t.match(/(^|[_-])(active|selected)([_-]|$)/i);if(e)return e[2].toLowerCase()}return\"\"}))"),e=("((el=>{const clean=s=>(typeof s===\"string\"?s:\"\").trim().replace(/\\s+/g,\" \").slice(0,80);const LABEL_VALUE=new Set([\"submit\",\"button\",\"reset\"]);const tg=(el.tagName||\"\").toUpperCase();const vraw=tg===\"INPUT\"&&LABEL_VALUE.has(el.type)?el.value:\"\";const own=el.isContentEditable||tg===\"SELECT\"?\"\":el.innerText!==void 0?el.innerText:el.textContent;const root=el.getRootNode&&el.getRootNode()||document;const byId=id=>{try{return(root.getElementById?root:document).getElementById(id)}catch(e){return null}};const txtOf=n=>!n?\"\":(n.innerText!==void 0?n.innerText:n.textContent)||\"\";const lb=(el.getAttribute(\"aria-labelledby\")||\"\").trim();const lbTxt=!lb?\"\":lb.split(/\\s+/).map(id=>txtOf(byId(id))).join(\" \");const labelTxt=lab=>{if(!lab)return\"\";if(!(lab.contains&&lab.contains(el)))return txtOf(lab);let out=\"\";for(const n of lab.childNodes){if(n===el||n.contains&&n.contains(el))continue;out+=\" \"+txtOf(n)}return out};const forTxt=el.labels&&el.labels.length?labelTxt(el.labels[0]):\"\";for(const c of[lbTxt,el.getAttribute(\"aria-label\"),own,forTxt,vraw,el.title,el.getAttribute(\"alt\"),el.getAttribute(\"placeholder\"),el.getAttribute(\"data-placeholder\"),el.getAttribute(\"aria-placeholder\")]){const t=clean(c);if(t)return t}if((__CHECKABLE_TARGET__)(el)){const label=el.nextElementSibling;if(label&&label.tagName===\"LABEL\"&&!label.control)return clean(txtOf(label))}return\"\"}))").replace("__CHECKABLE_TARGET__",n),d=("((el=>{const clean=s=>(typeof s===\"string\"?s:\"\").trim().replace(/\\s+/g,\" \").slice(0,80);const text=(__TEXT_NAME__)(el);if(text)return text;if(!el.querySelector)return\"\";const svgEl=(el.tagName||\"\").toLowerCase()===\"svg\"?el:el.querySelector(\"svg\");const t0=svgEl&&svgEl.querySelector(\"title\");const st=clean(t0&&t0.textContent);if(st)return st;const iconish=s=>clean(String(s==null?\"\":s).replace(/^#/,\"\").replace(/^(icon|ico|svg|sprite)[-_]/i,\"\").replace(/[-_]+/g,\" \"));const use=el.querySelector(\"use\");const href=use&&(use.getAttribute(\"href\")||use.getAttribute(\"xlink:href\"));if(href){const t=iconish(String(href).split(\"#\").pop());if(t)return t}const clsOf=e=>e&&e.getAttribute?e.getAttribute(\"class\")||\"\":\"\";const words=clsOf(el).replace(/([a-z])([A-Z])/g,\"$1 $2\").toLowerCase().split(/[\\s_-]+/);if(words.some(w=>w===\"close\"||w===\"closebtn\"||w===\"dismiss\"))return\"close\";const cls=clsOf(el)+\" \"+clsOf(el.querySelector(\"i,[class*=icon]\"));const cands=cls.match(/[\\w-]*icon[\\w-]*/gi)||[];const best=cands.sort((x,y)=>y.length-x.length)[0];if(best){const t=iconish(best);if(t)return t}return\"\"}))").replace("__TEXT_NAME__",e),h=e;function m(t){return new Function("__arg",`return (${t})(__arg);`)}export{l as BROWSER_ROLES,h as ELEM_NAME_JS,i as GROUP_CAP,c as GROUP_UP,s as MAX_REFS,d as NAME_JS,r as ROW_CAP,b as ROW_CONTEXT_BUDGET,a as ROW_UP,u as STATE_CLASS_JS,m as pageFn};
@@ -0,0 +1,8 @@
1
+ import type { Locator } from "playwright-core";
2
+ export declare const COVERER_LABEL_JS: string;
3
+ export declare const EDITOR_PROXY_JS: string;
4
+ export declare function isEditorProxy(loc: Locator): Promise<boolean>;
5
+ export declare function coverOf(loc: Locator): Promise<{
6
+ who: string;
7
+ pseudo: string | null;
8
+ } | null>;
@@ -0,0 +1 @@
1
+ import{NAME_JS as o,pageFn as t}from"./collect.js";const r=("(((n,a,g)=>{const s=e=>(e||\"\").trim().replace(/\\s+/g,\" \").slice(0,80),l=n.closest('[role=\"tooltip\"], [role=\"dialog\"], [role=\"alertdialog\"], [role=\"menu\"], dialog, [popover]');if(l){const e=l.getAttribute(\"role\")||(l.tagName===\"DIALOG\"?\"dialog\":\"popover\"),o=s(a(l));return e+(o?' \"'+o+'\"':\"\")}const c=n.ownerDocument,i=c.defaultView;let t=null;for(let e=n;e&&e!==c.body&&e!==c.documentElement&&!(g&&e.contains(g));e=e.parentElement){const o=i.getComputedStyle(e).position;if(o===\"fixed\"||o===\"sticky\"){t=e;break}}const r=n.closest('button, a[href], input, select, textarea, [role=\"button\"], [role=\"tab\"], [role=\"menuitem\"]');if(r&&(!t||r.contains(t))){const e=r.getAttribute(\"role\")||(r.tagName===\"A\"?\"link\":r.tagName.toLowerCase()),o=s(a(r));return e+(o?' \"'+o+'\"':\"\")}if(t){const e=t.getBoundingClientRect(),o=(e.top+e.bottom)/2,d=i.innerHeight,h=e.height>=d*.9&&e.width>=i.innerWidth*.9?\" over the whole viewport\":o<d/3?\" at the top of the viewport\":o>d*2/3?\" along the bottom of the viewport\":\"\",w=i.getComputedStyle(t).position+(e.width>=e.height*2?\" bar\":\" layer\"),p=t.tagName.toLowerCase(),u=t.id&&!/[0-9a-f]{8}|\\d{4}/i.test(t.id)?\"#\"+t.id:\"\",b=s(t.getAttribute(\"aria-label\"))||([\"header\",\"nav\",\"footer\",\"aside\"].includes(p)?p+(u?\" \"+u:\"\"):u);return w+h+(b?\" (\"+b+\")\":\"\")}const m=n.closest(\"[aria-label]\");return m?'element \"'+a(m).slice(0,80)+'\"':n.id?\"element #\"+n.id:\"another element\"}))"),n=("(((t,e)=>{const n=t.tagName;return!(n===\"TEXTAREA\"||n===\"INPUT\"||t.isContentEditable||t.getAttribute(\"role\")===\"textbox\")||!e||!t.parentElement||!t.parentElement.contains(e)?!1:parseInt(t.ownerDocument.defaultView.getComputedStyle(t).zIndex,10)<0}))");async function a(e){try{return await e.evaluate(t(("((el=>{const proxy=(__EDITOR_PROXY__);const r=el.getBoundingClientRect();return proxy(el,el.ownerDocument.elementFromPoint(r.left+r.width/2,r.top+r.height/2))}))").replace("__EDITOR_PROXY__",n)),void 0,{timeout:2e3})}catch{return!1}}async function l(e){try{return await e.evaluate(t(("((el=>{const label=(__COVERER_LABEL__),nameOf=(__NAME_OF__),proxy=(__EDITOR_PROXY__);const doc=el.ownerDocument,win=doc.defaultView;const quads=el.getClientRects&&el.getClientRects().length?Array.from(el.getClientRects()):[el.getBoundingClientRect()];let px=-1,py=-1;for(const q of quads){const l=Math.max(q.left,0),t=Math.max(q.top,0);const rr=Math.min(q.right,win.innerWidth),bb=Math.min(q.bottom,win.innerHeight);if(rr-l>0&&bb-t>0){px=(l+rr)/2;py=(t+bb)/2;break}}if(px<0)return null;const hit=doc.elementFromPoint(px,py);if(!hit||hit===el||el.contains(hit)||proxy(el,hit))return null;if(!hit.contains(el))return{who:label(hit,nameOf,el),pseudo:null};if(win.getComputedStyle(el).pointerEvents===\"none\")return null;const pseudo=[\"::after\",\"::before\"].find(p=>{const st=win.getComputedStyle(hit,p);return st.content!==\"none\"&&st.content!==\"normal\"&&st.display!==\"none\"});if(!pseudo)return null;const named=label(hit,nameOf,el);const tag=hit.tagName.toLowerCase()+(hit.classList.length?\".\"+hit.classList[0]:\"\");return{who:named===\"another element\"?tag:named,pseudo}}))").replace("__COVERER_LABEL__",r).replace("__NAME_OF__",o).replace("__EDITOR_PROXY__",n)),void 0,{timeout:2e3})}catch{return null}}export{r as COVERER_LABEL_JS,n as EDITOR_PROXY_JS,l as coverOf,a as isEditorProxy};
@@ -0,0 +1,2 @@
1
+ import type { Frame, Page } from "playwright-core";
2
+ export declare function readableFrames(page: Page): Frame[];
@@ -0,0 +1 @@
1
+ function a(e){return e.frames().filter(r=>r.parentFrame()===null||r.url()!=="")}export{a as readableFrames};
@@ -0,0 +1 @@
1
+ export declare const HEADINGS_JS: string;
@@ -0,0 +1 @@
1
+ const e=60,t=("(((visible,modal)=>{const out=[];for(const h of document.querySelectorAll('h1, h2, h3, h4, h5, h6, [role=\"heading\"]')){if(out.length>=(__HEADINGS_CAP__))break;if(modal&&!modal.contains(h))continue;if(!visible(h)||h.closest('[hidden], [inert], [aria-hidden=\"true\"]'))continue;const r=h.getBoundingClientRect();if(r.width<=0||r.height<=0)continue;const name=(h.textContent||\"\").replace(/\\s+/g,\" \").trim().slice(0,80);if(!name)continue;const level=Number(h.getAttribute(\"aria-level\"))||Number(h.tagName.slice(1))||0;out.push({name,level,top:r.top,left:r.left,width:r.width,height:r.height})}return out}))").replace("__HEADINGS_CAP__",String(60));export{t as HEADINGS_JS};
@@ -0,0 +1,2 @@
1
+ import type { Locator, Page } from "playwright-core";
2
+ export declare function locateOnPage(page: Page, ref: string | undefined, name: string | undefined, fill: boolean): Promise<Locator | null>;
@@ -0,0 +1 @@
1
+ import{nameAgrees as u}from"../driver-refs.js";import{ELEM_NAME_JS as b,pageFn as x}from"./collect.js";import{readableFrames as f}from"./frames.js";import{refSelector as w}from"./page-refs.js";async function E(o,c,i,s){const r=i||"";if(c){const a=await w(o,c);for(const n of f(o))try{const t=n.locator(a);if(!await t.count())continue;const e=t.first(),m=await e.evaluate(x(b)).catch(()=>"");if(u(m,r))return e;break}catch{}}if(!r)return null;const l=s?["textbox","searchbox","combobox"]:["button","link","menuitem","tab"];for(const a of f(o))for(const n of l)try{const t=a.getByRole(n,{name:r,exact:!1});if(await t.count())return t.first()}catch{}return null}export{E as locateOnPage};
@@ -0,0 +1,9 @@
1
+ import { type Locator } from "playwright-core";
2
+ import type { ActResult } from "../types.ts";
3
+ import { ChromiumPageBase } from "./page-base.ts";
4
+ export declare abstract class ChromiumPageActs extends ChromiumPageBase {
5
+ protected locate(ref: string | undefined, name: string | undefined, fill: boolean): Promise<Locator | null>;
6
+ protected missing(ref?: string, name?: string): Promise<ActResult>;
7
+ private documentReplaced;
8
+ protected dispatchPage(kind: string, a: Record<string, any>): Promise<ActResult | undefined>;
9
+ }
@@ -0,0 +1 @@
1
+ import"playwright-core";import{locateOnPage as f}from"./locate.js";import{clickNamingCover as u,performDrag as m,refuseIfGated as l}from"./actions.js";import{performScroll as h,performScrollInto as p}from"./scroll.js";import{performPaste as w,performSelectOption as d,setTextWithFallbacks as g}from"./actions-value.js";import{performSelectText as b}from"./select-text.js";import{ChromiumPageBase as k,normalizeChord as y}from"./page-base.js";import{CHECKABLE_TARGET_JS as x}from"./checkable-target.js";import{pageFn as v}from"./collect.js";const P="the page was reloaded or navigated since the last observation, so every ref from before is gone; re-observe (w.observe()) and use a current one";class R extends k{async locate(n,t,r){const i=await f(this.page(),n,t,r);if(i){const e=await i.boundingBox().catch(()=>null);this.lastPoint=e?{x:e.x+e.width/2,y:e.y+e.height/2}:null}return i}async missing(n,t){const r=await this.blankSurfaceHint();if(r)return{ok:!1,note:r};const i=`no element for ref ${n??"(none)"}${t?` / "${t}"`:""}`;return{ok:!1,note:n&&await this.documentReplaced()?`${i} \u2014 ${P}`:i}}async documentReplaced(){if(!this.refHighWater)return!1;try{return(Number(await this.page().mainFrame().evaluate("Number(window.__mfNextK) || 0"))||0)<this.refHighWater}catch{return!1}}async dispatchPage(n,t){this.hookDiagnostics();const r=this.page(),i=typeof t.x=="number"&&typeof t.y=="number";switch(n){case"click":{if(i)return await r.mouse.click(t.x,t.y,{button:t.button??"left",clickCount:t.count??1}),{ok:!0};const e=await this.locate(t.ref,t.name,!1);if(!e)return await this.missing(t.ref,t.name);const o=await l(e,t.ref,t.name);if(o)return o;const a=await e.evaluateHandle(v(x));try{const c=a.asElement()??e,s=await c.boundingBox();return s&&(this.lastPoint={x:s.x+s.width/2,y:s.y+s.height/2}),await u(c,e,{button:t.button??"left",clickCount:t.count??1},t.ref,t.name)}finally{await a.dispose()}}case"hover":{if(i)return await r.mouse.move(t.x,t.y),{ok:!0};const e=await this.locate(t.ref,t.name,!1);return e?(await e.hover({timeout:8e3}),await r.waitForTimeout(400),{ok:!0,note:"hovering \u2014 anything revealed shows in the next observation and disappears when the pointer moves; to click a revealed item, do it next"}):await this.missing(t.ref,t.name)}case"set_text":{if(!t.ref)return await r.keyboard.type(t.text),{ok:!0};const e=await this.locate(t.ref,t.name,!0);if(!e)return{ok:!1,note:await this.blankSurfaceHint()||`no input for ref ${t.ref}`};const o=await l(e,t.ref,t.name);return o||await g(r,e,t,()=>this.locate(t.ref,t.name,!0))}case"keys":{const e=Math.min(Math.max(Math.trunc(Number(t.count))||1,1),20);for(let o=0;o<e;o++)await r.keyboard.press(y(t.chord));return{ok:!0}}case"drag":{const e=await m(r,t,(o,a)=>this.locate(o,a,!1));return e.point&&(this.lastPoint=e.point),e.res}case"select_option":return await d(r,t,()=>this.locate(t.ref,t.name,!0));case"select_text":return await b(r,t,()=>this.locate(t.ref,t.name,!1));case"paste":return await w(r,t,()=>this.locate(t.ref,t.name,!0));case"scroll":{const e=typeof t.into=="string"?await p(r,t.into):await h(r,t,this.scrollLedger,this.contentRead);return this.lastPoint=e.point,this.lastScrollMoved=e.moved,e.res}default:return}}}export{R as ChromiumPageActs};
@@ -0,0 +1,33 @@
1
+ import { type Page } from "playwright-core";
2
+ import { ComputerUseDriver } from "../driver.ts";
3
+ import type { OwnershipResource } from "../types.ts";
4
+ import { type ScrollLedgerEntry } from "./scroll.ts";
5
+ import { type PageReadState } from "./read-page.ts";
6
+ export declare function normalizeChord(chord: string): string;
7
+ export declare const POST_NAV_QUIET_MS = 1200;
8
+ export declare abstract class ChromiumPageBase extends ComputerUseDriver {
9
+ private readonly ownershipIds;
10
+ ownershipResources(): Promise<OwnershipResource[]>;
11
+ protected pageOwnership(page: Page): Promise<OwnershipResource>;
12
+ protected abstract ensure(): Promise<void>;
13
+ protected abstract page(): Page;
14
+ protected lastPoint: {
15
+ x: number;
16
+ y: number;
17
+ } | null;
18
+ protected scrollLedger: Map<string, ScrollLedgerEntry>;
19
+ protected pageReadState: PageReadState;
20
+ protected refHighWater: number;
21
+ readonly supportsDiagnostics = true;
22
+ readonly scrollsInto = true;
23
+ private inflight;
24
+ private hookInflight;
25
+ protected pendingRenders(): number;
26
+ private diagHooked;
27
+ protected hookDiagnostics(): void;
28
+ protected resetPageState(): void;
29
+ protected blankSurfaceHint(): Promise<string>;
30
+ protected navPending: boolean;
31
+ private navHooked;
32
+ protected hookNavigation(): void;
33
+ }
@@ -0,0 +1 @@
1
+ import"playwright-core";import{ComputerUseDriver as h}from"../driver.js";import"./scroll.js";import"./read-page.js";function f(i){return i.split("+").map(t=>{const e=t.trim().toLowerCase();return e===""&&t!==""?"Space":{ctrl:"Control",cmd:"Meta",command:"Meta",alt:"Alt",opt:"Alt",shift:"Shift",esc:"Escape",space:"Space"}[e]??t.trim()}).join("+")}const c=new Set(["fetch","xhr","document","script"]),l=2e3,w=1200;class m extends h{ownershipIds=new WeakMap;async ownershipResources(){return await this.ensure(),[await this.pageOwnership(this.page())]}async pageOwnership(t){let e=this.ownershipIds.get(t);if(!e){const o=await t.context().newCDPSession(t);try{e=(await o.send("Target.getTargetInfo")).targetInfo.targetId}finally{await o.detach().catch(()=>{})}if(!e)throw new Error("CDP did not identify the target; refusing to claim a guessed window.");this.ownershipIds.set(t,e)}return{key:`chromium:${e}`,mode:"exclusive"}}lastPoint=null;scrollLedger=new Map;pageReadState={};refHighWater=0;supportsDiagnostics=!0;scrollsInto=!0;inflight=new WeakMap;hookInflight(){let t;try{t=this.page()}catch{return}if(this.inflight.has(t))return;const e=new Map;this.inflight.set(t,e),t.on("request",r=>{c.has(r.resourceType())&&e.set(r,Date.now())});const o=r=>{e.delete(r)};t.on("requestfinished",o),t.on("requestfailed",o)}pendingRenders(){let t;try{t=this.page()}catch{return 0}const e=this.inflight.get(t);if(!e)return 0;const o=Date.now();for(const[r,n]of e)o-n>l&&e.delete(r);return e.size}diagHooked=new WeakSet;hookDiagnostics(){this.hookNavigation(),this.hookInflight();const t=this.diagnosticsSink;if(!t)return;let e;try{e=this.page()}catch{return}this.diagHooked.has(e)||(this.diagHooked.add(e),e.on("console",o=>{const r=o.type();if(r==="error"&&o.text().startsWith("Failed to load resource:"))return;const n=r==="error"?"console.error":r==="warning"?"console.warn":r==="info"?"console.info":r==="debug"?"console.debug":"console.log",s=o.location(),a=s.url?` (${s.url.split("/").pop()}:${s.lineNumber+1})`:"";t.push(n,`${o.text()}${a}`,{url:e.url()})}),e.on("pageerror",o=>{t.push("pageerror",String(o.message||o),{...o.stack?{detail:o.stack}:{},url:e.url()})}),e.on("requestfailed",o=>{const r=o.failure()?.errorText??"failed";r!=="net::ERR_ABORTED"&&t.push("network",`${o.method()} ${o.url()} \u2014 ${r}`,{url:e.url()})}),e.on("response",o=>{o.status()>=400&&t.push("network",`${o.request().method()} ${o.url()} \u2014 HTTP ${o.status()}`,{url:e.url()})}))}resetPageState(){this.scrollLedger.clear(),this.pageReadState={}}async blankSurfaceHint(){return""}navPending=!1;navHooked=new WeakSet;hookNavigation(){let t;try{t=this.page()}catch{return}this.navHooked.has(t)||(this.navHooked.add(t),t.on("framenavigated",e=>{e===t.mainFrame()&&(this.navPending=!0)}))}}export{m as ChromiumPageBase,w as POST_NAV_QUIET_MS,f as normalizeChord};
@@ -0,0 +1,18 @@
1
+ import type { ScopedShot, ShotScope, Snapshot, ToolResult } from "../types.ts";
2
+ import { ChromiumPageWaits } from "./page-waits.ts";
3
+ export declare abstract class ChromiumPageDriver extends ChromiumPageWaits {
4
+ protected snapshot(): Promise<Snapshot>;
5
+ private candidateCount;
6
+ private snapshotOnce;
7
+ readText(): Promise<string>;
8
+ readValue(ref: string): Promise<string>;
9
+ readTextForWait(): Promise<string>;
10
+ screenshot(): Promise<Buffer>;
11
+ screenshotScoped(scope: ShotScope): Promise<ScopedShot>;
12
+ currentUrl(): Promise<string>;
13
+ readPage(opts: {
14
+ ref?: string;
15
+ depth?: number;
16
+ node?: string;
17
+ }): Promise<ToolResult>;
18
+ }
@@ -0,0 +1,2 @@
1
+ import{MAX_REFS as A,ROW_CONTEXT_BUDGET as M,pageFn as w}from"./collect.js";import{VIEWPORT_JS as W}from"./collect-viewport.js";import{EXCERPT_JS as B,FULL_TEXT_JS as D,WAIT_TEXT_JS as $}from"./page-text.js";import{unreachedFacts as V}from"./unreached.js";import{readPage as J}from"./read-page.js";import{JPEG_SHOT as X,scopedShot as q,withPageCapture as I}from"./shot.js";import{POST_NAV_QUIET_MS as U}from"./page-base.js";import{locateOnPage as Y}from"./locate.js";import{readableFrames as G}from"./frames.js";import{ChromiumPageWaits as K}from"./page-waits.js";const L=u=>new Promise(t=>setTimeout(t,u)),j=2400,E=300,Q=6;async function z(u){for(let t=u;t&&t.parentFrame();t=t.parentFrame())try{const n=await(await t.frameElement()).evaluate(r=>{if(r.closest('[hidden], [inert], [aria-hidden="true"]'))return"an inert region";const i=r.ownerDocument.querySelector(":modal");return!i||i.contains(r)?null:(i.getAttribute("aria-label")||i.querySelector("h1, h2, h3")?.textContent||"").trim().slice(0,60)||"a modal dialog"});if(n)return n}catch{return null}return null}class he extends K{async snapshot(){const t=await this.snapshotOnce();if(!this.navPending||(this.navPending=!1,(s=>s.elements.length+(s.offscreenElements?.length??0))(t)>Q))return t;let r=await this.candidateCount(),i=0;for(let s=0;s<j;s+=E){await L(E);const h=await this.candidateCount();if(i=h>r?0:i+E,r=h,i>=U)break}return await this.snapshotOnce()}async candidateCount(){try{return Number(await this.page().mainFrame().evaluate("document.querySelectorAll('a[href],button,input,select,textarea,summary,[role],[contenteditable],[tabindex]').length"))||0}catch{return 0}}async snapshotOnce(){await this.ensure(),this.hookDiagnostics();const t=this.page(),n=[];let r=0;try{r=Number(await t.mainFrame().evaluate("Number(window.__mfNextK) || 0"))||0}catch{}r<this.refHighWater&&(this.resetObservationState(),this.resetPageState(),r=this.refHighWater);let i={scrollY:0,viewportH:1,docH:0};const s=[],h=[],P=[],g=[];let p=1280,v=800,l=0,y="",m=0,C=0,N="";for(const o of G(t)){let c=0,f=0;const x=o.parentFrame()===null;if(!x)try{const e=await(await o.frameElement()).boundingBox();if(!e)continue;c=e.x,f=e.y}catch{continue}let a;try{a=await o.evaluate(w(W),r)}catch{continue}r=a.nextK??r;const d=x?null:await z(o);d&&a.items?.length&&(a.coveredItems=[...a.coveredItems??[],...a.items.map(e=>({...e,by:d}))],l+=a.items.length,a.items.length>m&&(m=a.items.length,y=d),a.items=[]),x&&(p=a.vw||p,v=a.vh||v,i={scrollY:a.scrollY||0,viewportH:a.vh||1,docH:a.docH||0}),a.covered&&(l+=a.covered.count,a.covered.modal&&(C+=a.covered.count,N=a.covered.modal),a.covered.topCount>m&&(m=a.covered.topCount,y=a.covered.top));for(const e of a.coveredItems??[])e.width<=0||e.height<=0||h.push({ref:e.ref,role:e.role,name:e.name||"",state:e.val||"",rect:{x:e.left+c,y:e.top+f,w:e.width,h:e.height},...e.by?{coveredBy:e.by}:{}});for(const e of a.groups??[])e.label&&e.members?.length&&P.push(e);if(!d)for(const e of a.headings??[])g.push({name:e.name,level:e.level,rect:{x:e.left+c,y:e.top+f,w:e.width,h:e.height}});for(const e of a.items){const T=e.top+f,S=e.left+c;if(e.width<=0||e.height<=0)continue;const R={ref:e.ref,role:e.role,name:e.name||"",state:e.val||"",rect:{x:S,y:T,w:e.width,h:e.height},...e.row?{row:e.row}:{},...e.rowBlank?{rowBlank:!0}:{}};if(e.clipped||T>=v||S>=p||T+e.height<=0||S+e.width<=0){s.push(R);continue}n.push(R)}}n.sort((o,c)=>o.rect.y-c.rect.y),s.sort((o,c)=>o.rect.y-c.rect.y),h.sort((o,c)=>o.rect.y-c.rect.y);const b=Math.max(0,n.length-A);b&&(n.length=A);let O=0,_=0,F=!1;for(const o of n)o.row&&(F||O+o.row.length>M?(delete o.row,_++,F=!0):O+=o.row.length);_&&(i.rowsCut=_);const k=P;this.refHighWater=r;try{await t.mainFrame().evaluate(`window.__mfNextK = ${r}`)}catch{}let H="";try{H=await t.evaluate(w(B))}catch{}return s.length&&(i.offscreen=s.length),b&&(i.capped=b),{url:t.url(),title:await t.title().catch(()=>""),elements:n,offscreenElements:s,coveredElements:h,text:H,meta:i,...k.length?{groups:k}:{},...g.length?{headings:g}:{},...l?{covered:{count:l,top:y||"overlay",...C===l?{modal:N}:{}}}:{}}}async readText(){await this.ensure(),this.hookDiagnostics();const t=await this.page().evaluate(w(D)).catch(()=>null);if(!t)return"";const n=V({...t.un,frames:Math.max(0,t.un.frames-t.framesRead)},t.text.length>0);return t.outside>0&&n.push(`this read is the region the page marks as its main content \u2014 ${t.outside} chars outside it (header, navigation, footer) are left out; page() has them, and wait({text}) searches them`),t.screens>.5&&n.unshift(`~${t.screens} more screens below have not been scrolled into view \u2014 content there may not have loaded yet, so do not conclude something is absent`),n.length?`${t.text}
2
+ (${n.join("; ")})`:t.text}async readValue(t){if(!this.knownRef(t))throw new Error(this.staleRefNote(t));const n=await Y(this.page(),t,void 0,!1);if(!n)throw new Error((await this.missing(t)).note??`no element for ref ${t}`);return String(await n.evaluate(r=>"value"in r&&typeof r.value=="string"&&!/^(button|submit|reset)$/i.test(r.type||"")?r.value:r.innerText||r.textContent||""))}async readTextForWait(){return await this.ensure(),await this.page().evaluate(w($)).catch(()=>null)??""}async screenshot(){await this.ensure(),this.hookDiagnostics();const t=this.page();return await I(t,()=>t.screenshot({...X,timeout:1e4}))}async screenshotScoped(t){await this.ensure(),this.hookDiagnostics();const n=this.page();return await I(n,()=>q(n,t,r=>this.locate(r,void 0,!1)))}async currentUrl(){try{return await this.ensure(),this.page().url()}catch{return""}}async readPage(t){return await this.ensure(),this.bounded("read page",J(this.page(),t,this.pageReadState,async()=>{await this.snapshot()}))}}export{he as ChromiumPageDriver};
@@ -0,0 +1,4 @@
1
+ import { type Browser, type Page } from "playwright-core";
2
+ export declare const PAGE_REFS_JS: string;
3
+ export declare function ensureRefSelector(browser: Browser): Promise<string>;
4
+ export declare function refSelector(page: Page, ref: string): Promise<string>;
@@ -0,0 +1 @@
1
+ import{selectors as o}from"playwright-core";const l=("((()=>{const e=Symbol.for(\"mirasim-computer-use.page-refs\");return(!globalThis[e]||globalThis[e].document!==document)&&(globalThis[e]={document,byElement:new WeakMap,elements:new Map,nodes:new Map}),globalThis[e]}))"),s=("((()=>{const query=(root,ref)=>{const state=(__PAGE_REFS__)();const el=state.elements.get(ref)?.deref();if(!el?.isConnected)return null;if(root.nodeType===9)return el.ownerDocument===root?el:null;for(let n=el;n;n=n.parentNode||n.host)if(n===root)return el;return null};return{query,queryAll:(root,ref)=>{const el=query(root,ref);return el?[el]:[]}}})())").replace("__PAGE_REFS__",l),n=new WeakMap;let c=0;function a(t){let e=n.get(t);if(!e){const r=`mirasim_ref_${++c}`;e=o.register(r,{content:s},{contentScript:!1}).then(()=>r),n.set(t,e)}return e}async function u(t,e){const r=t.context().browser();if(!r)throw new Error("The page has no connected browser for ref lookup");return`${await a(r)}=${e.replace(/[^\w-]/g,"")}`}export{l as PAGE_REFS_JS,a as ensureRefSelector,u as refSelector};
@@ -0,0 +1,15 @@
1
+ export declare const EXCERPT_JS: string;
2
+ export interface FullTextResult {
3
+ text: string;
4
+ screens: number;
5
+ un: {
6
+ canvases: number;
7
+ frames: number;
8
+ folded: number;
9
+ viewerType: string;
10
+ };
11
+ framesRead: number;
12
+ outside: number;
13
+ }
14
+ export declare const FULL_TEXT_JS: string;
15
+ export declare const WAIT_TEXT_JS: string;
@@ -0,0 +1 @@
1
+ import{unreachedJs as t}from"./unreached.js";const n=120,o=4e3,a=4e4,r=("((()=>{const out=[];let lineCut=false;const inView=el=>{const r=el.getBoundingClientRect();return r.bottom>0&&r.top<window.innerHeight&&r.width>0&&r.height>0};const walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT);let n;while(n=walker.nextNode()){if(n.nodeType===1){if(n.tagName!==\"IMG\")continue;const alt=(n.getAttribute(\"alt\")||\"\").trim().replace(/\\s+/g,\" \");if(!alt||!inView(n))continue;const st=getComputedStyle(n);if(st.visibility===\"hidden\"||st.display===\"none\")continue;if(out.length>=(__LINE_CAP__)){lineCut=true;break}out.push('img \"'+alt.slice(0,60)+'\"');continue}const t=(n.nodeValue||\"\").trim().replace(/\\s+/g,\" \");if(t.length<1||t.length<2&&!/[\\p{L}\\p{N}]/u.test(t))continue;const p=n.parentElement;if(!p||!inView(p))continue;const s=getComputedStyle(p);if(s.visibility===\"hidden\"||s.display===\"none\")continue;if(out.length>=(__LINE_CAP__)){lineCut=true;break}out.push(t)}if(lineCut)out.push(\"(cut at __LINE_CAP__ lines \\u2014 the viewport has more)\");const all=out.join(\"\\n\");if(all.length<=(__CHAR_CAP__))return all;return all.slice(0,(__CHAR_CAP__))+\"\\n(cut at __CHAR_CAP__ chars \\u2014 the page has the full text)\"}))").replace(/__LINE_CAP__/g,String(n)).replace(/__CHAR_CAP__/g,String(o)),e=("((l=>{const n=[];let o=0;for(const t of l.querySelectorAll(\"iframe\")){let e=null;try{e=t.contentDocument&&t.contentDocument.body}catch{e=null}if(!e)continue;const c=t.getBoundingClientRect();c.width>=100&&c.height>=100&&o++;const i=(e.innerText||\"\").trim();i&&n.push(\"[embedded page\"+(t.title?' \"'+t.title+'\"':\"\")+`]\n`+i)}return{parts:n,read:o}}))"),s=("((()=>{const visible=Array.from(document.querySelectorAll(\"[role=dialog], [role=alertdialog], dialog[open], [aria-modal=true]\")).filter(d=>d.getClientRects().length>0);const outer=visible.filter(d=>!visible.some(o=>o!==d&&o.contains(d)));const main=document.querySelector(\"main, [role=main]\");const articles=document.querySelectorAll(\"article\");let el=main||(articles.length===1?articles[0]:null);let note=\"\";if(el){const len=(el.innerText||\"\").length;const dialogChars=outer.reduce((n,d)=>n+(d.innerText||\"\").length,0);const bodyLen=(document.body.innerText||\"\").length-dialogChars;if(len*2<bodyLen){note=\"(the <\"+el.tagName.toLowerCase()+\"> this page marks as its main content holds only \"+len+\" of the page's \"+bodyLen+\" chars, so this read is the WHOLE page instead)\";el=null}}el=el||document.body;const outside=el===document.body?0:Math.max(0,(document.body.innerText||\"\").length-(el.innerText||\"\").length-outer.reduce((n,d)=>n+(el.contains(d)?0:(d.innerText||\"\").length),0));const frames=(__FRAMES__)(el);const parts=outer.filter(d=>!el.contains(d)).map(d=>d.innerText||\"\").concat(el.innerText||\"\").concat(frames.parts).filter(Boolean);const below=Math.max(0,document.documentElement.scrollHeight-document.documentElement.clientHeight-document.documentElement.scrollTop);const screens=Math.round(below/Math.max(1,window.innerHeight)*10)/10;const un=(__UNREACHED__);const all=(note?note+\"\\n\":\"\")+parts.join(\"\\n\\n\").replace(/\\n{3,}/g,\"\\n\\n\");const body=all.length<=(__CAP__)?all:all.slice(0,(__CAP__))+\"\\n(cut at __CAP__ chars \\u2014 the page continues past this)\";return{text:body,screens,un,framesRead:frames.read,outside}}))").replace(/__CAP__/g,String(a)).replace("__UNREACHED__",t("document.body")).replace("__FRAMES__",e),l=("((()=>{const frames=(__FRAMES__)(document.body);return[document.body.innerText||\"\"].concat(frames.parts).join(\"\\n\\n\")}))").replace("__FRAMES__",e);export{r as EXCERPT_JS,s as FULL_TEXT_JS,l as WAIT_TEXT_JS};
@@ -0,0 +1,7 @@
1
+ import type { ActResult } from "../types.ts";
2
+ import { ChromiumPageActs } from "./page-acts.ts";
3
+ export declare abstract class ChromiumPageWaits extends ChromiumPageActs {
4
+ waitForUrl(want: string, gone: boolean, capMs?: number): Promise<ActResult>;
5
+ waitForLoad(capMs?: number): Promise<ActResult>;
6
+ waitSettled(capMs?: number): Promise<ActResult>;
7
+ }
@@ -0,0 +1 @@
1
+ import{waitForLoadImpl as m,waitForUrlImpl as w}from"../driver-wait.js";import{ChromiumPageActs as g}from"./page-acts.js";const p=a=>new Promise(t=>setTimeout(t,a)),y=150,f=50,M=a=>Math.min(r,Math.max(y,a/2)),r=1e3;class _ extends g{async waitForUrl(t,i,n=16e3){return await this.ensure(),w(async()=>this.page().url(),t,i,n)}async waitForLoad(t=16e3){return await this.ensure(),m(async()=>String(await this.page().mainFrame().evaluate("document.readyState")),t)}async waitSettled(t=16e3){await this.ensure(),this.hookDiagnostics();const i=this.page(),n=()=>i.evaluate("(() => { const t = document.body ? document.body.innerText : ''; return document.readyState + '|' + t.length + '|' + t.slice(-240); })()"),l=Date.now();let e=await n().catch(()=>null),o=Date.now(),c=null,u=f;for(;Date.now()-l<t;){await p((e??"").startsWith("complete|")?u:r);const s=await n().catch(()=>null);if(s===null||s!==e)o=Date.now(),c??=o,Date.now()-l>r&&(u=Math.min(r,Math.round(u*1.7)));else if(s.startsWith("complete|")&&Date.now()-o>=M(c===null?0:o-c)&&this.pendingRenders()===0){const d=Date.now()-l;return{ok:!0,note:`content settled after ${d<1e3?`${d}ms`:`${Math.round(d/1e3)}s`}`}}e=s??e}const h=(e??"").split("|")[0];return{ok:!0,note:h&&h!=="complete"?`the document never finished loading (readyState ${h}) after ${Math.round(t/1e3)}s \u2014 what you read now may be a shell the site never filled in; reload or try again later`:`content still changing after ${Math.round(t/1e3)}s`}}}export{_ as ChromiumPageWaits};
@@ -0,0 +1,37 @@
1
+ import type { PreviewSink, PreviewStream, PreviewTarget } from "../types.ts";
2
+ export declare class CdpPreviewStream implements PreviewStream {
3
+ private readonly target;
4
+ private readonly sink;
5
+ private readonly maxSize;
6
+ private socket;
7
+ private nextId;
8
+ private sessionId;
9
+ private attachId;
10
+ private metricsId;
11
+ private width;
12
+ private height;
13
+ private lastFrameAt;
14
+ private disposed;
15
+ private connecting;
16
+ private videoStarted;
17
+ private videoStartedAt;
18
+ private sawScreencast;
19
+ private screenshotId;
20
+ private lastScreenshotAt;
21
+ private unavailableNow;
22
+ private connectionTimer;
23
+ private pendingCast;
24
+ private unacked;
25
+ private castTimer;
26
+ constructor(target: PreviewTarget, sink: PreviewSink, maxSize?: number);
27
+ tick(): Promise<void>;
28
+ private startVideo;
29
+ private message;
30
+ private scheduleCast;
31
+ private resetCast;
32
+ private frame;
33
+ private send;
34
+ private unavailable;
35
+ private capture;
36
+ dispose(): void;
37
+ }
@@ -0,0 +1 @@
1
+ import u from"ws";const f=2560,p=100;async function d(c){const e=new AbortController,t=setTimeout(()=>e.abort(),1200);try{const s=await fetch(c,{signal:e.signal});return s.ok?await s.json():null}catch{return null}finally{clearTimeout(t)}}class g{target;sink;maxSize;socket=null;nextId=0;sessionId=null;attachId=-1;metricsId=-1;width=0;height=0;lastFrameAt=0;disposed=!1;connecting=!1;videoStarted=!1;videoStartedAt=0;sawScreencast=!1;screenshotId=null;lastScreenshotAt=0;unavailableNow=!1;connectionTimer=null;pendingCast=null;unacked=[];castTimer=null;constructor(e,t,s=f){this.target=e,this.sink=t,this.maxSize=s}async tick(){const e=this.target.capture;if(this.disposed||this.connecting||e.type!=="cdp")return;if(this.socket){this.videoStarted&&!this.sawScreencast&&Date.now()-this.videoStartedAt>1500&&Date.now()-this.lastScreenshotAt>1e3&&this.capture();return}const t=e.endpoint.replace(/\/$/,"");this.connecting=!0;try{let s=t,a=!1;if(/^https?:/.test(t)){const n=await d(`${t}/json/list`);if(!Array.isArray(n)||n.some(r=>!r||typeof r.id!="string"||typeof r.type!="string"||typeof r.url!="string")){this.unavailable("Could not read the application targets. Reconnecting\u2026");return}if(this.disposed)return;const h=n.find(r=>r.id===e.targetId&&r.type==="page");if(!h){this.dispose(),this.sink.gone();return}a=!!h.webSocketDebuggerUrl;const l=a?null:await d(`${t}/json/version`);s=h.webSocketDebuggerUrl??l?.webSocketDebuggerUrl??""}if(this.disposed)return;if(!s){this.unavailable("The application video endpoint is unavailable. Reconnecting\u2026");return}const i=new u(s);this.socket=i;const o=Date.now();this.connectionTimer=setTimeout(()=>{this.socket===i&&this.lastFrameAt<o&&(this.unavailable("The application did not provide a frame. Reconnecting\u2026"),i.close())},6e3),this.connectionTimer.unref(),i.on("open",()=>{this.disposed||this.socket!==i||(a?this.startVideo():this.attachId=this.send("Target.attachToTarget",{targetId:e.targetId,flatten:!0},!1))}),i.on("message",n=>{this.socket===i&&this.message(String(n))}),i.on("error",()=>{this.socket===i&&this.unavailable("The application video connection failed.")}),i.on("close",()=>{this.socket===i&&(this.unavailable("The video connection was interrupted. Reconnecting\u2026"),this.socket=null,this.sessionId=null,this.videoStarted=!1,this.sawScreencast=!1,this.screenshotId=null,this.connectionTimer&&clearTimeout(this.connectionTimer),this.resetCast())})}catch{this.unavailable("The application video connection failed. Reconnecting\u2026")}finally{this.connecting=!1}}startVideo(){this.videoStarted||(this.videoStarted=!0,this.videoStartedAt=Date.now(),this.metricsId=this.send("Page.getLayoutMetrics",{}),this.capture(),this.send("Page.startScreencast",{format:"png",maxWidth:this.maxSize,maxHeight:this.maxSize,everyNthFrame:2}))}message(e){if(this.disposed)return;let t;try{t=JSON.parse(e)}catch{return}const s=this.target.capture;if(t.method==="Target.attachedToTarget"&&s.type==="cdp"&&t.params?.targetInfo?.targetId===s.targetId&&typeof t.params.sessionId=="string"){this.sessionId=t.params.sessionId,this.startVideo();return}if(t.id===this.attachId){if(t.error||!t.result?.sessionId){this.unavailable(t.error?.message??"Could not attach to the selected tab.");return}this.sessionId??=t.result.sessionId,this.startVideo();return}if(!(this.sessionId&&t.sessionId!==this.sessionId)){if(t.id===this.screenshotId&&(this.screenshotId=null,t.error)){this.unavailable(t.error.message??"The application could not provide a frame.");return}if(t.id===this.metricsId&&(this.width=t.result?.cssVisualViewport?.clientWidth??0,this.height=t.result?.cssVisualViewport?.clientHeight??0),t.method==="Page.screencastFrame"&&t.params?.data)this.sawScreencast=!0,this.pendingCast={data:t.params.data,w:t.params.metadata?.deviceWidth??this.width,h:t.params.metadata?.deviceHeight??this.height},this.unacked.push(t.params.sessionId),this.scheduleCast();else if(t.result?.data){const a=Buffer.from(t.result.data.slice(0,32),"base64");if(a.length<24)return;const i=a.readUInt32BE(16),o=a.readUInt32BE(20);if(!i||!o)return;const n=this.width?this.width/i:1;this.frame(t.result.data,i*n,o*n)}}}scheduleCast(){if(this.castTimer||this.disposed)return;const e=Math.max(0,this.lastFrameAt+p-Date.now());this.castTimer=setTimeout(()=>{if(this.castTimer=null,this.disposed)return;const t=this.pendingCast;this.pendingCast=null,t&&this.frame(t.data,t.w,t.h),this.unacked.length&&this.send("Page.screencastFrameAck",{sessionId:this.unacked.shift()}),this.unacked.length&&this.scheduleCast()},e),this.castTimer.unref()}resetCast(){this.castTimer&&clearTimeout(this.castTimer),this.castTimer=null,this.pendingCast=null,this.unacked=[]}frame(e,t,s){this.lastFrameAt=Date.now(),this.connectionTimer&&clearTimeout(this.connectionTimer),this.unavailableNow&&(this.unavailableNow=!1,this.sink.status({unavailable:null})),this.sink.frame({mimeType:"image/png",data:e,width:t,height:s,capturedAt:Date.now()})}send(e,t,s=!0){const a=++this.nextId;try{this.socket?.send(JSON.stringify({id:a,method:e,params:t,...s&&this.sessionId?{sessionId:this.sessionId}:{}}))}catch{}return a}unavailable(e){this.disposed||(this.unavailableNow=!0,this.sink.status({unavailable:e}))}capture(){this.screenshotId!==null&&Date.now()-this.lastScreenshotAt<5e3||(this.lastScreenshotAt=Date.now(),this.screenshotId=this.send("Page.captureScreenshot",{format:"png"}))}dispose(){this.disposed=!0,this.connectionTimer&&clearTimeout(this.connectionTimer),this.resetCast(),this.socket&&this.send("Page.stopScreencast",{}),this.sessionId&&this.send("Target.detachFromTarget",{sessionId:this.sessionId},!1),this.socket?.close(),this.socket=null}}export{g as CdpPreviewStream};
@@ -0,0 +1,3 @@
1
+ import type { Page } from "playwright-core";
2
+ import type { PreviewTarget } from "../types.ts";
3
+ export declare function previewPage(page: Page, endpoint: string, kind: "browser" | "electron"): Promise<PreviewTarget | null>;
@@ -0,0 +1 @@
1
+ const n=new WeakMap;async function c(t,s,r){if(t.isClosed())return null;let e=n.get(t);e||(e=(async()=>{const a=await t.context().newCDPSession(t);try{return(await a.send("Target.getTargetInfo")).targetInfo.targetId}finally{await a.detach().catch(()=>{})}})(),n.set(t,e),e.catch(()=>n.delete(t)));const i=await e;return t.isClosed()?null:{kind:r,targetId:i,title:await t.title().catch(()=>""),url:t.url(),appName:r==="browser"?"Browser":"Desktop app",capture:{type:"cdp",endpoint:s,targetId:i}}}export{c as previewPage};
@@ -0,0 +1 @@
1
+ export declare const TREE_JS: string;
@@ -0,0 +1 @@
1
+ import{STATE_CLASS_JS as e}from"./collect.js";import{unreachedJs as t}from"./unreached.js";import{PAGE_REFS_JS as n}from"./page-refs.js";const r=t("root"),i=("((arg=>{const refs=("+(n)+")();const rootSel=arg.node||arg.ref,depth=arg.depth,cap=arg.cap;const root=arg.node?refs.nodes.get(arg.node)?.deref():arg.ref?refs.elements.get(arg.ref)?.deref():document.body;if(!root?.isConnected)return{error:\"target not found on this page (it may have been re-rendered)\"};refs.nodes.clear();const containsRef=new WeakSet;for(const entry of refs.elements.values()){const el=entry.deref();if(el?.isConnected)for(let n=el;n;n=n.parentElement)containsRef.add(n)}const INLINE=new Set([\"B\",\"I\",\"EM\",\"STRONG\",\"A\",\"SPAN\",\"CODE\",\"SMALL\",\"SUP\",\"SUB\",\"MARK\",\"U\",\"S\",\"ABBR\",\"TIME\",\"BR\",\"WBR\",\"LABEL\"]);const MEDIA={IMG:\"img\",SVG:\"svg\",VIDEO:\"video\",AUDIO:\"audio\",CANVAS:\"canvas\",PICTURE:\"img\"};const SKIP=new Set([\"SCRIPT\",\"STYLE\",\"NOSCRIPT\",\"TEMPLATE\",\"HEAD\",\"LINK\",\"META\"]);const clean=s=>(typeof s===\"string\"?s:\"\").replace(/\\s+/g,\" \").trim();const visible=el=>{const s=getComputedStyle(el);return s.visibility!==\"hidden\"&&s.display!==\"none\"&&parseFloat(s.opacity||\"1\")>0};const vw=window.innerWidth,vh=window.innerHeight;let nextNode=0;const vpOf=el=>{const r=el.getBoundingClientRect();if(r.width<=0&&r.height<=0)return\"out\";const overlaps=r.bottom>0&&r.top<vh&&r.right>0&&r.left<vw;if(!overlaps)return\"out\";return r.top>=0&&r.bottom<=vh&&r.left>=0&&r.right<=vw?\"in\":\"part\"};const coveredBy=el=>{if(el.getRootNode()instanceof ShadowRoot)return false;let q=null;for(const r of el.getClientRects()){if(r.bottom>0&&r.top<vh&&r.right>0&&r.left<vw&&r.width>0&&r.height>0){q=r;break}}if(!q)return false;const x=(Math.max(0,q.left)+Math.min(vw,q.right))/2;const y=(Math.max(0,q.top)+Math.min(vh,q.bottom))/2;const hit=document.elementFromPoint(x,y);if(!hit)return false;return!(hit===el||el.contains(hit)||hit.contains(el))};const mediaName=el=>{const alt=clean(el.getAttribute&&(el.getAttribute(\"alt\")||el.getAttribute(\"aria-label\")||el.getAttribute(\"title\")));if(alt)return alt;const src=el.getAttribute&&(el.getAttribute(\"src\")||el.getAttribute(\"href\"));if(src){const base=String(src).split(\"?\")[0].split(\"/\").pop();if(base&&base.length<=60)return base}const t=el.querySelector&&el.querySelector(\"title\");return clean(t&&t.textContent)||\"\"};const stateOf=el=>{const cut2=s=>{s=clean(s);return s.length>60?s.slice(0,60)+\"\\u2026\":s};const cls=typeof el.className===\"string\"?el.className:\"\";const inv=el.getAttribute&&el.getAttribute(\"aria-invalid\")===\"true\"||/(^|[\\s_-])(error|invalid)([\\s_-]|$)/i.test(cls);const cur=el.getAttribute&&el.getAttribute(\"aria-current\");const sel=!el.getAttribute?\"\":el.getAttribute(\"aria-selected\")===\"true\"?\"selected\":el.getAttribute(\"aria-pressed\")===\"true\"?\"pressed\":cur&&cur!==\"false\"?\"current\":\"\";const selCls=("+(e)+")(cls);const mark=s=>[inv?\"invalid\":\"\",sel||(!s?selCls:\"\"),s].filter(Boolean).join(\", \");const tag=el.tagName;if(el.isContentEditable){const v=cut2(el.innerText);return mark(v?\"filled:\"+v:\"empty\")}if(tag===\"INPUT\"&&(el.type===\"checkbox\"||el.type===\"radio\"))return mark(el.checked?\"checked\":\"unchecked\");if(tag===\"SELECT\"){const o=el.selectedOptions&&el.selectedOptions[0];const t=cut2(o&&(o.label||o.textContent));return mark(t?\"selected:\"+t:\"unselected\")}if(tag===\"TEXTAREA\"||tag===\"INPUT\"){const v=el.value==null?\"\":String(el.value);if(!v)return mark(\"empty\");const ws=v!==v.trim()?\" (value has edge whitespace \\u2014 drill this ref for the exact value)\":\"\";return mark(el.type===\"password\"?\"filled:\"+v.length+\" chars\":\"filled:\"+cut2(v)+ws)}return mark(\"\")};const runOf=(raw,node)=>{let t=clean(raw);if(!t)return null;if(/^(…|\\.\\.\\.)/.test(t))node.clipped=true;if(t.length>cap){t=t.slice(0,cap);node.cut=true}node.text=t;return node};const isProse=el=>{let hasText=false;for(const n of el.childNodes){if(n.nodeType===3){if(clean(n.nodeValue))hasText=true}else if(n.nodeType===1){if(SKIP.has(n.tagName))continue;if(!INLINE.has(n.tagName))return false;if(containsRef.has(n))return false}}return hasText};const hasContentBelow=el=>!!clean(el.textContent)||containsRef.has(el)||!!(el.querySelector&&el.querySelector(\"img,svg,video,iframe\"));const build=(el,level,parentVp)=>{if(SKIP.has(el.tagName))return null;if(!visible(el))return null;const node={};const vp=vpOf(el);if(vp!==parentVp)node.vp=vp;const ref=refs.byElement.get(el);const media=MEDIA[el.tagName];const tag=el.tagName;if(tag===\"IFRAME\"||tag===\"FRAME\"){node.tag=\"iframe\";node.name=\"embedded page \\u2014 its content is not in this tree\";return node}if(media){node.tag=media;const n=mediaName(el);if(n)node.name=n;return Object.keys(node).length>1||node.tag?node:null}if(tag===\"INPUT\"||tag===\"TEXTAREA\"||tag===\"SELECT\"){node.role=tag===\"SELECT\"?\"combobox\":\"textbox\";if(tag===\"INPUT\"&&(el.type===\"checkbox\"||el.type===\"radio\"))node.role=el.type;if(tag===\"INPUT\"&&(el.type===\"button\"||el.type===\"submit\"))node.role=\"button\";if(ref)node.ref=ref;const nm=clean(el.getAttribute(\"aria-label\")||el.getAttribute(\"placeholder\")||el.value&&[\"button\",\"submit\",\"reset\"].includes(el.type)&&el.value||\"\");if(nm)node.name=nm;const st=stateOf(el);if(st)node.state=st;if(ref&&vp!==\"out\"&&coveredBy(el))node.covered=true;return node}if(ref){node.ref=ref;node.role=el.getAttribute(\"role\")||({A:\"link\",BUTTON:\"button\"}[tag]||\"button\");const st=stateOf(el);if(st)node.state=st;if(vp!==\"out\"&&coveredBy(el))node.covered=true}const label=clean(el.getAttribute&&el.getAttribute(\"aria-label\"));if(label&&!node.name)node.name=label;const selfContentful=!!(node.ref||node.name);const myLevel=level+(selfContentful||isProse(el)?1:0);if(depth>0&&myLevel>depth){if(!hasContentBelow(el)&&!selfContentful)return null;const id=\"n\"+ ++nextNode;refs.nodes.set(id,new WeakRef(el));node.fold={items:el.children.length,chars:clean(el.textContent).length,id};return node}if(isProse(el))return runOf(el.innerText,node)||(selfContentful?node:null);const kids=[];for(const n of el.childNodes){if(n.nodeType===3){const r=runOf(n.nodeValue,{});if(r)kids.push(r)}else if(n.nodeType===1){if(n.shadowRoot){for(const sn of n.shadowRoot.children){const b2=build(sn,myLevel,vp);if(b2)kids.push(b2)}}const b=build(n,myLevel,vp);if(b)kids.push(b)}}if(!selfContentful&&!node.vp){if(kids.length===1)return kids[0];if(kids.length===0)return null}else if(kids.length===0&&!selfContentful)return null;if(kids.length)node.kids=kids;return selfContentful||node.kids||node.text?node:null};const tree=build(root,0,vpOf(root))||{};const remOf=el=>Math.max(0,el.scrollHeight-el.clientHeight-el.scrollTop);const rem=rootSel?remOf(root):Math.max(remOf(document.documentElement),remOf(document.body));let exact;if(rootSel){const tg=root.tagName;if((tg===\"TEXTAREA\"||tg===\"INPUT\")&&typeof root.value===\"string\"&&root.type!==\"password\")exact=String(root.value);else if(root.isContentEditable)exact=String(root.innerText||\"\")}const un=("+(r)+");return{tree,moreScreens:Math.round(rem/Math.max(1,vh)*10)/10,exact,un}}))");export{i as TREE_JS};