ai-dev-browser 0.12.2__tar.gz → 0.12.4__tar.gz

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 (197) hide show
  1. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.github/workflows/ci.yml +2 -1
  2. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/PKG-INFO +1 -1
  3. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/_tab.py +63 -25
  4. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/PKG-INFO +1 -1
  5. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/SOURCES.txt +1 -0
  6. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/scm_file_list.json +1 -0
  7. ai_dev_browser-0.12.4/ai_dev_browser.egg-info/scm_version.json +8 -0
  8. ai_dev_browser-0.12.4/tests/integration/test_scroll_embedded_target.py +211 -0
  9. ai_dev_browser-0.12.2/ai_dev_browser.egg-info/scm_version.json +0 -8
  10. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.github/workflows/publish.yml +0 -0
  11. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.gitignore +0 -0
  12. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.gitmodules +0 -0
  13. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.pre-commit-config.yaml +0 -0
  14. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/.secrets.baseline +0 -0
  15. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/LICENSE +0 -0
  16. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/README.md +0 -0
  17. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/__init__.py +0 -0
  18. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/_cli.py +0 -0
  19. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/_version.py +0 -0
  20. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/__init__.py +0 -0
  21. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/accessibility.py +0 -0
  22. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/animation.py +0 -0
  23. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/audits.py +0 -0
  24. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/autofill.py +0 -0
  25. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/background_service.py +0 -0
  26. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
  27. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/browser.py +0 -0
  28. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/cache_storage.py +0 -0
  29. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/cast.py +0 -0
  30. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/console.py +0 -0
  31. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/crash_report_context.py +0 -0
  32. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/css.py +0 -0
  33. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/debugger.py +0 -0
  34. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/device_access.py +0 -0
  35. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/device_orientation.py +0 -0
  36. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/dom.py +0 -0
  37. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/dom_debugger.py +0 -0
  38. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
  39. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/dom_storage.py +0 -0
  40. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/emulation.py +0 -0
  41. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
  42. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/extensions.py +0 -0
  43. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/fed_cm.py +0 -0
  44. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/fetch.py +0 -0
  45. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/file_system.py +0 -0
  46. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/headless_experimental.py +0 -0
  47. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/heap_profiler.py +0 -0
  48. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/indexed_db.py +0 -0
  49. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/input_.py +0 -0
  50. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/inspector.py +0 -0
  51. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/io.py +0 -0
  52. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/layer_tree.py +0 -0
  53. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/log.py +0 -0
  54. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/media.py +0 -0
  55. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/memory.py +0 -0
  56. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/network.py +0 -0
  57. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/overlay.py +0 -0
  58. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/page.py +0 -0
  59. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/performance.py +0 -0
  60. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/performance_timeline.py +0 -0
  61. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/preload.py +0 -0
  62. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/profiler.py +0 -0
  63. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/pwa.py +0 -0
  64. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/py.typed +0 -0
  65. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/runtime.py +0 -0
  66. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/schema.py +0 -0
  67. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/security.py +0 -0
  68. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/service_worker.py +0 -0
  69. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
  70. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/storage.py +0 -0
  71. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/system_info.py +0 -0
  72. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/target.py +0 -0
  73. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/tethering.py +0 -0
  74. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/tracing.py +0 -0
  75. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/util.py +0 -0
  76. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/web_audio.py +0 -0
  77. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/web_authn.py +0 -0
  78. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/cdp/web_mcp.py +0 -0
  79. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/__init__.py +0 -0
  80. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/_case.py +0 -0
  81. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/_element.py +0 -0
  82. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/_image_cap.py +0 -0
  83. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/_transport.py +0 -0
  84. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/ax.py +0 -0
  85. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/browser.py +0 -0
  86. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/cdp.py +0 -0
  87. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/chrome.py +0 -0
  88. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/cleanup.py +0 -0
  89. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/config.py +0 -0
  90. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/connection.py +0 -0
  91. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/cookies.py +0 -0
  92. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/cookies_import.py +0 -0
  93. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/dialog.py +0 -0
  94. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/download.py +0 -0
  95. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/elements.py +0 -0
  96. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/human.py +0 -0
  97. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/login.py +0 -0
  98. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/mouse.py +0 -0
  99. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/navigation.py +0 -0
  100. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/overlays.py +0 -0
  101. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/page.py +0 -0
  102. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/port.py +0 -0
  103. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/process.py +0 -0
  104. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/snapshot.py +0 -0
  105. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/storage.py +0 -0
  106. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/tabs.py +0 -0
  107. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/text_match.py +0 -0
  108. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/core/window.py +0 -0
  109. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/pool/__init__.py +0 -0
  110. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/pool/job.py +0 -0
  111. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/pool/persistence.py +0 -0
  112. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/pool/pool.py +0 -0
  113. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/pool/worker.py +0 -0
  114. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/profile.py +0 -0
  115. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/py.typed +0 -0
  116. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/__init__.py +0 -0
  117. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/_generate.py +0 -0
  118. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/browser_list.py +0 -0
  119. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/browser_start.py +0 -0
  120. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/browser_stop.py +0 -0
  121. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cdp_send.py +0 -0
  122. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/click_by_html_id.py +0 -0
  123. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/click_by_ref.py +0 -0
  124. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/click_by_text.py +0 -0
  125. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/click_by_xpath.py +0 -0
  126. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cookies_extract.py +0 -0
  127. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cookies_import.py +0 -0
  128. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cookies_list.py +0 -0
  129. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cookies_load.py +0 -0
  130. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/cookies_save.py +0 -0
  131. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/dialog_respond.py +0 -0
  132. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/download.py +0 -0
  133. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/drag_by_ref.py +0 -0
  134. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/find_by_html_id.py +0 -0
  135. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/find_by_text.py +0 -0
  136. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/find_by_xpath.py +0 -0
  137. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/focus_by_ref.py +0 -0
  138. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
  139. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/hover_by_ref.py +0 -0
  140. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/html_by_ref.py +0 -0
  141. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/js_evaluate.py +0 -0
  142. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/login_interactive.py +0 -0
  143. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/mouse_click.py +0 -0
  144. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/mouse_drag.py +0 -0
  145. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/mouse_move.py +0 -0
  146. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_discover.py +0 -0
  147. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
  148. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_goto.py +0 -0
  149. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_html.py +0 -0
  150. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_info.py +0 -0
  151. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_pdf.py +0 -0
  152. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_reload.py +0 -0
  153. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_screenshot.py +0 -0
  154. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_scroll.py +0 -0
  155. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_wait_element.py +0 -0
  156. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_wait_ready.py +0 -0
  157. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/page_wait_url.py +0 -0
  158. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
  159. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/select_by_ref.py +0 -0
  160. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/storage_get.py +0 -0
  161. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/storage_set.py +0 -0
  162. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/tab_close.py +0 -0
  163. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/tab_list.py +0 -0
  164. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/tab_new.py +0 -0
  165. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/tab_switch.py +0 -0
  166. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/type_by_ref.py +0 -0
  167. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/type_by_text.py +0 -0
  168. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/upload_by_ref.py +0 -0
  169. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser/tools/window_set.py +0 -0
  170. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
  171. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/requires.txt +0 -0
  172. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/ai_dev_browser.egg-info/top_level.txt +0 -0
  173. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/pyproject.toml +0 -0
  174. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/scripts/sync_cdp.py +0 -0
  175. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/setup.cfg +0 -0
  176. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/__init__.py +0 -0
  177. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/conftest.py +0 -0
  178. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/__init__.py +0 -0
  179. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/scenarios_workspace.json +0 -0
  180. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_ci_integration.py +0 -0
  181. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_cli_unicode_help.py +0 -0
  182. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_cookies_extract_windows.py +0 -0
  183. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_cookies_import.py +0 -0
  184. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_dialog_respond.py +0 -0
  185. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_find_and_interact_workflows.py +0 -0
  186. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_image_cap.py +0 -0
  187. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_locator_workflows.py +0 -0
  188. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_multi_profile_pool.py +0 -0
  189. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_page_reload.py +0 -0
  190. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_port_detection.py +0 -0
  191. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_startup_timeout.py +0 -0
  192. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_timeout_and_retry.py +0 -0
  193. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/integration/test_workspace_workflows.py +0 -0
  194. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/unit/test_case.py +0 -0
  195. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/unit/test_cli_param_type.py +0 -0
  196. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/tests/unit/test_no_acronym_rot.py +0 -0
  197. {ai_dev_browser-0.12.2 → ai_dev_browser-0.12.4}/uv.lock +0 -0
@@ -71,7 +71,8 @@ jobs:
71
71
  tests/integration/test_ci_integration.py \
72
72
  tests/integration/test_cookies_extract_windows.py \
73
73
  tests/integration/test_cli_unicode_help.py \
74
- tests/integration/test_image_cap.py
74
+ tests/integration/test_image_cap.py \
75
+ tests/integration/test_scroll_embedded_target.py
75
76
 
76
77
  # ─────────────────────────────────────────────────────────────────────
77
78
  # CI-integration dogfood: prove the documented "consumer's CI" recipe
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-dev-browser
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: A browser for AI to develop web automation — human-like automation that works seamlessly in a world designed for humans
5
5
  Author: sudoprivacy
6
6
  License: AGPL-3.0
@@ -27,6 +27,26 @@ if typing.TYPE_CHECKING:
27
27
 
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
+ # CDP JSON-RPC error code for "method not found" — sent by the
31
+ # target when a domain method the client called isn't implemented
32
+ # there. Embedded CDP targets (Electron, CEF, packaged Chromium
33
+ # exposing --remote-debugging-port) commonly return this on
34
+ # Chrome-browser-specific methods like Browser.getWindowForTarget or
35
+ # Input.synthesizeScrollGesture. Anything else (protocol violation,
36
+ # malformed params, target crash) must not be silently downgraded.
37
+ _CDP_METHOD_NOT_FOUND = -32601
38
+
39
+
40
+ def _is_cdp_method_not_found(exc: ProtocolException) -> bool:
41
+ """Robust match: prefer the numeric code from the CDP error dict;
42
+ fall back to the human-readable variants for older transports
43
+ that surface the error as a bare string."""
44
+ err = exc.args[0] if exc.args else None
45
+ if isinstance(err, dict) and err.get("code") == _CDP_METHOD_NOT_FOUND:
46
+ return True
47
+ text = str(exc)
48
+ return str(_CDP_METHOD_NOT_FOUND) in text or "wasn't found" in text
49
+
30
50
 
31
51
  class Tab:
32
52
  """CDP tab connection."""
@@ -493,36 +513,54 @@ class Tab:
493
513
  # Scroll
494
514
  # =========================================================================
495
515
 
516
+ async def _scroll_by_viewport_percent(self, percent: float) -> None:
517
+ """Scroll by `percent` of viewport height (positive = down,
518
+ negative = up).
519
+
520
+ Prefers Chrome's gesture-based path
521
+ (`Browser.getWindowForTarget` for bounds +
522
+ `Input.synthesizeScrollGesture` for a smooth swipe) so scroll
523
+ looks human-like — the gesture-shape acceleration + easing
524
+ matter for anti-bot heuristics that flag instant jumps.
525
+
526
+ Falls back to `Runtime.evaluate` + `window.scrollBy` only
527
+ when the gesture path returns CDP -32601 "method not found".
528
+ That signals an embedded target (Electron, CEF, packaged
529
+ Chromium exposing CDP via `--remote-debugging-port`) that
530
+ doesn't implement Browser or synthesizeScrollGesture. On
531
+ the fallback path, scroll events still fire so lazy-load /
532
+ IntersectionObserver / scroll-listener triggers still work —
533
+ we just lose the gesture-shape (which the embedded target
534
+ can't observe anyway).
535
+ """
536
+ try:
537
+ _window_id, bounds = await self.get_window()
538
+ await self.send(
539
+ cdp_input.synthesize_scroll_gesture(
540
+ x=0,
541
+ y=0,
542
+ y_distance=-(bounds.height * (percent / 100)),
543
+ y_overscroll=0,
544
+ x_overscroll=0,
545
+ prevent_fling=True,
546
+ repeat_delay_ms=0,
547
+ speed=7777,
548
+ )
549
+ )
550
+ except ProtocolException as e:
551
+ if not _is_cdp_method_not_found(e):
552
+ raise
553
+ await self.evaluate(
554
+ f"window.scrollBy(0, window.innerHeight * {percent / 100})"
555
+ )
556
+
496
557
  async def scroll_down(self, amount: int = 25):
497
558
  """Scroll down by percentage of viewport height."""
498
- _window_id, bounds = await self.get_window()
499
- await self.send(
500
- cdp_input.synthesize_scroll_gesture(
501
- x=0,
502
- y=0,
503
- y_distance=-(bounds.height * (amount / 100)),
504
- y_overscroll=0,
505
- x_overscroll=0,
506
- prevent_fling=True,
507
- repeat_delay_ms=0,
508
- speed=7777,
509
- )
510
- )
559
+ await self._scroll_by_viewport_percent(amount)
511
560
 
512
561
  async def scroll_up(self, amount: int = 25):
513
562
  """Scroll up by percentage of viewport height."""
514
- _window_id, bounds = await self.get_window()
515
- await self.send(
516
- cdp_input.synthesize_scroll_gesture(
517
- x=0,
518
- y=0,
519
- y_distance=(bounds.height * (amount / 100)),
520
- x_overscroll=0,
521
- prevent_fling=True,
522
- repeat_delay_ms=0,
523
- speed=7777,
524
- )
525
- )
563
+ await self._scroll_by_viewport_percent(-amount)
526
564
 
527
565
  # =========================================================================
528
566
  # Navigation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-dev-browser
3
- Version: 0.12.2
3
+ Version: 0.12.4
4
4
  Summary: A browser for AI to develop web automation — human-like automation that works seamlessly in a world designed for humans
5
5
  Author: sudoprivacy
6
6
  License: AGPL-3.0
@@ -185,6 +185,7 @@ tests/integration/test_locator_workflows.py
185
185
  tests/integration/test_multi_profile_pool.py
186
186
  tests/integration/test_page_reload.py
187
187
  tests/integration/test_port_detection.py
188
+ tests/integration/test_scroll_embedded_target.py
188
189
  tests/integration/test_startup_timeout.py
189
190
  tests/integration/test_timeout_and_retry.py
190
191
  tests/integration/test_workspace_workflows.py
@@ -175,6 +175,7 @@
175
175
  "tests/integration/test_cookies_import.py",
176
176
  "tests/integration/test_multi_profile_pool.py",
177
177
  "tests/integration/test_port_detection.py",
178
+ "tests/integration/test_scroll_embedded_target.py",
178
179
  "tests/integration/test_startup_timeout.py",
179
180
  "tests/integration/test_image_cap.py",
180
181
  "tests/integration/test_locator_workflows.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.12.4",
3
+ "distance": 0,
4
+ "node": "ge3c868e0ce9ed21d51ae89082631b88c41004162",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-02"
8
+ }
@@ -0,0 +1,211 @@
1
+ """Regression: page_scroll must work on embedded CDP targets that
2
+ don't implement Chrome's `Browser` domain (Electron / CEF / packaged
3
+ Chromium apps exposing CDP via `--remote-debugging-port`).
4
+
5
+ Pre-fix, `page_scroll(direction="down")` walked:
6
+ page_scroll → tab.scroll_down → tab.get_window (Browser.getWindowForTarget)
7
+
8
+ That CDP method is Chrome-browser-specific; embedded targets return
9
+ `-32601 Method not found` and the whole scroll path aborts. Reported
10
+ by a downstream integrator attaching to a packaged Electron app.
11
+
12
+ Fix (in `_tab._scroll_by_viewport_percent`): try the Chrome
13
+ gesture path first (`get_window` + `synthesize_scroll_gesture` —
14
+ still the preferred path because gesture-shape acceleration helps
15
+ evade anti-bot heuristics that flag instant scroll jumps). If that
16
+ returns CDP `-32601 method not found`, fall back to
17
+ `Runtime.evaluate` + `window.scrollBy` — Runtime is universally
18
+ supported. Any OTHER ProtocolException still surfaces so real
19
+ protocol violations aren't silently downgraded to the fallback.
20
+
21
+ Tests below pin all three contracts:
22
+ 1. Positive — scroll_down actually scrolls a real Chrome fixture
23
+ via the gesture path (proves it wasn't broken by the fallback
24
+ plumbing).
25
+ 2. Fallback — scroll_down succeeds when `get_window` is patched
26
+ to raise `-32601 method not found` (proves the embedded-target
27
+ path works).
28
+ 3. Guardrail — a NON-32601 ProtocolException must NOT be
29
+ downgraded to fallback (proves we didn't paper over real
30
+ protocol bugs).
31
+
32
+ Tests 2 + 3 are the load-bearing pair — without them a future
33
+ "cleanup" could either reintroduce `get_window()` in the fallback
34
+ path (re-breaking Electron users) or widen the except-clause to
35
+ swallow all protocol errors (masking real bugs), and regular Chrome
36
+ CI would go green for both regressions.
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import base64
42
+ import contextlib
43
+ import os
44
+
45
+ import pytest
46
+
47
+ from ai_dev_browser.core import page_goto, page_scroll
48
+ from ai_dev_browser.core.browser import browser_start, browser_stop
49
+ from ai_dev_browser.core.connection import connect_browser, get_active_tab
50
+
51
+ SKIP_INTEGRATION = os.environ.get("SKIP_INTEGRATION", "").lower() in (
52
+ "1",
53
+ "true",
54
+ "yes",
55
+ )
56
+
57
+
58
+ @pytest.fixture(autouse=True)
59
+ def _integration_guard():
60
+ if SKIP_INTEGRATION:
61
+ pytest.skip("SKIP_INTEGRATION is set")
62
+
63
+
64
+ @pytest.fixture
65
+ async def tab():
66
+ """Headless Chrome loaded with a tall data:URL so scrollY has
67
+ room to change. Deliberately >5x viewport so a 25% scroll is a
68
+ detectable delta."""
69
+ result = browser_start(headless=True, temp=True, reuse="none")
70
+ assert "error" not in result, f"browser_start failed: {result}"
71
+ port = result["port"]
72
+ browser_client = None
73
+ try:
74
+ browser_client = await connect_browser(port=port)
75
+ the_tab = await get_active_tab(browser_client)
76
+ html = (
77
+ "<html><body style='margin:0'>"
78
+ + "".join(
79
+ f"<div style='height:200px;background:hsl({i * 37 % 360},60%,50%);"
80
+ f"color:white;padding:20px;font-size:22px'>Row {i}</div>"
81
+ for i in range(30)
82
+ )
83
+ + "</body></html>"
84
+ )
85
+ data_url = "data:text/html;base64," + base64.b64encode(html.encode()).decode()
86
+ await page_goto(the_tab, data_url)
87
+ yield the_tab
88
+ finally:
89
+ if browser_client is not None:
90
+ with contextlib.suppress(Exception):
91
+ await browser_client.close()
92
+ with contextlib.suppress(Exception):
93
+ browser_stop(port=port)
94
+
95
+
96
+ async def _get_scroll_y(tab) -> float:
97
+ """Read window.scrollY through the same Runtime.evaluate channel
98
+ the fix uses — deep-serialization deserialization of a numeric
99
+ RemoteObject varies, so pull via .value from the eval envelope."""
100
+ result = await tab.evaluate("window.scrollY", return_by_value=True)
101
+ return float(result if result is not None else 0)
102
+
103
+
104
+ async def test_page_scroll_down_actually_scrolls(tab):
105
+ """Positive: real headless Chrome + tall fixture → scroll_down
106
+ must observably move window.scrollY forward. Baseline that proves
107
+ the JS-based rewrite works at all before we go asserting Electron
108
+ compat."""
109
+ before = await _get_scroll_y(tab)
110
+ assert before == 0, f"fixture should start at scrollY=0, got {before}"
111
+
112
+ ok = await page_scroll(tab, direction="down", amount=50)
113
+ assert ok is True
114
+
115
+ after = await _get_scroll_y(tab)
116
+ assert after > before, (
117
+ f"scroll_down(50%) did not move scrollY: before={before}, after={after}"
118
+ )
119
+
120
+
121
+ async def test_page_scroll_up_after_down_reverses(tab):
122
+ """Positive: scroll_up unwinds a prior scroll_down. Two-step
123
+ verifies the sign convention (positive percent = down / content
124
+ moves up) is preserved after the rewrite — a reversed sign would
125
+ slip past 'did it scroll?' asserts."""
126
+ await page_scroll(tab, direction="down", amount=50)
127
+ peak = await _get_scroll_y(tab)
128
+ assert peak > 0
129
+
130
+ await page_scroll(tab, direction="up", amount=50)
131
+ after_up = await _get_scroll_y(tab)
132
+ assert after_up < peak, (
133
+ f"scroll_up did not reduce scrollY: peak={peak}, after_up={after_up}"
134
+ )
135
+
136
+
137
+ async def test_page_scroll_falls_back_on_cdp_method_not_found(tab, monkeypatch):
138
+ """The Electron path: `get_window` raises CDP -32601 'method not
139
+ found' → scroll must fall back to `window.scrollBy` via
140
+ Runtime.evaluate and still scroll.
141
+
142
+ Simulating the failure mode with the actual `ProtocolException`
143
+ shape the CDP transport produces (dict with `code: -32601`), not
144
+ a generic Exception — otherwise a future edit narrowing the
145
+ except-clause to only catch method-not-found would break this
146
+ test and force a re-think, which is exactly the point."""
147
+ from ai_dev_browser.core._transport import ProtocolException
148
+
149
+ async def _boom():
150
+ raise ProtocolException(
151
+ {"code": -32601, "message": "'Browser.getWindowForTarget' wasn't found"}
152
+ )
153
+
154
+ monkeypatch.setattr(tab, "get_window", _boom)
155
+
156
+ before = await _get_scroll_y(tab)
157
+ ok = await page_scroll(tab, direction="down", amount=50)
158
+ assert ok is True
159
+
160
+ after = await _get_scroll_y(tab)
161
+ assert after > before, (
162
+ f"scroll_down did not fall back to JS scroll on -32601: "
163
+ f"before={before}, after={after}"
164
+ )
165
+
166
+
167
+ async def test_page_scroll_does_not_swallow_other_protocol_errors(tab, monkeypatch):
168
+ """Guardrail: only -32601 method-not-found triggers fallback. A
169
+ different CDP error (say a target crash, code -32000, or protocol
170
+ violation) must NOT be silently downgraded to the JS path —
171
+ that would mask real bugs in the transport / gesture pipeline.
172
+
173
+ Without this test, a maintainer could widen the except clause to
174
+ `except ProtocolException:` (catch-all) and Chrome CI would go
175
+ green, but any transport-layer failure would silently succeed
176
+ with a JS fallback — losing observability of a real bug."""
177
+ from ai_dev_browser.core._transport import ProtocolException
178
+
179
+ async def _boom():
180
+ # -32000 is a generic server error, NOT method-not-found.
181
+ raise ProtocolException(
182
+ {"code": -32000, "message": "Target crashed while loading window bounds"}
183
+ )
184
+
185
+ monkeypatch.setattr(tab, "get_window", _boom)
186
+
187
+ with pytest.raises(ProtocolException) as ei:
188
+ await page_scroll(tab, direction="down", amount=50)
189
+ assert ei.value.args[0].get("code") == -32000, (
190
+ f"expected -32000 to propagate, got: {ei.value.args}"
191
+ )
192
+
193
+
194
+ async def test_page_scroll_to_bottom_survives_broken_get_window(tab, monkeypatch):
195
+ """The other two page_scroll branches (to_bottom / to_top) already
196
+ used tab.evaluate, so they should already work on embedded
197
+ targets. This test pins that assumption so a future refactor
198
+ doesn't route them through get_window()."""
199
+
200
+ async def _boom():
201
+ raise RuntimeError("simulated Electron: Browser domain unavailable")
202
+
203
+ monkeypatch.setattr(tab, "get_window", _boom)
204
+
205
+ ok = await page_scroll(tab, to_bottom=True)
206
+ assert ok is True
207
+
208
+ after = await _get_scroll_y(tab)
209
+ assert after > 500, (
210
+ f"to_bottom should scroll a 6000px page substantially; got scrollY={after}"
211
+ )
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.12.2",
3
- "distance": 0,
4
- "node": "g6ef249945f324f1963fe8c9513bbf6acd6f022cd",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-07-01"
8
- }
File without changes
File without changes