ai-dev-browser 0.9.2__tar.gz → 0.9.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.
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/PKG-INFO +1 -1
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/browser.py +8 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/chrome.py +16 -2
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/pool/job.py +49 -1
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/pool/pool.py +30 -9
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser.egg-info/PKG-INFO +1 -1
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.github/workflows/ci.yml +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.github/workflows/publish.yml +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.gitignore +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.gitmodules +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.pre-commit-config.yaml +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/.secrets.baseline +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/LICENSE +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/README.md +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/_cli.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/_version.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/accessibility.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/animation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/audits.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/autofill.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/background_service.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/browser.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/cache_storage.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/cast.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/console.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/crash_report_context.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/css.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/debugger.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/device_access.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/device_orientation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/dom.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/dom_debugger.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/dom_storage.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/emulation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/extensions.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/fed_cm.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/fetch.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/file_system.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/headless_experimental.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/heap_profiler.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/indexed_db.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/input_.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/inspector.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/io.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/layer_tree.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/log.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/media.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/memory.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/network.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/overlay.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/page.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/performance.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/performance_timeline.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/preload.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/profiler.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/pwa.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/py.typed +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/runtime.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/schema.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/security.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/service_worker.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/storage.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/system_info.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/target.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/tethering.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/tracing.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/util.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/web_audio.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/web_authn.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/cdp/web_mcp.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/_case.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/_element.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/_tab.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/_transport.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/ax.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/cdp.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/cleanup.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/config.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/connection.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/cookies.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/dialog.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/download.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/elements.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/human.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/login.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/mouse.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/navigation.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/overlays.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/page.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/port.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/process.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/snapshot.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/storage.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/tabs.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/text_match.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/core/window.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/pool/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/pool/persistence.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/pool/worker.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/profile.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/py.typed +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/_generate.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/browser_list.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/browser_start.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/browser_stop.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/cdp_send.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/click_by_html_id.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/click_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/click_by_text.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/click_by_xpath.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/cookies_list.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/cookies_load.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/cookies_save.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/dialog_respond.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/download.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/drag_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/find_by_html_id.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/find_by_text.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/find_by_xpath.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/focus_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/hover_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/html_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/js_evaluate.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/login_interactive.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/mouse_click.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/mouse_drag.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/mouse_move.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_discover.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_goto.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_html.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_info.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_reload.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_screenshot.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_scroll.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_wait_element.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_wait_ready.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/page_wait_url.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/select_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/storage_get.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/storage_set.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/tab_close.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/tab_list.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/tab_new.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/tab_switch.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/type_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/type_by_text.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/upload_by_ref.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser/tools/window_set.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser.egg-info/SOURCES.txt +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser.egg-info/requires.txt +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/ai_dev_browser.egg-info/top_level.txt +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/pyproject.toml +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/scripts/sync_cdp.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/setup.cfg +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/conftest.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/__init__.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/scenarios_workspace.json +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_dialog_respond.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_find_and_interact_workflows.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_locator_workflows.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_multi_profile_pool.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_page_reload.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_startup_timeout.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_timeout_and_retry.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/integration/test_workspace_workflows.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/unit/test_case.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/unit/test_cli_param_type.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/tests/unit/test_no_acronym_rot.py +0 -0
- {ai_dev_browser-0.9.2 → ai_dev_browser-0.9.4}/uv.lock +0 -0
|
@@ -77,6 +77,7 @@ def browser_start(
|
|
|
77
77
|
startup_timeout: float = 30.0,
|
|
78
78
|
extra_args: list[str] | None = None,
|
|
79
79
|
disable_default_args: list[str] | None = None,
|
|
80
|
+
silent_stderr: bool = False,
|
|
80
81
|
) -> dict:
|
|
81
82
|
"""Start or reuse a browser instance.
|
|
82
83
|
|
|
@@ -106,6 +107,12 @@ def browser_start(
|
|
|
106
107
|
logic and what removing them costs (in particular,
|
|
107
108
|
`--enable-automation` removal makes the Chrome invisible to
|
|
108
109
|
`browser_list` workspace filter and `browser_cleanup`).
|
|
110
|
+
silent_stderr: If True, route Chrome's stderr to DEVNULL instead
|
|
111
|
+
of PIPE. Use in long-running / multi-agent scenarios where
|
|
112
|
+
you don't want Chrome's GPU/Crashpad/V8 subsystems filling
|
|
113
|
+
the pipe buffer. Trade-off: loses Chrome's exit-time stderr
|
|
114
|
+
in the error path (falls back to generic "Chrome exited
|
|
115
|
+
silently"). See `launch_chrome` docstring for details.
|
|
109
116
|
|
|
110
117
|
Returns:
|
|
111
118
|
dict with port, pid, headless, url, profile, reused, message
|
|
@@ -174,6 +181,7 @@ def browser_start(
|
|
|
174
181
|
user_data_dir=str(user_data_dir) if user_data_dir else None,
|
|
175
182
|
extra_args=extra_args,
|
|
176
183
|
disable_default_args=disable_default_args,
|
|
184
|
+
silent_stderr=silent_stderr,
|
|
177
185
|
)
|
|
178
186
|
|
|
179
187
|
# Wait for Chrome to bind its debug port.
|
|
@@ -131,6 +131,7 @@ def launch_chrome(
|
|
|
131
131
|
profile_prefix: str = DEFAULT_PROFILE_PREFIX,
|
|
132
132
|
extra_args: list[str] | None = None,
|
|
133
133
|
disable_default_args: list[str] | None = None,
|
|
134
|
+
silent_stderr: bool = False,
|
|
134
135
|
start_url: str = "about:blank",
|
|
135
136
|
disable_session_restore: bool = True,
|
|
136
137
|
disable_session_crashed_bubble: bool = True,
|
|
@@ -148,6 +149,18 @@ def launch_chrome(
|
|
|
148
149
|
user_data_dir: Custom user data directory. If None, creates a temp directory.
|
|
149
150
|
profile_prefix: Prefix for temp profile directory name
|
|
150
151
|
extra_args: Additional Chrome command-line arguments appended after defaults.
|
|
152
|
+
silent_stderr: If True, route Chrome's stderr to `subprocess.DEVNULL`
|
|
153
|
+
instead of `subprocess.PIPE`. Default False preserves current
|
|
154
|
+
behavior — `browser_start` captures Chrome's exit-time stderr
|
|
155
|
+
to surface in the "Chrome exited unexpectedly" error.
|
|
156
|
+
|
|
157
|
+
Set True in long-running multi-instance / multi-agent scenarios
|
|
158
|
+
where you don't need the diagnostic and want to avoid PIPE
|
|
159
|
+
buffer pressure from Chrome's GPU / Crashpad / V8 subsystems
|
|
160
|
+
(these subsystems ignore `--disable-logging` and emit
|
|
161
|
+
unconditional stderr). Trade-off: on a Chrome-exit-on-startup,
|
|
162
|
+
the error message falls back to the generic "Chrome exited
|
|
163
|
+
silently" wording with no captured detail.
|
|
151
164
|
disable_default_args: List of default Chrome flags to remove before launch.
|
|
152
165
|
Match is exact for `--flag` form and prefix-equality for `--flag=value`
|
|
153
166
|
form (e.g. `["--enable-automation"]` strips both). Use this when a
|
|
@@ -261,16 +274,17 @@ def launch_chrome(
|
|
|
261
274
|
# Use subprocess.Popen on all platforms
|
|
262
275
|
# On Unix, use start_new_session to detach from parent
|
|
263
276
|
# On Windows, CREATE_NEW_PROCESS_GROUP for similar isolation
|
|
277
|
+
stderr_target = subprocess.DEVNULL if silent_stderr else subprocess.PIPE
|
|
264
278
|
if platform.system() == "Windows":
|
|
265
279
|
popen_kwargs = {
|
|
266
280
|
"stdout": subprocess.DEVNULL,
|
|
267
|
-
"stderr":
|
|
281
|
+
"stderr": stderr_target,
|
|
268
282
|
"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP,
|
|
269
283
|
}
|
|
270
284
|
else:
|
|
271
285
|
popen_kwargs = {
|
|
272
286
|
"stdout": subprocess.DEVNULL,
|
|
273
|
-
"stderr":
|
|
287
|
+
"stderr": stderr_target,
|
|
274
288
|
"start_new_session": True,
|
|
275
289
|
}
|
|
276
290
|
|
|
@@ -96,7 +96,15 @@ class JobResult:
|
|
|
96
96
|
job_id: ID of the job this result belongs to
|
|
97
97
|
success: True if job completed without error
|
|
98
98
|
data: Return value from the client method (JSON-serializable)
|
|
99
|
-
error: Error message if job failed
|
|
99
|
+
error: Error message if job failed (from `str(exception)`)
|
|
100
|
+
error_type: Exception class name if job failed (e.g.
|
|
101
|
+
`"GrokRateLimitError"`). Lets callers branch on exception
|
|
102
|
+
type without string-matching `error`. None on success.
|
|
103
|
+
error_bases: List of exception base-class names from the mro,
|
|
104
|
+
excluding `object` (e.g.
|
|
105
|
+
`["RateLimitError", "RuntimeError", "Exception"]`). Lets
|
|
106
|
+
callers match superclasses when the concrete type is
|
|
107
|
+
domain-specific but the base is standard. Empty on success.
|
|
100
108
|
completed_at: Timestamp when job completed
|
|
101
109
|
worker_id: ID of the worker that executed the job
|
|
102
110
|
|
|
@@ -107,15 +115,51 @@ class JobResult:
|
|
|
107
115
|
data={"url": "https://example.com", "title": "Example"},
|
|
108
116
|
worker_id=0,
|
|
109
117
|
)
|
|
118
|
+
|
|
119
|
+
# Failure branching without text matching:
|
|
120
|
+
if not result.success:
|
|
121
|
+
if result.error_type == "GrokRateLimitError":
|
|
122
|
+
schedule_retry()
|
|
123
|
+
elif "TimeoutError" in result.error_bases:
|
|
124
|
+
...
|
|
110
125
|
"""
|
|
111
126
|
|
|
112
127
|
job_id: str
|
|
113
128
|
success: bool
|
|
114
129
|
data: Any = None
|
|
115
130
|
error: str | None = None
|
|
131
|
+
error_type: str | None = None
|
|
132
|
+
error_bases: list[str] = field(default_factory=list)
|
|
116
133
|
completed_at: datetime = field(default_factory=datetime.now)
|
|
117
134
|
worker_id: int | None = None
|
|
118
135
|
|
|
136
|
+
@classmethod
|
|
137
|
+
def from_exception(
|
|
138
|
+
cls,
|
|
139
|
+
job_id: str,
|
|
140
|
+
exc: BaseException,
|
|
141
|
+
worker_id: int | None = None,
|
|
142
|
+
) -> "JobResult":
|
|
143
|
+
"""Build a failure result capturing the exception's type + mro.
|
|
144
|
+
|
|
145
|
+
Use inside worker exception handlers so callers downstream can
|
|
146
|
+
branch on `error_type` / `error_bases` without parsing
|
|
147
|
+
`error` text.
|
|
148
|
+
"""
|
|
149
|
+
cls_chain = type(exc).__mro__
|
|
150
|
+
# mro ends with `object`; drop it. First element is the type
|
|
151
|
+
# itself, which we surface as `error_type`; the rest is bases.
|
|
152
|
+
error_type = cls_chain[0].__name__
|
|
153
|
+
error_bases = [c.__name__ for c in cls_chain[1:] if c is not object]
|
|
154
|
+
return cls(
|
|
155
|
+
job_id=job_id,
|
|
156
|
+
success=False,
|
|
157
|
+
error=str(exc),
|
|
158
|
+
error_type=error_type,
|
|
159
|
+
error_bases=error_bases,
|
|
160
|
+
worker_id=worker_id,
|
|
161
|
+
)
|
|
162
|
+
|
|
119
163
|
def to_dict(self) -> dict:
|
|
120
164
|
"""Serialize result for persistence."""
|
|
121
165
|
return {
|
|
@@ -123,6 +167,8 @@ class JobResult:
|
|
|
123
167
|
"success": self.success,
|
|
124
168
|
"data": self.data,
|
|
125
169
|
"error": self.error,
|
|
170
|
+
"error_type": self.error_type,
|
|
171
|
+
"error_bases": self.error_bases,
|
|
126
172
|
"completed_at": self.completed_at.isoformat(),
|
|
127
173
|
"worker_id": self.worker_id,
|
|
128
174
|
}
|
|
@@ -135,6 +181,8 @@ class JobResult:
|
|
|
135
181
|
success=data["success"],
|
|
136
182
|
data=data.get("data"),
|
|
137
183
|
error=data.get("error"),
|
|
184
|
+
error_type=data.get("error_type"),
|
|
185
|
+
error_bases=data.get("error_bases") or [],
|
|
138
186
|
completed_at=datetime.fromisoformat(data["completed_at"]),
|
|
139
187
|
worker_id=data.get("worker_id"),
|
|
140
188
|
)
|
|
@@ -697,7 +697,7 @@ class BrowserPool(Generic[ClientT]):
|
|
|
697
697
|
)
|
|
698
698
|
|
|
699
699
|
except Exception as e:
|
|
700
|
-
await self._handle_job_failure(worker, job, str(e))
|
|
700
|
+
await self._handle_job_failure(worker, job, str(e), exc=e)
|
|
701
701
|
|
|
702
702
|
finally:
|
|
703
703
|
worker.mark_idle()
|
|
@@ -709,8 +709,22 @@ class BrowserPool(Generic[ClientT]):
|
|
|
709
709
|
logger.error(f"Worker {worker.worker_id} loop error: {e}")
|
|
710
710
|
await asyncio.sleep(1)
|
|
711
711
|
|
|
712
|
-
async def _handle_job_failure(
|
|
713
|
-
|
|
712
|
+
async def _handle_job_failure(
|
|
713
|
+
self,
|
|
714
|
+
worker: Worker,
|
|
715
|
+
job: Job,
|
|
716
|
+
error: str,
|
|
717
|
+
*,
|
|
718
|
+
exc: BaseException | None = None,
|
|
719
|
+
) -> None:
|
|
720
|
+
"""Handle job failure - retry or mark as failed.
|
|
721
|
+
|
|
722
|
+
If `exc` is provided (exception caught in the worker loop), the
|
|
723
|
+
terminal `JobResult` captures `error_type` + `error_bases` so
|
|
724
|
+
callers can branch on exception type without parsing `error`.
|
|
725
|
+
Non-exception failures (e.g. `fail_condition` triggered) pass
|
|
726
|
+
only `error` and the type fields stay None / [].
|
|
727
|
+
"""
|
|
714
728
|
job.retries += 1
|
|
715
729
|
worker.stats.fail += 1
|
|
716
730
|
|
|
@@ -725,12 +739,19 @@ class BrowserPool(Generic[ClientT]):
|
|
|
725
739
|
f"({job.job_id[:8]}...), retry {job.retries}: {error}"
|
|
726
740
|
)
|
|
727
741
|
else:
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
742
|
+
if exc is not None:
|
|
743
|
+
result = JobResult.from_exception(
|
|
744
|
+
job_id=job.job_id,
|
|
745
|
+
exc=exc,
|
|
746
|
+
worker_id=worker.worker_id,
|
|
747
|
+
)
|
|
748
|
+
else:
|
|
749
|
+
result = JobResult(
|
|
750
|
+
job_id=job.job_id,
|
|
751
|
+
success=False,
|
|
752
|
+
error=error,
|
|
753
|
+
worker_id=worker.worker_id,
|
|
754
|
+
)
|
|
734
755
|
self._results[job.job_id] = result
|
|
735
756
|
self._pending_jobs.pop(job.job_id, None)
|
|
736
757
|
job.status = JobStatus.FAILED
|
|
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
|
|
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.2 → ai_dev_browser-0.9.4}/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
|