Support browser CDP URL from config

This commit is contained in:
helix4u 2026-04-17 15:03:31 -06:00 committed by Teknium
parent e9b8ece103
commit 64b354719f
4 changed files with 65 additions and 5 deletions

View file

@ -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.

View file

@ -184,6 +184,8 @@ _HERMES_BEHAVIORAL_VARS = frozenset({
"HERMES_BACKGROUND_NOTIFICATIONS",
"HERMES_EXEC_ASK",
"HERMES_HOME_MODE",
"BROWSER_CDP_URL",
"CAMOFOX_URL",
})

View file

@ -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)

View file

@ -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 ""
# ============================================================================