ai-dev-browser 0.9.0__tar.gz → 0.9.2__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 (182) hide show
  1. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/PKG-INFO +5 -3
  2. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/README.md +2 -2
  3. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/__init__.py +2 -1
  4. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/_cli.py +2 -2
  5. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/__init__.py +4 -0
  6. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/browser.py +16 -0
  7. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/chrome.py +32 -1
  8. ai_dev_browser-0.9.2/ai_dev_browser/core/cleanup.py +149 -0
  9. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/_generate.py +2 -0
  10. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/PKG-INFO +5 -3
  11. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/SOURCES.txt +1 -0
  12. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/requires.txt +3 -0
  13. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/pyproject.toml +3 -0
  14. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.github/workflows/ci.yml +0 -0
  15. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.github/workflows/publish.yml +0 -0
  16. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.gitignore +0 -0
  17. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.gitmodules +0 -0
  18. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.pre-commit-config.yaml +0 -0
  19. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.secrets.baseline +0 -0
  20. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/LICENSE +0 -0
  21. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/_version.py +0 -0
  22. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/__init__.py +0 -0
  23. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/accessibility.py +0 -0
  24. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/animation.py +0 -0
  25. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/audits.py +0 -0
  26. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/autofill.py +0 -0
  27. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/background_service.py +0 -0
  28. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
  29. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/browser.py +0 -0
  30. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/cache_storage.py +0 -0
  31. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/cast.py +0 -0
  32. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/console.py +0 -0
  33. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/crash_report_context.py +0 -0
  34. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/css.py +0 -0
  35. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/debugger.py +0 -0
  36. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/device_access.py +0 -0
  37. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/device_orientation.py +0 -0
  38. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom.py +0 -0
  39. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_debugger.py +0 -0
  40. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
  41. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_storage.py +0 -0
  42. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/emulation.py +0 -0
  43. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
  44. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/extensions.py +0 -0
  45. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/fed_cm.py +0 -0
  46. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/fetch.py +0 -0
  47. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/file_system.py +0 -0
  48. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/headless_experimental.py +0 -0
  49. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/heap_profiler.py +0 -0
  50. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/indexed_db.py +0 -0
  51. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/input_.py +0 -0
  52. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/inspector.py +0 -0
  53. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/io.py +0 -0
  54. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/layer_tree.py +0 -0
  55. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/log.py +0 -0
  56. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/media.py +0 -0
  57. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/memory.py +0 -0
  58. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/network.py +0 -0
  59. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/overlay.py +0 -0
  60. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/page.py +0 -0
  61. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/performance.py +0 -0
  62. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/performance_timeline.py +0 -0
  63. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/preload.py +0 -0
  64. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/profiler.py +0 -0
  65. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/pwa.py +0 -0
  66. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/py.typed +0 -0
  67. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/runtime.py +0 -0
  68. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/schema.py +0 -0
  69. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/security.py +0 -0
  70. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/service_worker.py +0 -0
  71. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
  72. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/storage.py +0 -0
  73. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/system_info.py +0 -0
  74. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/target.py +0 -0
  75. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/tethering.py +0 -0
  76. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/tracing.py +0 -0
  77. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/util.py +0 -0
  78. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_audio.py +0 -0
  79. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_authn.py +0 -0
  80. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_mcp.py +0 -0
  81. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_case.py +0 -0
  82. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_element.py +0 -0
  83. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_tab.py +0 -0
  84. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_transport.py +0 -0
  85. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/ax.py +0 -0
  86. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/cdp.py +0 -0
  87. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/config.py +0 -0
  88. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/connection.py +0 -0
  89. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/cookies.py +0 -0
  90. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/dialog.py +0 -0
  91. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/download.py +0 -0
  92. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/elements.py +0 -0
  93. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/human.py +0 -0
  94. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/login.py +0 -0
  95. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/mouse.py +0 -0
  96. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/navigation.py +0 -0
  97. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/overlays.py +0 -0
  98. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/page.py +0 -0
  99. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/port.py +0 -0
  100. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/process.py +0 -0
  101. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/snapshot.py +0 -0
  102. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/storage.py +0 -0
  103. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/tabs.py +0 -0
  104. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/text_match.py +0 -0
  105. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/window.py +0 -0
  106. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/__init__.py +0 -0
  107. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/job.py +0 -0
  108. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/persistence.py +0 -0
  109. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/pool.py +0 -0
  110. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/worker.py +0 -0
  111. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/profile.py +0 -0
  112. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/py.typed +0 -0
  113. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/__init__.py +0 -0
  114. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_list.py +0 -0
  115. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_start.py +0 -0
  116. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_stop.py +0 -0
  117. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cdp_send.py +0 -0
  118. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_html_id.py +0 -0
  119. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_ref.py +0 -0
  120. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_text.py +0 -0
  121. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_xpath.py +0 -0
  122. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_list.py +0 -0
  123. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_load.py +0 -0
  124. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_save.py +0 -0
  125. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/dialog_respond.py +0 -0
  126. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/download.py +0 -0
  127. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/drag_by_ref.py +0 -0
  128. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_html_id.py +0 -0
  129. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_text.py +0 -0
  130. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_xpath.py +0 -0
  131. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/focus_by_ref.py +0 -0
  132. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
  133. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/hover_by_ref.py +0 -0
  134. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/html_by_ref.py +0 -0
  135. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/js_evaluate.py +0 -0
  136. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/login_interactive.py +0 -0
  137. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_click.py +0 -0
  138. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_drag.py +0 -0
  139. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_move.py +0 -0
  140. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_discover.py +0 -0
  141. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
  142. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_goto.py +0 -0
  143. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_html.py +0 -0
  144. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_info.py +0 -0
  145. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_reload.py +0 -0
  146. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_screenshot.py +0 -0
  147. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_scroll.py +0 -0
  148. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_element.py +0 -0
  149. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_ready.py +0 -0
  150. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_url.py +0 -0
  151. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
  152. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/select_by_ref.py +0 -0
  153. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/storage_get.py +0 -0
  154. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/storage_set.py +0 -0
  155. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_close.py +0 -0
  156. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_list.py +0 -0
  157. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_new.py +0 -0
  158. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_switch.py +0 -0
  159. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/type_by_ref.py +0 -0
  160. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/type_by_text.py +0 -0
  161. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/upload_by_ref.py +0 -0
  162. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/window_set.py +0 -0
  163. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
  164. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/top_level.txt +0 -0
  165. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/scripts/sync_cdp.py +0 -0
  166. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/setup.cfg +0 -0
  167. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/__init__.py +0 -0
  168. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/conftest.py +0 -0
  169. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/__init__.py +0 -0
  170. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/scenarios_workspace.json +0 -0
  171. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_dialog_respond.py +0 -0
  172. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_find_and_interact_workflows.py +0 -0
  173. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_locator_workflows.py +0 -0
  174. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_multi_profile_pool.py +0 -0
  175. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_page_reload.py +0 -0
  176. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_startup_timeout.py +0 -0
  177. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_timeout_and_retry.py +0 -0
  178. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_workspace_workflows.py +0 -0
  179. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_case.py +0 -0
  180. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_cli_param_type.py +0 -0
  181. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_no_acronym_rot.py +0 -0
  182. {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-dev-browser
3
- Version: 0.9.0
3
+ Version: 0.9.2
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
@@ -29,6 +29,8 @@ Requires-Dist: pytest>=7.0; extra == "dev"
29
29
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
30
30
  Provides-Extra: fuzzy
31
31
  Requires-Dist: rapidfuzz>=3.0; extra == "fuzzy"
32
+ Provides-Extra: cleanup
33
+ Requires-Dist: psutil>=5.9; extra == "cleanup"
32
34
  Dynamic: license-file
33
35
 
34
36
  # ai-dev-browser
@@ -75,7 +77,7 @@ convenient:
75
77
 
76
78
  Because both paths are generated from a single source, parameter
77
79
  changes flow to both at once and can't drift. See
78
- [cli-args-ssot](https://github.com/sudoprivacy/cli-args-ssot) for the
80
+ [cli-steering-engineering](https://github.com/sudoprivacy/cli-steering-engineering) for the
79
81
  underlying decorator.
80
82
 
81
83
  Tools cover: navigation, element interaction, mouse, tabs, screenshots,
@@ -89,7 +91,7 @@ ls ai_dev_browser/tools/
89
91
 
90
92
  **One file per CLI command, by design.** Each CLI command gets its own
91
93
  `tools/<name>.py` (e.g. `tools/click_by_text.py`, `tools/click_by_xpath.py`).
92
- This deviates from cli-args-ssot's "domain-grouped subcommands" recommendation
94
+ This deviates from cli-steering-engineering's "domain-grouped subcommands" recommendation
93
95
  because our invocation is `python -m ai_dev_browser.tools.<name>` — one file
94
96
  1:1 maps to one CLI path with no extra subcommand layer. The
95
97
  `<verb>_by_<spec>` family (`click_by_text`, `click_by_ref`, `click_by_html_id`,
@@ -42,7 +42,7 @@ convenient:
42
42
 
43
43
  Because both paths are generated from a single source, parameter
44
44
  changes flow to both at once and can't drift. See
45
- [cli-args-ssot](https://github.com/sudoprivacy/cli-args-ssot) for the
45
+ [cli-steering-engineering](https://github.com/sudoprivacy/cli-steering-engineering) for the
46
46
  underlying decorator.
47
47
 
48
48
  Tools cover: navigation, element interaction, mouse, tabs, screenshots,
@@ -56,7 +56,7 @@ ls ai_dev_browser/tools/
56
56
 
57
57
  **One file per CLI command, by design.** Each CLI command gets its own
58
58
  `tools/<name>.py` (e.g. `tools/click_by_text.py`, `tools/click_by_xpath.py`).
59
- This deviates from cli-args-ssot's "domain-grouped subcommands" recommendation
59
+ This deviates from cli-steering-engineering's "domain-grouped subcommands" recommendation
60
60
  because our invocation is `python -m ai_dev_browser.tools.<name>` — one file
61
61
  1:1 maps to one CLI path with no extra subcommand layer. The
62
62
  `<verb>_by_<spec>` family (`click_by_text`, `click_by_ref`, `click_by_html_id`,
@@ -93,6 +93,7 @@ __all__ = [
93
93
  # Tools
94
94
  "browser_start",
95
95
  "browser_stop",
96
+ "browser_cleanup",
96
97
  "connect_browser",
97
98
  ]
98
99
 
@@ -102,4 +103,4 @@ from . import cdp
102
103
  from .core import connect_browser
103
104
 
104
105
  # Core functions also available at package level
105
- from .core import browser_start, browser_stop
106
+ from .core import browser_cleanup, browser_start, browser_stop
@@ -391,7 +391,7 @@ def wrap_core(core_func: Callable, result_key: str = "success") -> Callable:
391
391
  result = await core_func(*args, **kwargs)
392
392
  except Exception as e:
393
393
  # Verbatim message — Python `repr(e)` and CLI stdout stay in
394
- # lockstep (cli-args-ssot rule 7: never re-render error text
394
+ # lockstep (cli-steering-engineering rule 7: never re-render error text
395
395
  # in tool files).
396
396
  out: dict = {"error": str(e)}
397
397
  if failure_hint:
@@ -408,7 +408,7 @@ def wrap_core(core_func: Callable, result_key: str = "success") -> Callable:
408
408
  if isinstance(result, dict):
409
409
  filtered = _filter_dict_for_json(result)
410
410
  # Auto-inject failure hint when the tool reports failure via
411
- # result_key=False. Pairs with cli-args-ssot Rule 5a: failure
411
+ # result_key=False. Pairs with cli-steering-engineering Rule 5a: failure
412
412
  # steering goes through the return channel (100% reach at
413
413
  # invocation time), not docstring (only reaches on --help).
414
414
  if filtered.get(result_key) is False and failure_hint:
@@ -32,6 +32,9 @@ from .ax import (
32
32
  from .browser import browser_list, browser_start, browser_stop
33
33
  from .cdp import cdp_send
34
34
 
35
+ # Orphan profile cleanup (optional psutil dep)
36
+ from .cleanup import browser_cleanup
37
+
35
38
  # Chrome detection and launching
36
39
  from .chrome import find_chrome, launch_chrome
37
40
 
@@ -135,6 +138,7 @@ __all__ = [
135
138
  "browser_start",
136
139
  "browser_stop",
137
140
  "browser_list",
141
+ "browser_cleanup",
138
142
  # Config
139
143
  "DEFAULT_BASE_DIR",
140
144
  "DEFAULT_PROFILE_DIR",
@@ -75,6 +75,8 @@ def browser_start(
75
75
  temp: bool = False,
76
76
  reuse: ReuseStrategy = DEFAULT_REUSE_STRATEGY,
77
77
  startup_timeout: float = 30.0,
78
+ extra_args: list[str] | None = None,
79
+ disable_default_args: list[str] | None = None,
78
80
  ) -> dict:
79
81
  """Start or reuse a browser instance.
80
82
 
@@ -92,6 +94,18 @@ def browser_start(
92
94
  "started but port not listening" on a known-good environment;
93
95
  lower it (e.g. 5s) for headless CI where startup is fast and
94
96
  you want to fail loud quickly.
97
+ extra_args: Additional Chrome command-line flags appended after
98
+ the defaults. Plain passthrough to `launch_chrome`.
99
+ disable_default_args: Default Chrome flags to strip before launch.
100
+ Composable with `extra_args` so callers can fully control
101
+ launch flags without forking — e.g. anti-bot fingerprint
102
+ tuning passes `disable_default_args=["--enable-automation"]`
103
+ + `extra_args=["--disable-blink-features=AutomationControlled"]`.
104
+ See `launch_chrome` docstring for which defaults are
105
+ load-bearing for ai-dev-browser's own connection / discovery
106
+ logic and what removing them costs (in particular,
107
+ `--enable-automation` removal makes the Chrome invisible to
108
+ `browser_list` workspace filter and `browser_cleanup`).
95
109
 
96
110
  Returns:
97
111
  dict with port, pid, headless, url, profile, reused, message
@@ -158,6 +172,8 @@ def browser_start(
158
172
  headless=headless,
159
173
  start_url=start_url,
160
174
  user_data_dir=str(user_data_dir) if user_data_dir else None,
175
+ extra_args=extra_args,
176
+ disable_default_args=disable_default_args,
161
177
  )
162
178
 
163
179
  # Wait for Chrome to bind its debug port.
@@ -130,6 +130,7 @@ def launch_chrome(
130
130
  user_data_dir: str | Path | None = None,
131
131
  profile_prefix: str = DEFAULT_PROFILE_PREFIX,
132
132
  extra_args: list[str] | None = None,
133
+ disable_default_args: list[str] | None = None,
133
134
  start_url: str = "about:blank",
134
135
  disable_session_restore: bool = True,
135
136
  disable_session_crashed_bubble: bool = True,
@@ -146,7 +147,25 @@ def launch_chrome(
146
147
  headless: Run in headless mode (default: False)
147
148
  user_data_dir: Custom user data directory. If None, creates a temp directory.
148
149
  profile_prefix: Prefix for temp profile directory name
149
- extra_args: Additional Chrome command-line arguments
150
+ extra_args: Additional Chrome command-line arguments appended after defaults.
151
+ disable_default_args: List of default Chrome flags to remove before launch.
152
+ Match is exact for `--flag` form and prefix-equality for `--flag=value`
153
+ form (e.g. `["--enable-automation"]` strips both). Use this when a
154
+ default flag conflicts with what you want — most common case is
155
+ `["--enable-automation"]` for anti-bot fingerprint scenarios where
156
+ sites detect automation Chrome via that flag's side effects.
157
+
158
+ **Load-bearing defaults — do NOT remove these or ai-dev-browser's
159
+ connection / discovery logic breaks**:
160
+ - `--remote-debugging-port=...` — CDP transport
161
+ - `--user-data-dir=...` — profile isolation + workspace tagging
162
+ - `--remote-allow-origins=*` — CDP origin check
163
+ - `--enable-automation` — required for `Browser.getBrowserCommandLine`
164
+ cmdline readback. Removing it makes this Chrome **invisible to
165
+ `browser_list` workspace filter and `browser_cleanup` orphan
166
+ detection** (caller must manage the port themselves). That's
167
+ often acceptable in stealth / private-flow scenarios; just be
168
+ aware of the trade-off.
150
169
  start_url: Initial URL to open (default: "about:blank" for clean state)
151
170
  disable_session_restore: Prevent Chrome from restoring previous tabs (default: True).
152
171
  Sets restore_on_startup=5 in Preferences file.
@@ -218,6 +237,18 @@ def launch_chrome(
218
237
  if headless:
219
238
  args.append("--headless=new")
220
239
 
240
+ # Filter out defaults the caller explicitly disabled. Match exact OR
241
+ # `<prefix>=...` form so callers can disable both `--enable-automation`
242
+ # and `--user-data-dir=anything` with a single entry.
243
+ if disable_default_args:
244
+ disable_set = set(disable_default_args)
245
+ args = [
246
+ a
247
+ for a in args
248
+ if a not in disable_set
249
+ and not any(a.startswith(d + "=") for d in disable_set)
250
+ ]
251
+
221
252
  if extra_args:
222
253
  args.extend(extra_args)
223
254
 
@@ -0,0 +1,149 @@
1
+ """Orphan Chrome cleanup for ai-dev-browser-managed profiles.
2
+
3
+ Long sessions with `close_chrome=False` accumulate Chrome processes
4
+ (main + helper/renderer children) holding `--user-data-dir` locks on
5
+ our managed profile directories. When the next `browser_start` tries
6
+ to launch a fresh Chrome on the same profile, the lockfile contention
7
+ causes the new spawn to fail. `browser_cleanup` scans for and kills
8
+ the orphans.
9
+
10
+ `psutil` is the only reliable cross-platform way to read every
11
+ chrome.exe's command line; we make it an optional extra so users who
12
+ never need cleanup don't pay the install cost.
13
+ """
14
+
15
+ import logging
16
+ import os
17
+ from pathlib import Path
18
+
19
+ from .config import (
20
+ DEFAULT_PROFILE_DIR,
21
+ DEFAULT_PROFILE_PREFIX,
22
+ get_workspace_profile_dir,
23
+ )
24
+ from .port import _query_chrome_cmdline, find_debug_chromes
25
+ from .process import _kill_process_tree
26
+
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ def _normalize_path(path: str) -> str:
32
+ """Normalize a path for comparison (resolves separators + Windows case)."""
33
+ return os.path.normcase(os.path.normpath(path))
34
+
35
+
36
+ def _is_managed_profile(user_data_dir_normalized: str) -> bool:
37
+ """True iff the user_data_dir is inside our managed namespace.
38
+
39
+ Two namespaces:
40
+ - Workspace profiles: under `DEFAULT_PROFILE_DIR`
41
+ (`~/.ai-dev-browser/profiles/...`)
42
+ - Temp profiles: directory name starts with `DEFAULT_PROFILE_PREFIX`
43
+ (`ai_dev_browser_<port>` in the system temp dir)
44
+ """
45
+ workspace_root = _normalize_path(str(DEFAULT_PROFILE_DIR))
46
+ if user_data_dir_normalized.startswith(workspace_root):
47
+ return True
48
+ name = Path(user_data_dir_normalized).name
49
+ return name.startswith(DEFAULT_PROFILE_PREFIX)
50
+
51
+
52
+ def browser_cleanup(profile: str | None = None) -> dict:
53
+ """Use when: long sessions with `close_chrome=False` accumulated
54
+ orphan chrome.exe processes that hold workspace profile dir locks,
55
+ blocking new `browser_start` calls (typical symptom on Windows:
56
+ `browser_start` errors with "started but port not listening" even
57
+ after raising `startup_timeout`).
58
+
59
+ Scans every chrome.exe whose `--user-data-dir` argument points
60
+ into our managed profile namespace, then kills the process tree
61
+ of any whose user-data-dir has no live debug-ready Chrome owner.
62
+ Live Chromes (visible to `find_debug_chromes`) are left alone.
63
+
64
+ Returns `{killed, count, profile_dirs}` so you can verify what was
65
+ cleaned. Safe by construction — never touches Chrome processes
66
+ outside `~/.ai-dev-browser/profiles/...` or the temp profile
67
+ prefix `ai_dev_browser_*`.
68
+
69
+ Args:
70
+ profile: Workspace profile name to scope cleanup to. None →
71
+ all managed profiles for the current workspace + any
72
+ temp profiles in our namespace.
73
+
74
+ Returns:
75
+ dict with `killed` (list of PIDs), `count` (len of killed),
76
+ and `profile_dirs` (sorted list of user-data-dirs that had
77
+ orphans cleaned).
78
+
79
+ Failure:
80
+ Requires the `cleanup` extra for `psutil`. Install via:
81
+ `pip install 'ai-dev-browser[cleanup]'`. After install, retry.
82
+ """
83
+ try:
84
+ import psutil
85
+ except ImportError as e:
86
+ raise ImportError(
87
+ "browser_cleanup requires psutil. Install via: "
88
+ "pip install 'ai-dev-browser[cleanup]'"
89
+ ) from e
90
+
91
+ # 1. Snapshot user_data_dirs of currently live debug-ready Chromes.
92
+ # These are NOT orphans — their lifecycle is owned by whoever
93
+ # launched them, and killing them would surprise the caller.
94
+ alive_dirs: set[str] = set()
95
+ for port, _pid, _ws in find_debug_chromes():
96
+ cmdline = _query_chrome_cmdline(port)
97
+ if not cmdline:
98
+ continue
99
+ for arg in cmdline:
100
+ if arg.startswith("--user-data-dir="):
101
+ alive_dirs.add(_normalize_path(arg.split("=", 1)[1]))
102
+ break
103
+
104
+ # 2. Optional single-profile filter.
105
+ profile_filter: str | None = None
106
+ if profile:
107
+ profile_filter = _normalize_path(str(get_workspace_profile_dir(profile)))
108
+
109
+ # 3. Enumerate chrome processes; kill orphans whose --user-data-dir
110
+ # is in our namespace and has no live debug owner.
111
+ killed: list[int] = []
112
+ profile_dirs_cleaned: set[str] = set()
113
+
114
+ for proc in psutil.process_iter(["pid", "name", "cmdline"]):
115
+ try:
116
+ name = (proc.info.get("name") or "").lower()
117
+ if "chrome" not in name:
118
+ continue
119
+ cmdline = proc.info.get("cmdline") or []
120
+ udd: str | None = None
121
+ for arg in cmdline:
122
+ if arg.startswith("--user-data-dir="):
123
+ udd = _normalize_path(arg.split("=", 1)[1])
124
+ break
125
+ if not udd:
126
+ # Helper / renderer subprocess inherits user-data-dir
127
+ # implicitly — _kill_process_tree on the main parent
128
+ # below catches these. Skip standalone matching here.
129
+ continue
130
+ if not _is_managed_profile(udd):
131
+ continue
132
+ if profile_filter and udd != profile_filter:
133
+ continue
134
+ if udd in alive_dirs:
135
+ continue # debug-ready owner exists, not an orphan
136
+
137
+ pid = proc.info["pid"]
138
+ if _kill_process_tree(pid):
139
+ killed.append(pid)
140
+ profile_dirs_cleaned.add(udd)
141
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
142
+ # Process vanished mid-iteration or we lack rights — skip.
143
+ continue
144
+
145
+ return {
146
+ "killed": killed,
147
+ "count": len(killed),
148
+ "profile_dirs": sorted(profile_dirs_cleaned),
149
+ }
@@ -26,6 +26,8 @@ INTERNAL = {
26
26
  "graceful_close_browser",
27
27
  "connect_browser",
28
28
  "get_active_tab",
29
+ # destructive — Python-only API; not exposed to LLM tool discovery
30
+ "browser_cleanup",
29
31
  }
30
32
 
31
33
  # Per-tool metadata overrides. Key = function name, value = dict of overrides.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-dev-browser
3
- Version: 0.9.0
3
+ Version: 0.9.2
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
@@ -29,6 +29,8 @@ Requires-Dist: pytest>=7.0; extra == "dev"
29
29
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
30
30
  Provides-Extra: fuzzy
31
31
  Requires-Dist: rapidfuzz>=3.0; extra == "fuzzy"
32
+ Provides-Extra: cleanup
33
+ Requires-Dist: psutil>=5.9; extra == "cleanup"
32
34
  Dynamic: license-file
33
35
 
34
36
  # ai-dev-browser
@@ -75,7 +77,7 @@ convenient:
75
77
 
76
78
  Because both paths are generated from a single source, parameter
77
79
  changes flow to both at once and can't drift. See
78
- [cli-args-ssot](https://github.com/sudoprivacy/cli-args-ssot) for the
80
+ [cli-steering-engineering](https://github.com/sudoprivacy/cli-steering-engineering) for the
79
81
  underlying decorator.
80
82
 
81
83
  Tools cover: navigation, element interaction, mouse, tabs, screenshots,
@@ -89,7 +91,7 @@ ls ai_dev_browser/tools/
89
91
 
90
92
  **One file per CLI command, by design.** Each CLI command gets its own
91
93
  `tools/<name>.py` (e.g. `tools/click_by_text.py`, `tools/click_by_xpath.py`).
92
- This deviates from cli-args-ssot's "domain-grouped subcommands" recommendation
94
+ This deviates from cli-steering-engineering's "domain-grouped subcommands" recommendation
93
95
  because our invocation is `python -m ai_dev_browser.tools.<name>` — one file
94
96
  1:1 maps to one CLI path with no extra subcommand layer. The
95
97
  `<verb>_by_<spec>` family (`click_by_text`, `click_by_ref`, `click_by_html_id`,
@@ -86,6 +86,7 @@ ai_dev_browser/core/ax.py
86
86
  ai_dev_browser/core/browser.py
87
87
  ai_dev_browser/core/cdp.py
88
88
  ai_dev_browser/core/chrome.py
89
+ ai_dev_browser/core/cleanup.py
89
90
  ai_dev_browser/core/config.py
90
91
  ai_dev_browser/core/connection.py
91
92
  ai_dev_browser/core/cookies.py
@@ -3,6 +3,9 @@ websocket-client>=1.0
3
3
  Pillow>=10.0
4
4
  deprecated>=1.2
5
5
 
6
+ [cleanup]
7
+ psutil>=5.9
8
+
6
9
  [dev]
7
10
  pytest>=7.0
8
11
  pytest-asyncio>=0.21
@@ -40,6 +40,9 @@ dev = [
40
40
  fuzzy = [
41
41
  "rapidfuzz>=3.0", # Fast C-based Levenshtein for fuzzy_find/fuzzy_click
42
42
  ]
43
+ cleanup = [
44
+ "psutil>=5.9", # For browser_cleanup orphan-process scan
45
+ ]
43
46
 
44
47
  [project.urls]
45
48
  Homepage = "https://github.com/sudoprivacy/ai-dev-browser"
File without changes
File without changes
File without changes