feat(python): extension_cdp_url for local_browser.connect
Why
Stagehand's extension opens its own CDP connection to the browser, and it is handed the URL the Python client connected with. That only works when the client and the browser can use the same address.
Sometimes they can't. Our case: the client runs in a server process and reaches a sandboxed Chrome through an authenticated gateway, wss://<gateway>/.../cdp?api_key=.... The extension, running inside that Chrome, should dial the browser's local endpoint (ws://127.0.0.1:<port>/devtools/browser/<id>). Using the gateway URL would hand the gateway's credentials to the browser, which loads arbitrary pages, and send every extension command out and back in through the gateway. Today the only workaround is overriding the private Stagehand._worker_init_params.
What
browser = await local_browser.connect(
cdp_url="wss://gateway/cdp?api_key=...",
extension_cdp_url="ws://127.0.0.1:9300/devtools/browser/<id>",
)
stagehand = await Stagehand.create(browser=browser)
local_browser.connecttakes an optionalextension_cdp_url, which must be aws://orwss://URL with a host.- It is carried on the claimed browser's worker metadata, and
Stagehand.createsends it as the init parambrowser_cdp_urlin place of the client's URL. - When it is unset, behaviour is unchanged.
- Python SDK only, with a minor changeset. The TypeScript SDK has the same assumption and could take the same option; happy to follow up.
Tests
- New tests cover the init param, connect carrying the value through, the default, and rejection of non-WebSocket URLs.
ruff format --check,ruff check,ty checkandgenerate.py --checkpass.pytest: 793 passed.test_official_api_routes_through_and_never_closes_a_caller_http_clientfails with and without this change in my checkout, because it needs the extension built inpackages/extension/dist.
🤖 Generated with Claude Code
Summary by cubic
Adds an optional extension_cdp_url to local_browser.connect so Stagehand's extension can dial a different CDP WebSocket than the client's, for cases where the client reaches Chrome through a proxy the extension cannot use.
- When set, Stagehand sends it as the init param
browser_cdp_urlin place of the client's URL; when unset, behavior is unchanged. - The URL must be
ws://orwss://with a host; anything else raises a validation error. - Python SDK only; the TypeScript SDK has the same assumption and could take the same option in a follow-up.
- New tests cover the init param, carrying the value through, the default, and rejection of invalid URLs.
pytestreports 793 passed;test_official_api_routes_through_and_never_closes_a_caller_http_clientfails with and without this change because it needs the extension built inpackages/extension/dist.