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.
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/PKG-INFO +5 -3
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/README.md +2 -2
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/__init__.py +2 -1
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/_cli.py +2 -2
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/__init__.py +4 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/browser.py +16 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/chrome.py +32 -1
- ai_dev_browser-0.9.2/ai_dev_browser/core/cleanup.py +149 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/_generate.py +2 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/PKG-INFO +5 -3
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/SOURCES.txt +1 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/requires.txt +3 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/pyproject.toml +3 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.github/workflows/ci.yml +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.github/workflows/publish.yml +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.gitignore +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.gitmodules +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.pre-commit-config.yaml +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/.secrets.baseline +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/LICENSE +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/_version.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/__init__.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/accessibility.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/animation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/audits.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/autofill.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/background_service.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/browser.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/cache_storage.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/cast.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/console.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/crash_report_context.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/css.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/debugger.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/device_access.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/device_orientation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_debugger.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/dom_storage.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/emulation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/extensions.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/fed_cm.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/fetch.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/file_system.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/headless_experimental.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/heap_profiler.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/indexed_db.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/input_.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/inspector.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/io.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/layer_tree.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/log.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/media.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/memory.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/network.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/overlay.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/page.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/performance.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/performance_timeline.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/preload.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/profiler.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/pwa.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/py.typed +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/runtime.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/schema.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/security.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/service_worker.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/storage.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/system_info.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/target.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/tethering.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/tracing.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/util.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_audio.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_authn.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/cdp/web_mcp.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_case.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_element.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_tab.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/_transport.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/ax.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/cdp.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/config.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/connection.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/cookies.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/dialog.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/download.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/elements.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/human.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/login.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/mouse.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/navigation.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/overlays.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/page.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/port.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/process.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/snapshot.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/storage.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/tabs.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/text_match.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/core/window.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/__init__.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/job.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/persistence.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/pool.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/pool/worker.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/profile.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/py.typed +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/__init__.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_list.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_start.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/browser_stop.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cdp_send.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_html_id.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_text.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/click_by_xpath.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_list.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_load.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/cookies_save.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/dialog_respond.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/download.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/drag_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_html_id.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_text.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/find_by_xpath.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/focus_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/hover_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/html_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/js_evaluate.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/login_interactive.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_click.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_drag.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/mouse_move.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_discover.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_goto.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_html.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_info.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_reload.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_screenshot.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_scroll.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_element.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_ready.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/page_wait_url.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/select_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/storage_get.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/storage_set.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_close.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_list.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_new.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/tab_switch.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/type_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/type_by_text.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/upload_by_ref.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser/tools/window_set.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/ai_dev_browser.egg-info/top_level.txt +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/scripts/sync_cdp.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/setup.cfg +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/__init__.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/conftest.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/__init__.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/scenarios_workspace.json +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_dialog_respond.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_find_and_interact_workflows.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_locator_workflows.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_multi_profile_pool.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_page_reload.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_startup_timeout.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_timeout_and_retry.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_workspace_workflows.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_case.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_cli_param_type.py +0 -0
- {ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/unit/test_no_acronym_rot.py +0 -0
- {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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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-
|
|
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-
|
|
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`,
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_dev_browser-0.9.0 → ai_dev_browser-0.9.2}/tests/integration/test_find_and_interact_workflows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|