ai-dev-browser 0.58.2__tar.gz → 0.58.3__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.58.2 → ai_dev_browser-0.58.3}/.github/workflows/ci.yml +8 -3
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/PKG-INFO +1 -1
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/ext_bridge.py +1 -1
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/port.py +56 -15
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/background.js +3 -2
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/manifest.json +1 -1
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/page_view.js +2 -2
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/PKG-INFO +1 -1
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/SOURCES.txt +3 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/scm_file_list.json +3 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/docs/remote-views.md +22 -4
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/conftest.py +20 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/extension_ownership_support.py +18 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/live_cleanup.py +5 -2
- ai_dev_browser-0.58.3/tests/integration/live_resources.py +58 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/navigation_startup_support.py +52 -1
- ai_dev_browser-0.58.3/tests/integration/test_discovery_socket_pressure.py +108 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_remote_selection_live.py +159 -38
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_remote_view.py +3 -1
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/extension_ownership.test.cjs +23 -0
- ai_dev_browser-0.58.3/tests/unit/test_discovery_budget.py +47 -0
- ai_dev_browser-0.58.3/tests/unit/test_live_cleanup.py +135 -0
- ai_dev_browser-0.58.2/tests/unit/test_live_cleanup.py +0 -65
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.dockerignore +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.gitattributes +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.github/workflows/publish.yml +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.gitignore +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.gitmodules +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.pre-commit-config.yaml +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/.secrets.baseline +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/LICENSE +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/README.md +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/_cli.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/_version.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/accessibility.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/animation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/audits.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/autofill.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/background_service.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/browser.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/cache_storage.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/cast.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/console.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/crash_report_context.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/css.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/debugger.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/device_access.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/device_orientation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/dom.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/dom_debugger.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/dom_storage.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/emulation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/extensions.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/fed_cm.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/fetch.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/file_system.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/headless_experimental.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/heap_profiler.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/indexed_db.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/input_.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/inspector.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/io.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/layer_tree.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/log.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/media.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/memory.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/network.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/overlay.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/page.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/performance.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/performance_timeline.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/preload.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/profiler.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/pwa.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/py.typed +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/runtime.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/schema.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/security.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/service_worker.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/storage.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/system_info.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/target.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/tethering.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/tracing.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/util.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/web_audio.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/web_authn.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/cdp/web_mcp.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_browser_guidance.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_case.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_demo.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_element.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_extension_lifecycle.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_image_cap.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_js.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_recorder.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_session.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_tab.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/_transport.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/ax.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/browser.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/cdp.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/chrome.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/cleanup.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/config.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/connection.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/cookies.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/cookies_import.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/dialog.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/download.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/elements.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/errors.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/extension.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/human.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/identity.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/login.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/mouse.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/navigation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/overlays.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/page.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/process.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/recording.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/registry.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/snapshot.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/storage.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/tabs.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/text_match.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/core/window.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/README.md +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/icon128.png +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/icon16.png +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/icon48.png +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/extension/managed-schema.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/pool/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/pool/job.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/pool/persistence.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/pool/pool.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/pool/worker.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/profile.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/py.typed +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/cdp.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/control.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/existing.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/files.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/mcp-tools.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/page.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/profile_lease.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/provision.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/session.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/vnc.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/window.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/remote/worker.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/_generate.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_cleanup.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_connect.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_disconnect.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_list.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_reconnect.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_restart.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_start.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/browser_stop.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cdp_send.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/click_by_html_id.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/click_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/click_by_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/click_by_xpath.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/click_row_by_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cookies_extract_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cookies_extract_offline.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cookies_import.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cookies_load.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/cookies_save.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/dialog_respond.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/download.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/download_link.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/drag_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/find_by_html_id.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/find_by_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/find_by_xpath.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/focus_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/hover_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/html_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/js_evaluate.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/login_interactive.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/mouse_click.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/mouse_drag.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/mouse_move.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_discover.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_goto.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_html.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_info.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_pdf.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_record_start.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_record_stop.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_reload.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_screenshot.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_scroll.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_wait_element.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_wait_ready.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/page_wait_url.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/press_key.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/select_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/select_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/storage_get.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/storage_set.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/tab_close.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/tab_list.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/tab_new.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/tab_switch.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/type_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/type_by_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/upload_by_ref.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser/tools/window_set.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/requires.txt +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/ai_dev_browser.egg-info/top_level.txt +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/deploy/managed-chrome/Dockerfile +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/deploy/managed-chrome/configure_extension.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/docs/BR-real-profile-hidden-input-resolution.md +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/docs/navigation-ownership.md +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/pyproject.toml +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/scripts/build_release_assets.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/scripts/live_browser_lifecycle_steering.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/scripts/live_grok_pool_persistence.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/scripts/live_recording_steering.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/scripts/sync_cdp.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/setup.cfg +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/conftest.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/__init__.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/browser_lifecycle_acceptance.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/browser_lifecycle_journeys.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/browser_lifecycle_support.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/browser_lifecycle_ui.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/cli_steering_acceptance.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/cli_steering_support.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/extension_window_support.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/native_window_occluder.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/pdf_readiness_support.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/pool-persistence-acceptance.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/recording_demo_acceptance.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/recording_demo_support.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/run_managed_extension.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_browser_lifecycle.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_cli_extension.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_cli_steering.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_extension_ownership.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_extension_ownership_native.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_extension_window.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_extension_window_previous.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_navigation_startup.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_pdf_readiness.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_public_web.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_recording.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_recording_demo.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/scenarios_workspace.json +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_background_processes.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_control_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_files_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_inventory.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_lifecycle.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_session_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_start_retry.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_browser_worker_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cdp_send_warning.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_ci_integration.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cli_extension_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cli_steering.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cli_unicode_help.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_click_robust.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_config_resolvers.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cookies_extract_hermetic.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cookies_extract_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cookies_extract_windows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_cookies_import.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_dialog_auto.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_dialog_respond.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_dom_discovery.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_download_link.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_download_link_frame.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_existing_worker_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_ext_bridge.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_extension_ownership.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_extension_ownership_native.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_extension_window_native.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_extension_window_previous.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_find_and_interact_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_image_cap.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_js_contract.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_js_evaluate_frame.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_locator_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_mouse_click.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_mouse_drag_move.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_multi_profile_pool.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_navigation_startup.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_discover.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_goto.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_pdf.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_reload.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_scroll_verify.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_page_wait_element.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_pdf_readiness.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_pool_model_persistence.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_port_detection.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_press_key.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_proxy_identity.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_public_web_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_recording_demo.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_recording_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_remote_vnc.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_remote_window_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_screenshot_coords.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_scroll_embedded_target.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_select_text.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_startup_timeout.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_stealth_defaults.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_tab_close.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_tab_url_matching.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_text_locator.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_timeout_and_retry.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_trusted_click.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_type_verified_fill.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_viewport.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_worker_managed_status_live.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/test_workspace_workflows.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_browser_control.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_build_info.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_case.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cdp_coerce.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cdp_session_scoped.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cdp_unbound.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cli_docstring_args.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cli_error_envelope.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cli_param_type.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_cli_steering_contract.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_demo_encoding.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_extension_ownership.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_find_hint.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_launch_window_size.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_lifecycle_handshake_errors.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_no_acronym_rot.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_pdf_units.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_recording.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_release_version.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_remote_files.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_remote_navigation.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_session_failure_state.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_tool_registry.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_window_input_contract.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/unit/test_worker_lifecycle_status.py +0 -0
- {ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/uv.lock +0 -0
|
@@ -167,13 +167,13 @@ jobs:
|
|
|
167
167
|
env:
|
|
168
168
|
AI_DEV_BROWSER_CHROME: ${{ steps.chrome.outputs.chrome-path }}
|
|
169
169
|
AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
|
|
170
|
-
run: xvfb-run -a -s "-screen 0 1280x1000x24" python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_remote_selection_live.py tests/integration/test_remote_vnc.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
|
|
170
|
+
run: xvfb-run -a -s "-screen 0 1280x1000x24" python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_discovery_socket_pressure.py tests/integration/test_remote_selection_live.py tests/integration/test_remote_vnc.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
|
|
171
171
|
- name: Run page and extension workflows on Windows
|
|
172
172
|
if: runner.os == 'Windows'
|
|
173
173
|
env:
|
|
174
174
|
AI_DEV_BROWSER_CHROME: ${{ steps.chrome.outputs.chrome-path }}
|
|
175
175
|
AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
|
|
176
|
-
run: python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_remote_selection_live.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
|
|
176
|
+
run: python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_discovery_socket_pressure.py tests/integration/test_remote_selection_live.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
|
|
177
177
|
- name: Retain real viewer evidence
|
|
178
178
|
if: always()
|
|
179
179
|
uses: actions/upload-artifact@v4
|
|
@@ -193,7 +193,7 @@ jobs:
|
|
|
193
193
|
run: docker build -f deploy/managed-chrome/Dockerfile --secret "id=extension_key,src=$RUNNER_TEMP/extension-fixture.pem" --build-arg "SOURCE_REVISION=$GITHUB_SHA" -t adb-managed-chrome:ci .
|
|
194
194
|
- name: Run real signed extension, handoff and viewer workflows
|
|
195
195
|
run: |
|
|
196
|
-
docker create --name "adb-managed-live-$GITHUB_RUN_ID" --memory=1536m --shm-size=256m --pids-limit=384 \
|
|
196
|
+
docker create --init --name "adb-managed-live-$GITHUB_RUN_ID" --memory=1536m --shm-size=256m --pids-limit=384 \
|
|
197
197
|
--mount "type=bind,source=$PWD/tests,target=/opt/browser/tests,readonly" \
|
|
198
198
|
adb-managed-chrome:ci /bin/sleep infinity
|
|
199
199
|
docker start "adb-managed-live-$GITHUB_RUN_ID"
|
|
@@ -301,11 +301,16 @@ jobs:
|
|
|
301
301
|
extension-results/**/installed-extension.png
|
|
302
302
|
extension-results/**/extension-startup-evidence.json
|
|
303
303
|
extension-results/**/extension-chrome.log
|
|
304
|
+
extension-results/**/host-resources-*.json
|
|
305
|
+
extension-results/**/navigation-readiness.json
|
|
304
306
|
extension-results/**/extension-cleanup-stacks.txt
|
|
305
307
|
native-ownership-results/**
|
|
306
308
|
native-window-results/**/native-window-evidence.json
|
|
307
309
|
native-window-results/**/native-window-accepted.png
|
|
308
310
|
native-window-results/**/extension-chrome.log
|
|
311
|
+
native-window-results/**/host-resources-*.json
|
|
312
|
+
native-window-results/**/extension-cleanup-evidence.json
|
|
313
|
+
native-window-results/**/extension-cleanup-stacks.txt
|
|
309
314
|
previous-extension-results/**/previous-extension-evidence.json
|
|
310
315
|
|
|
311
316
|
# ─────────────────────────────────────────────────────────────────────
|
|
@@ -440,7 +440,7 @@ def _launch_page(connection_or_path, request_or_headers):
|
|
|
440
440
|
path = connection_or_path if legacy else request_or_headers.path
|
|
441
441
|
if not re.fullmatch(r"/adb-launch/[a-f0-9]{32}", path):
|
|
442
442
|
return None
|
|
443
|
-
body = "<!doctype html><meta charset=utf-8><title>
|
|
443
|
+
body = "<!doctype html><meta charset=utf-8><title>AI Dev Browser</title><p>This window is reserved for your browser task.</p>"
|
|
444
444
|
if legacy:
|
|
445
445
|
return (
|
|
446
446
|
HTTPStatus.OK,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Port management for Chrome debugging.
|
|
2
2
|
|
|
3
|
-
Discovery uses
|
|
4
|
-
1.
|
|
3
|
+
Discovery uses the OS listener table + CDP (no process enumeration):
|
|
4
|
+
1. Read listening ports, with bounded probes on unsupported hosts
|
|
5
5
|
2. Hit /json/version to confirm Chrome debug port
|
|
6
6
|
3. CDP Browser.getBrowserCommandLine() to read workspace tag
|
|
7
7
|
|
|
@@ -11,6 +11,7 @@ Example:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import ipaddress
|
|
14
|
+
import errno
|
|
14
15
|
import json
|
|
15
16
|
import logging
|
|
16
17
|
import os
|
|
@@ -146,7 +147,10 @@ def _query_chrome_version(
|
|
|
146
147
|
|
|
147
148
|
|
|
148
149
|
def _guid_from_version(info: dict) -> str | None:
|
|
149
|
-
|
|
150
|
+
url = info.get("webSocketDebuggerUrl")
|
|
151
|
+
if not isinstance(url, str):
|
|
152
|
+
return None
|
|
153
|
+
m = _BROWSER_GUID_RE.search(url)
|
|
150
154
|
return m.group(1) if m else None
|
|
151
155
|
|
|
152
156
|
|
|
@@ -174,9 +178,13 @@ def _resolve_instance(
|
|
|
174
178
|
so the CDP cmdline is populated) or by an older build.
|
|
175
179
|
"""
|
|
176
180
|
info = _query_chrome_version(port, host, timeout)
|
|
177
|
-
if info
|
|
181
|
+
if not isinstance(info, dict):
|
|
178
182
|
return None
|
|
179
183
|
guid = _guid_from_version(info)
|
|
184
|
+
if not guid:
|
|
185
|
+
# An unrelated local HTTP service can also return JSON at this path.
|
|
186
|
+
# It has no browser identity and must not become a CDP candidate.
|
|
187
|
+
return None
|
|
180
188
|
|
|
181
189
|
from . import registry
|
|
182
190
|
|
|
@@ -454,32 +462,65 @@ def _fast_listening_check(
|
|
|
454
462
|
return False
|
|
455
463
|
|
|
456
464
|
|
|
465
|
+
def _listening_ports(port_range: tuple[int, int]) -> list[int] | None:
|
|
466
|
+
"""Read local IPv4 listeners without opening sockets to every candidate.
|
|
467
|
+
|
|
468
|
+
Some hosts do not expose their socket table to ordinary users. Retain the
|
|
469
|
+
existing probe path there, without changing process privileges. Resource
|
|
470
|
+
exhaustion must propagate instead of triggering more socket allocation.
|
|
471
|
+
"""
|
|
472
|
+
import psutil
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
connections = psutil.net_connections(kind="tcp4")
|
|
476
|
+
except OSError as error:
|
|
477
|
+
if (
|
|
478
|
+
error.errno in (errno.ENOBUFS, errno.ENOMEM, errno.EMFILE, errno.ENFILE)
|
|
479
|
+
or getattr(error, "winerror", None) == 10055
|
|
480
|
+
):
|
|
481
|
+
raise
|
|
482
|
+
return None
|
|
483
|
+
except (psutil.Error, NotImplementedError):
|
|
484
|
+
return None
|
|
485
|
+
return sorted(
|
|
486
|
+
{
|
|
487
|
+
connection.laddr.port
|
|
488
|
+
for connection in connections
|
|
489
|
+
if connection.status == psutil.CONN_LISTEN
|
|
490
|
+
and connection.laddr
|
|
491
|
+
and connection.laddr.ip in (DEFAULT_DEBUG_HOST, "0.0.0.0")
|
|
492
|
+
and port_range[0] <= connection.laddr.port < port_range[1]
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
|
|
457
497
|
def _scan_ports_for_chrome(
|
|
458
498
|
port_range: tuple[int, int],
|
|
459
|
-
listening_workers: int =
|
|
499
|
+
listening_workers: int = 16,
|
|
460
500
|
cdp_workers: int = 16,
|
|
461
501
|
cdp_timeout: float = 0.5,
|
|
462
502
|
) -> list[tuple[int, int | None, str | None]]:
|
|
463
503
|
"""Scan a port range for Chrome debug instances (parallelized).
|
|
464
504
|
|
|
465
505
|
Two stages:
|
|
466
|
-
1.
|
|
467
|
-
(bind-first + tight-timeout IPv4 connect, no IPv6)
|
|
506
|
+
1. OS listener inventory; bounded IPv4 probes only when unavailable
|
|
468
507
|
2. Parallel _resolve_instance() — verify each is Chrome (via
|
|
469
508
|
/json/version) and read its workspace from the instance registry
|
|
470
509
|
(GUID-matched), falling back to Browser.getBrowserCommandLine
|
|
471
510
|
|
|
472
|
-
|
|
473
|
-
|
|
511
|
+
Listener inventory avoids hundreds of probe threads and empty TCP
|
|
512
|
+
connections. The fallback keeps a bounded worker count.
|
|
474
513
|
"""
|
|
475
514
|
ports = list(range(port_range[0], port_range[1]))
|
|
476
515
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
516
|
+
listening = _listening_ports(port_range)
|
|
517
|
+
if listening is None:
|
|
518
|
+
with ThreadPoolExecutor(max_workers=listening_workers) as pool:
|
|
519
|
+
listening = [
|
|
520
|
+
p
|
|
521
|
+
for p, live in pool.map(lambda p: (p, _fast_listening_check(p)), ports)
|
|
522
|
+
if live
|
|
523
|
+
]
|
|
483
524
|
|
|
484
525
|
if not listening:
|
|
485
526
|
return []
|
|
@@ -243,7 +243,6 @@ async function ensureDedicatedTabSelected() {
|
|
|
243
243
|
if (marker) {
|
|
244
244
|
launchToken = launchMarker(marker);
|
|
245
245
|
await chrome.storage.session.set({ adbLaunchToken: launchToken });
|
|
246
|
-
await chrome.tabs.update(marker.id, { url: "about:blank" });
|
|
247
246
|
}
|
|
248
247
|
mainTabId = t.id;
|
|
249
248
|
claim(t.id);
|
|
@@ -300,7 +299,9 @@ function adoptLaunch(tab) {
|
|
|
300
299
|
await chrome.storage.session.set({ adbLaunchToken: token });
|
|
301
300
|
claim(tab.id, null, "launch");
|
|
302
301
|
await attachTab(tab.id);
|
|
303
|
-
|
|
302
|
+
// The nonce page already belongs to this launch. Keep it until the caller
|
|
303
|
+
// navigates: an extra startup navigation can invalidate Chrome's debugger
|
|
304
|
+
// target after the ownership handshake has announced readiness.
|
|
304
305
|
if (old != null && old !== tab.id && autoTabs.has(old)) {
|
|
305
306
|
const previous = await chrome.tabs.get(old).catch(() => null);
|
|
306
307
|
if (previous && previous.url === "about:blank") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "AI Dev Browser — bridge",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.5",
|
|
5
5
|
"storage": { "managed_schema": "managed-schema.json" },
|
|
6
6
|
"description": "Lets ai-dev-browser drive THIS Chrome (your real profile) via chrome.debugger over a local bridge. Opt-in; only active while ai-dev-browser is connected.",
|
|
7
7
|
"minimum_chrome_version": "116",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** A reusable pixel surface. The host owns authentication and control policy. */
|
|
2
2
|
export class PageSurface {
|
|
3
|
-
constructor(image, send, { drag = false } = {}) {
|
|
3
|
+
constructor(image, send, { drag = false, touchDrag = false } = {}) {
|
|
4
4
|
this.image = image;
|
|
5
5
|
this.send = send;
|
|
6
6
|
this.width = 0;
|
|
@@ -15,7 +15,7 @@ export class PageSurface {
|
|
|
15
15
|
const point = this.point(event);
|
|
16
16
|
if (!point) return;
|
|
17
17
|
this.pointer = { id: event.pointerId, lastY: event.clientY, startY: event.clientY, startX: event.clientX,
|
|
18
|
-
moved: false, drag: drag && event.pointerType !== 'touch', points: [point] };
|
|
18
|
+
moved: false, drag: drag && (event.pointerType !== 'touch' || touchDrag), points: [point] };
|
|
19
19
|
image.setPointerCapture(event.pointerId);
|
|
20
20
|
image.focus({ preventScroll: true });
|
|
21
21
|
});
|
|
@@ -238,6 +238,7 @@ tests/integration/conftest.py
|
|
|
238
238
|
tests/integration/extension_ownership_support.py
|
|
239
239
|
tests/integration/extension_window_support.py
|
|
240
240
|
tests/integration/live_cleanup.py
|
|
241
|
+
tests/integration/live_resources.py
|
|
241
242
|
tests/integration/native_window_occluder.py
|
|
242
243
|
tests/integration/navigation_startup_support.py
|
|
243
244
|
tests/integration/pdf_readiness_support.py
|
|
@@ -279,6 +280,7 @@ tests/integration/test_cookies_extract_windows.py
|
|
|
279
280
|
tests/integration/test_cookies_import.py
|
|
280
281
|
tests/integration/test_dialog_auto.py
|
|
281
282
|
tests/integration/test_dialog_respond.py
|
|
283
|
+
tests/integration/test_discovery_socket_pressure.py
|
|
282
284
|
tests/integration/test_dom_discovery.py
|
|
283
285
|
tests/integration/test_download_link.py
|
|
284
286
|
tests/integration/test_download_link_frame.py
|
|
@@ -341,6 +343,7 @@ tests/unit/test_cli_error_envelope.py
|
|
|
341
343
|
tests/unit/test_cli_param_type.py
|
|
342
344
|
tests/unit/test_cli_steering_contract.py
|
|
343
345
|
tests/unit/test_demo_encoding.py
|
|
346
|
+
tests/unit/test_discovery_budget.py
|
|
344
347
|
tests/unit/test_extension_ownership.py
|
|
345
348
|
tests/unit/test_find_hint.py
|
|
346
349
|
tests/unit/test_launch_window_size.py
|
|
@@ -233,6 +233,7 @@
|
|
|
233
233
|
"tests/integration/extension_ownership_support.py",
|
|
234
234
|
"tests/integration/extension_window_support.py",
|
|
235
235
|
"tests/integration/live_cleanup.py",
|
|
236
|
+
"tests/integration/live_resources.py",
|
|
236
237
|
"tests/integration/native_window_occluder.py",
|
|
237
238
|
"tests/integration/navigation_startup_support.py",
|
|
238
239
|
"tests/integration/pdf_readiness_support.py",
|
|
@@ -274,6 +275,7 @@
|
|
|
274
275
|
"tests/integration/test_cookies_import.py",
|
|
275
276
|
"tests/integration/test_dialog_auto.py",
|
|
276
277
|
"tests/integration/test_dialog_respond.py",
|
|
278
|
+
"tests/integration/test_discovery_socket_pressure.py",
|
|
277
279
|
"tests/integration/test_dom_discovery.py",
|
|
278
280
|
"tests/integration/test_download_link.py",
|
|
279
281
|
"tests/integration/test_download_link_frame.py",
|
|
@@ -336,6 +338,7 @@
|
|
|
336
338
|
"tests/unit/test_cli_param_type.py",
|
|
337
339
|
"tests/unit/test_cli_steering_contract.py",
|
|
338
340
|
"tests/unit/test_demo_encoding.py",
|
|
341
|
+
"tests/unit/test_discovery_budget.py",
|
|
339
342
|
"tests/unit/test_extension_ownership.py",
|
|
340
343
|
"tests/unit/test_find_hint.py",
|
|
341
344
|
"tests/unit/test_launch_window_size.py",
|
|
@@ -53,7 +53,17 @@ dialogs and supported keys. `PageSurface` is a rendering/input adapter, not a
|
|
|
53
53
|
complete tab bar, toolbar, WebSocket authorization protocol or React panel.
|
|
54
54
|
Hosts whose worker advertises `capabilities.dragInput` can opt into mouse/pen
|
|
55
55
|
selection with `new PageSurface(imageElement, sendAuthorizedInput, {drag: true})`.
|
|
56
|
-
Touch gestures
|
|
56
|
+
Touch gestures scroll by default. A host can offer an explicit "Select text or
|
|
57
|
+
drag" gesture mode with `{drag: true, touchDrag: true}`. A cancelled touch sends
|
|
58
|
+
no partial drag; switching away from a surface must dispose it before accepting
|
|
59
|
+
another gesture. The existing host lease and epoch checks still apply. This is
|
|
60
|
+
an input mode, not an ownership transfer or permission to resume AI. The real
|
|
61
|
+
Chrome touch-emulation journey scrolls out and back in the default mode, then
|
|
62
|
+
selects the exact phrase in drag mode through a separate scaled viewer. It also
|
|
63
|
+
checks cancellation before a completed gesture. Physical iOS/Android acceptance
|
|
64
|
+
remains a separate device requirement.
|
|
65
|
+
|
|
66
|
+
A drag carries at most 64 viewport points in
|
|
57
67
|
one bounded `input` operation; it is sent on pointer release, so this is not a
|
|
58
68
|
continuous drag stream. The host's existing input barrier drains the gesture
|
|
59
69
|
before changing control or targets. The adapter never retries it.
|
|
@@ -84,10 +94,11 @@ tool execution and viewer input.
|
|
|
84
94
|
## AI and human control
|
|
85
95
|
|
|
86
96
|
The agent communicates why human input is needed through its native question,
|
|
87
|
-
options or normal conversation. The host's existing
|
|
88
|
-
|
|
97
|
+
options or normal conversation. The host's existing authenticated interrupt
|
|
98
|
+
and continuation actions perform the actual ownership transition. A native answer is only
|
|
89
99
|
communication unless the host explicitly routes it to that authenticated action
|
|
90
|
-
and waits for the tool barrier. Do not
|
|
100
|
+
and waits for the tool barrier. Do not require separate browser business handoff
|
|
101
|
+
buttons or build an additional question UI into the
|
|
91
102
|
remote display adapter. Credentials are entered in the same browser by the
|
|
92
103
|
human, not sent through agent chat. The public MCP tool catalog and normal CLI
|
|
93
104
|
mode help include this guidance; execution barriers remain a host contract.
|
|
@@ -197,6 +208,11 @@ refuses arbitrary existing profiles. The host provides the policy/update server
|
|
|
197
208
|
and exclusive profile ownership; the SDK does not modify extension preferences
|
|
198
209
|
or install unpacked code into a user's browser.
|
|
199
210
|
|
|
211
|
+
An extension launch keeps its owned loopback handshake page until the caller
|
|
212
|
+
navigates. The handshake does not schedule an additional blank-page navigation:
|
|
213
|
+
that can invalidate Chrome's debugger target after readiness was announced.
|
|
214
|
+
An explicit launch URL still uses the normal caller-requested navigation.
|
|
215
|
+
|
|
200
216
|
### Managed Chrome image
|
|
201
217
|
|
|
202
218
|
`deploy/managed-chrome/Dockerfile` is the shared Linux runtime for consuming
|
|
@@ -217,6 +233,8 @@ X11 or VNC service by default; hosts provide their authorized session API and
|
|
|
217
233
|
start display resources when required. Chrome runs as UID 10001. Persist its
|
|
218
234
|
host-owned profile separately from the image, enforce one runtime owner, and
|
|
219
235
|
pin production images by digest after validating their recorded Chrome version.
|
|
236
|
+
Docker hosts must use `--init` or another child-reaping supervisor so exited
|
|
237
|
+
Chrome descendants do not accumulate against the runtime's process limit.
|
|
220
238
|
The `Managed branded Chrome` CI job uses an isolated fixture signing key and
|
|
221
239
|
real installed headless extension to exercise the same shared-session workflow
|
|
222
240
|
as CDP, including trusted input, handoff and stale-target rejection.
|
|
@@ -22,6 +22,26 @@ from ai_dev_browser.core.connection import BrowserClient
|
|
|
22
22
|
from ai_dev_browser.core.ext_bridge import _Bridge
|
|
23
23
|
from ai_dev_browser.core.extension import extension_dir
|
|
24
24
|
|
|
25
|
+
# Shared fixture visibility must not depend on which test module pytest collects
|
|
26
|
+
# before a module-level pytest_plugins declaration.
|
|
27
|
+
from .test_remote_view import remote_site as remote_site
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture(autouse=True)
|
|
31
|
+
def live_host_resource_receipts(request, tmp_path):
|
|
32
|
+
if "live_extension" not in request.fixturenames and not os.environ.get(
|
|
33
|
+
"AI_DEV_BROWSER_MANAGED_EXTENSION_PROFILE"
|
|
34
|
+
):
|
|
35
|
+
yield
|
|
36
|
+
return
|
|
37
|
+
from .live_resources import record_host_resources
|
|
38
|
+
|
|
39
|
+
record_host_resources(tmp_path, "before")
|
|
40
|
+
try:
|
|
41
|
+
yield
|
|
42
|
+
finally:
|
|
43
|
+
record_host_resources(tmp_path, "after")
|
|
44
|
+
|
|
25
45
|
|
|
26
46
|
@pytest.fixture
|
|
27
47
|
def lifecycle(tmp_path):
|
{ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/extension_ownership_support.py
RENAMED
|
@@ -97,6 +97,7 @@ async def cli(extension, folder, *arguments, code=0):
|
|
|
97
97
|
|
|
98
98
|
async def navigation_journey(extension, folder: Path, mode: str):
|
|
99
99
|
receipts = []
|
|
100
|
+
visits = []
|
|
100
101
|
url = ""
|
|
101
102
|
|
|
102
103
|
async def receipt(request):
|
|
@@ -104,6 +105,7 @@ async def navigation_journey(extension, folder: Path, mode: str):
|
|
|
104
105
|
return web.json_response({"accepted": True})
|
|
105
106
|
|
|
106
107
|
async def page(request):
|
|
108
|
+
visits.append(request.path)
|
|
107
109
|
destination = url.replace("127.0.0.1", "localhost") if mode != "same" else url
|
|
108
110
|
landing = destination + "/result?private=must-not-log#private-fragment"
|
|
109
111
|
action = f"location.href={json.dumps(landing)}"
|
|
@@ -138,6 +140,22 @@ await fetch('/receipt',{{method:'POST',headers:{{'Content-Type':'application/jso
|
|
|
138
140
|
try:
|
|
139
141
|
browser = await connect(extension)
|
|
140
142
|
parent = await browser.get(url)
|
|
143
|
+
readiness = await wait_until(
|
|
144
|
+
lambda: parent.evaluate(
|
|
145
|
+
"({protocol:location.protocol,path:location.pathname,"
|
|
146
|
+
"ready:document.readyState,hasSubmit:!!document.querySelector('#submit'),"
|
|
147
|
+
"errorCode:document.querySelector('#error-code')?.textContent||null})"
|
|
148
|
+
),
|
|
149
|
+
lambda value: value["protocol"] == "chrome-error:"
|
|
150
|
+
or (value["ready"] == "complete" and value["hasSubmit"]),
|
|
151
|
+
)
|
|
152
|
+
(folder / "navigation-readiness.json").write_text(
|
|
153
|
+
json.dumps(
|
|
154
|
+
{"mode": mode, "page": readiness, "fixtureVisits": visits}, indent=2
|
|
155
|
+
),
|
|
156
|
+
encoding="utf8",
|
|
157
|
+
)
|
|
158
|
+
assert readiness["protocol"] == "http:" and readiness["hasSubmit"], readiness
|
|
141
159
|
parent_id = str(parent.target_id)
|
|
142
160
|
sentinel = await worker_call(
|
|
143
161
|
extension,
|
|
@@ -47,6 +47,11 @@ async def close_live_extension(chrome, server, folder, timeout=30):
|
|
|
47
47
|
from ai_dev_browser.core.browser import browser_stop
|
|
48
48
|
|
|
49
49
|
try:
|
|
50
|
+
# Let the still-running extension acknowledge WebSocket close frames.
|
|
51
|
+
# Killing Chrome first resets its sockets on Windows and leaves the
|
|
52
|
+
# legacy server waiting for a peer that can no longer finish closing.
|
|
53
|
+
await close_live_server(server, folder)
|
|
54
|
+
finally:
|
|
50
55
|
if chrome and "port" in chrome:
|
|
51
56
|
record_phase(folder, "chrome-stop-start", pid=chrome["pid"])
|
|
52
57
|
try:
|
|
@@ -63,5 +68,3 @@ async def close_live_extension(chrome, server, folder, timeout=30):
|
|
|
63
68
|
) from exc
|
|
64
69
|
record_phase(folder, "chrome-stopped", result=result)
|
|
65
70
|
assert result.get("stopped") and result.get("count") == 1, result
|
|
66
|
-
finally:
|
|
67
|
-
await close_live_server(server, folder)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Retain host resource counts, never process arguments or network addresses."""
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import psutil
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def record_host_resources(folder, phase):
|
|
13
|
+
snapshot = {"phase": phase, "at": datetime.now(timezone.utc).isoformat()}
|
|
14
|
+
try:
|
|
15
|
+
current = psutil.Process()
|
|
16
|
+
memory = psutil.virtual_memory()
|
|
17
|
+
snapshot.update(
|
|
18
|
+
pythonRss=current.memory_info().rss,
|
|
19
|
+
pythonThreads=current.num_threads(),
|
|
20
|
+
hostAvailableMemory=memory.available,
|
|
21
|
+
hostTotalMemory=memory.total,
|
|
22
|
+
tcpStates=dict(
|
|
23
|
+
Counter(
|
|
24
|
+
connection.status for connection in psutil.net_connections("tcp4")
|
|
25
|
+
)
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
chrome = []
|
|
29
|
+
for process in psutil.process_iter(
|
|
30
|
+
["name", "num_threads", "memory_info"], ad_value=None
|
|
31
|
+
):
|
|
32
|
+
if "chrome" in (process.info["name"] or "").lower():
|
|
33
|
+
chrome.append(process.info)
|
|
34
|
+
snapshot.update(
|
|
35
|
+
chromeProcessCount=len(chrome),
|
|
36
|
+
chromeThreads=sum(value["num_threads"] or 0 for value in chrome),
|
|
37
|
+
chromeRss=sum(
|
|
38
|
+
value["memory_info"].rss for value in chrome if value["memory_info"]
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
if sys.platform == "linux":
|
|
42
|
+
snapshot["cgroup"] = {
|
|
43
|
+
name: path.read_text().strip()
|
|
44
|
+
for name in (
|
|
45
|
+
"pids.current",
|
|
46
|
+
"pids.max",
|
|
47
|
+
"pids.events",
|
|
48
|
+
"memory.current",
|
|
49
|
+
"memory.max",
|
|
50
|
+
"memory.events",
|
|
51
|
+
)
|
|
52
|
+
if (path := Path("/sys/fs/cgroup") / name).is_file()
|
|
53
|
+
}
|
|
54
|
+
except (psutil.Error, OSError) as error:
|
|
55
|
+
snapshot["auditUnavailable"] = type(error).__name__
|
|
56
|
+
(folder / f"host-resources-{phase}.json").write_text(
|
|
57
|
+
json.dumps(snapshot, indent=2), encoding="utf8"
|
|
58
|
+
)
|
{ai_dev_browser-0.58.2 → ai_dev_browser-0.58.3}/tests/integration/navigation_startup_support.py
RENAMED
|
@@ -9,16 +9,23 @@ import pytest
|
|
|
9
9
|
from aiohttp import web
|
|
10
10
|
|
|
11
11
|
from ai_dev_browser.core.browser import browser_start, browser_stop
|
|
12
|
-
from tests.integration.test_remote_view import
|
|
12
|
+
from tests.integration.test_remote_view import (
|
|
13
|
+
connect_page,
|
|
14
|
+
navigate_owned_browser,
|
|
15
|
+
wait_for,
|
|
16
|
+
)
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
async def startup_race_journey(folder, accepted=True):
|
|
16
20
|
first_request = asyncio.Event()
|
|
17
21
|
release_first = asyncio.Event()
|
|
18
22
|
requests = []
|
|
23
|
+
bootstrap_requests = []
|
|
24
|
+
phases = []
|
|
19
25
|
|
|
20
26
|
async def page(request):
|
|
21
27
|
requests.append(request.path)
|
|
28
|
+
phases.append({"phase": "target-request", "count": len(requests)})
|
|
22
29
|
if len(requests) == 1:
|
|
23
30
|
first_request.set()
|
|
24
31
|
await release_first.wait()
|
|
@@ -31,7 +38,18 @@ async def startup_race_journey(folder, accepted=True):
|
|
|
31
38
|
app = web.Application()
|
|
32
39
|
app.router.add_get("/target", page)
|
|
33
40
|
|
|
41
|
+
async def bootstrap(request):
|
|
42
|
+
bootstrap_requests.append(request.path)
|
|
43
|
+
return web.Response(
|
|
44
|
+
text='<title>Race fixture ready</title><main id="bootstrap">Ready</main>',
|
|
45
|
+
content_type="text/html",
|
|
46
|
+
headers={"Cache-Control": "no-store"},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
app.router.add_get("/bootstrap", bootstrap)
|
|
50
|
+
|
|
34
51
|
async def redirect(request):
|
|
52
|
+
phases.append({"phase": "competing-redirect"})
|
|
35
53
|
raise web.HTTPFound("/target" if accepted else "/wrong")
|
|
36
54
|
|
|
37
55
|
app.router.add_get("/redirect", redirect)
|
|
@@ -47,6 +65,7 @@ async def startup_race_journey(folder, accepted=True):
|
|
|
47
65
|
site = web.TCPSite(runner, "127.0.0.1", 0)
|
|
48
66
|
await site.start()
|
|
49
67
|
url = f"http://127.0.0.1:{site._server.sockets[0].getsockname()[1]}/target"
|
|
68
|
+
bootstrap_url = url.replace("/target", "/bootstrap")
|
|
50
69
|
browser = None
|
|
51
70
|
navigation = None
|
|
52
71
|
try:
|
|
@@ -64,12 +83,32 @@ async def startup_race_journey(folder, accepted=True):
|
|
|
64
83
|
http, browser["port"], browser["automation_target_id"]
|
|
65
84
|
)
|
|
66
85
|
try:
|
|
86
|
+
# Chrome for Testing 155 on Windows advertises the CLI startup
|
|
87
|
+
# URL while its document remains about:blank. Prepare this neutral
|
|
88
|
+
# fixture through the existing owned CDP connection exactly once;
|
|
89
|
+
# the cancellation under test is dispatched separately afterwards.
|
|
90
|
+
prepared = await peer.call("Page.navigate", {"url": bootstrap_url})
|
|
91
|
+
assert "errorText" not in prepared, prepared
|
|
92
|
+
await peer.wait_navigation(prepared, timeout=10)
|
|
93
|
+
ready = await wait_for(
|
|
94
|
+
lambda: peer.js(
|
|
95
|
+
"({url:location.href,ready:document.readyState,"
|
|
96
|
+
"marker:document.querySelector('#bootstrap')?.textContent})"
|
|
97
|
+
),
|
|
98
|
+
lambda value: value["url"] == bootstrap_url
|
|
99
|
+
and value["ready"] in ("interactive", "complete")
|
|
100
|
+
and value["marker"] == "Ready",
|
|
101
|
+
timeout=10,
|
|
102
|
+
)
|
|
103
|
+
assert bootstrap_requests == ["/bootstrap"] and requests == []
|
|
104
|
+
phases.append({"phase": "bootstrap-ready", "observed": ready})
|
|
67
105
|
navigation = asyncio.create_task(
|
|
68
106
|
navigate_owned_browser(
|
|
69
107
|
browser, url, folder, settle_timeout=15 if accepted else 2
|
|
70
108
|
)
|
|
71
109
|
)
|
|
72
110
|
await asyncio.wait_for(first_request.wait(), 10)
|
|
111
|
+
phases.append({"phase": "first-target-held"})
|
|
73
112
|
# This independent owned driver is the controlled competing
|
|
74
113
|
# browser navigation; the helper itself still dispatches once.
|
|
75
114
|
await asyncio.wait_for(
|
|
@@ -108,6 +147,18 @@ async def startup_race_journey(folder, accepted=True):
|
|
|
108
147
|
await peer.close()
|
|
109
148
|
finally:
|
|
110
149
|
release_first.set()
|
|
150
|
+
(folder / "startup-navigation-race-evidence.json").write_text(
|
|
151
|
+
json.dumps(
|
|
152
|
+
{
|
|
153
|
+
"bootstrapRequests": bootstrap_requests,
|
|
154
|
+
"targetRequests": requests,
|
|
155
|
+
"phases": phases,
|
|
156
|
+
"acceptedDestination": accepted,
|
|
157
|
+
},
|
|
158
|
+
indent=2,
|
|
159
|
+
),
|
|
160
|
+
encoding="utf-8",
|
|
161
|
+
)
|
|
111
162
|
if browser and "session_id" in browser:
|
|
112
163
|
stopped = await asyncio.to_thread(
|
|
113
164
|
browser_stop, session_id=browser["session_id"]
|