From 64b354719f5e7d242b0405b9014fd60bd8ac0cd4 Mon Sep 17 00:00:00 2001 From: helix4u <4317663+helix4u@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:03:31 -0600 Subject: [PATCH] Support browser CDP URL from config --- hermes_cli/config.py | 1 + tests/conftest.py | 2 ++ tests/tools/test_browser_cdp_override.py | 39 ++++++++++++++++++++++++ tools/browser_tool.py | 28 ++++++++++++++--- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 1670156b2..e17107b6c 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -418,6 +418,7 @@ DEFAULT_CONFIG = { "command_timeout": 30, # Timeout for browser commands in seconds (screenshot, navigate, etc.) "record_sessions": False, # Auto-record browser sessions as WebM videos "allow_private_urls": False, # Allow navigating to private/internal IPs (localhost, 192.168.x.x, etc.) + "cdp_url": "", # Optional persistent CDP endpoint for attaching to an existing Chromium/Chrome "camofox": { # When true, Hermes sends a stable profile-scoped userId to Camofox # so the server maps it to a persistent Firefox profile automatically. diff --git a/tests/conftest.py b/tests/conftest.py index c5b367266..ca4a9a970 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -184,6 +184,8 @@ _HERMES_BEHAVIORAL_VARS = frozenset({ "HERMES_BACKGROUND_NOTIFICATIONS", "HERMES_EXEC_ASK", "HERMES_HOME_MODE", + "BROWSER_CDP_URL", + "CAMOFOX_URL", }) diff --git a/tests/tools/test_browser_cdp_override.py b/tests/tools/test_browser_cdp_override.py index aa3887738..73f0f574f 100644 --- a/tests/tools/test_browser_cdp_override.py +++ b/tests/tools/test_browser_cdp_override.py @@ -77,3 +77,42 @@ class TestResolveCdpOverride: "https://cdp.browser-use.example/session/json/version", timeout=10, ) + + +class TestGetCdpOverride: + def test_prefers_env_var_over_config(self, monkeypatch): + import tools.browser_tool as browser_tool + + monkeypatch.setenv("BROWSER_CDP_URL", HTTP_URL) + monkeypatch.setattr( + browser_tool, + "read_raw_config", + lambda: {"browser": {"cdp_url": "http://config-host:9222"}}, + raising=False, + ) + + response = Mock() + response.raise_for_status.return_value = None + response.json.return_value = {"webSocketDebuggerUrl": WS_URL} + + with patch("tools.browser_tool.requests.get", return_value=response) as mock_get: + resolved = browser_tool._get_cdp_override() + + assert resolved == WS_URL + mock_get.assert_called_once_with(VERSION_URL, timeout=10) + + def test_uses_config_browser_cdp_url_when_env_missing(self, monkeypatch): + import tools.browser_tool as browser_tool + + monkeypatch.delenv("BROWSER_CDP_URL", raising=False) + + response = Mock() + response.raise_for_status.return_value = None + response.json.return_value = {"webSocketDebuggerUrl": WS_URL} + + with patch("hermes_cli.config.read_raw_config", return_value={"browser": {"cdp_url": HTTP_URL}}), \ + patch("tools.browser_tool.requests.get", return_value=response) as mock_get: + resolved = browser_tool._get_cdp_override() + + assert resolved == WS_URL + mock_get.assert_called_once_with(VERSION_URL, timeout=10) diff --git a/tools/browser_tool.py b/tools/browser_tool.py index 4d0595c98..24114f697 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -260,13 +260,31 @@ def _resolve_cdp_override(cdp_url: str) -> str: def _get_cdp_override() -> str: - """Return a normalized user-supplied CDP URL override, or empty string. + """Return a normalized CDP URL override, or empty string. - When ``BROWSER_CDP_URL`` is set (e.g. via ``/browser connect``), we skip - both Browserbase and the local headless launcher and connect directly to - the supplied Chrome DevTools Protocol endpoint. + Precedence is: + 1. ``BROWSER_CDP_URL`` env var (live override from ``/browser connect``) + 2. ``browser.cdp_url`` in config.yaml (persistent config) + + When either is set, we skip both Browserbase and the local headless + launcher and connect directly to the supplied Chrome DevTools Protocol + endpoint. """ - return _resolve_cdp_override(os.environ.get("BROWSER_CDP_URL", "")) + env_override = os.environ.get("BROWSER_CDP_URL", "").strip() + if env_override: + return _resolve_cdp_override(env_override) + + try: + from hermes_cli.config import read_raw_config + + cfg = read_raw_config() + browser_cfg = cfg.get("browser", {}) + if isinstance(browser_cfg, dict): + return _resolve_cdp_override(str(browser_cfg.get("cdp_url", "") or "")) + except Exception as e: + logger.debug("Could not read browser.cdp_url from config: %s", e) + + return "" # ============================================================================