feat(browser): support direct WebSocket CDP URLs#31085
feat(browser): support direct WebSocket CDP URLs#31085steipete merged 15 commits intoopenclaw:mainfrom
Conversation
Add Browserbase documentation section alongside the existing Browserless section in the browser docs. Includes signup instructions, CDP connection configuration, and environment variable setup for both English and Chinese (zh-CN) translations. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This reverts commit c469657.
Add Browserbase documentation section alongside the existing Browserless section in the browser docs. Includes signup instructions, CDP connection configuration, and environment variable setup. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Rename "Configuration" sub-heading to "Profile setup" to avoid MD024/no-duplicate-heading conflict with the existing top-level "Configuration" heading. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Browserbase requires creating a session via their API to get a CDP connect URL, unlike Browserless which uses a static endpoint. Updated to show the correct curl-based session creation flow, removed unverified static WebSocket URL, and added the 5-minute connect timeout note from official docs. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Browserbase exposes a direct WebSocket connect endpoint that auto-creates a session, similar to how Browserless works. Simplified the section to use this static URL pattern instead of requiring manual session creation via the API. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Fix CAPTCHA/stealth/proxy claims: these are Developer plan+ only, not available on free tier - Fix free tier limits: 1 browser hour, 15-min session duration (not "60 minutes of monthly usage") - Add link to pricing page for paid plan details - Simplify structure to match Browserless section format - Remove sub-headings to match Browserless section style Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Restore platform-level feature description (CAPTCHA solving, stealth mode, proxies) without plan-specific pricing gating. Keep free tier note brief. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Browserbase uses direct WebSocket connections (wss://) rather than the standard HTTP-based /json/version CDP discovery flow used by Browserless. This change teaches the browser tool to accept ws:// and wss:// URLs as cdpUrl values: when a WebSocket URL is detected, OpenClaw connects directly instead of attempting HTTP discovery. Changes: - config.ts: accept ws:// and wss:// in cdpUrl validation - cdp.helpers.ts: add isWebSocketUrl() helper - cdp.ts: skip /json/version when cdpUrl is already a WebSocket URL - chrome.ts: probe WSS endpoints via WebSocket handshake instead of HTTP - cdp.test.ts: add test for direct WebSocket target creation - docs/tools/browser.md: update Browserbase section with correct URL format and notes Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Add 12 new tests covering: isWebSocketUrl detection, parseHttpUrl WSS acceptance/rejection, direct WS target creation with query params, SSRF enforcement on WS URLs, WS reachability probing bypasses HTTP - Reframe docs section as generic "Direct WebSocket CDP providers" with Browserbase as one example — any WSS-based provider works - Update security tips to mention WSS alongside HTTPS Co-Authored-By: Claude Opus 4.6 <[email protected]>
Two pre-existing tests still expected ws:// URLs to be rejected by parseHttpUrl, which now accepts them. Switch the invalid-protocol fixture to ftp:// and tighten the assertion to match the full "must be http(s) or ws(s)" error message. Co-Authored-By: Claude Opus 4.6 <[email protected]>
c1a05a2 to
0c2713f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c2713ff43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Greptile SummaryThis PR extends the browser tool's CDP connection flow to accept Key issue to address before merging:
Confidence Score: 2/5
Last reviewed commit: 93889c0 |
Additional Comments (1)
This matters because // config.ts ~line 330
cdpUrl = `${resolved.cdpProtocol}://${resolved.cdpHost}:${cdpPort}`;A top-level |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93889c0eb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Additional Comments (1)
const cdpProtocol = cdpInfo.parsed.protocol === "https:" ? "https" : "http";This value is used as a fallback in cdpUrl = `${resolved.cdpProtocol}://${resolved.cdpHost}:${cdpPort}`;The problem: If the root config uses a Example: browser:
cdpUrl: "wss://connect.browserbase.com"
profiles:
custom:
cdpPort: 9999This would construct The default "openclaw" profile is protected by the Suggested fix: Extend the const cdpProtocol =
cdpInfo.parsed.protocol === "https:" || cdpInfo.parsed.protocol === "wss:"
? (cdpInfo.parsed.protocol === "wss:" ? "wss" : "https")
: cdpInfo.parsed.protocol === "ws:" ? "ws" : "http";This ensures custom profiles with |
|
Re: new P2 ("Disallow ws:// loopback cdpUrl in profile parsing") — not a concern in practice. For remote (non-loopback) profiles, No real-world provider exposes a loopback WebSocket CDP endpoint ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5daf9a74c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/browser/config.ts
Outdated
| const legacyCdpUrl = rawCdpUrl && isWsUrl ? cdpInfo.normalized : undefined; | ||
| const profiles = ensureDefaultChromeExtensionProfile( | ||
| ensureDefaultProfile(cfg?.profiles, defaultColor, legacyCdpPort, cdpPortRangeStart), | ||
| ensureDefaultProfile(cfg?.profiles, defaultColor, legacyCdpPort, cdpPortRangeStart, legacyCdpUrl), |
There was a problem hiding this comment.
Preserve ws/wss base protocol for port-only profiles
When browser.cdpUrl is a WebSocket URL, this branch only preserves that full URL for an auto-created default profile; any existing (or additional) profile that has only cdpPort is still resolved via resolveProfile using resolved.cdpProtocol, which currently collapses ws:/wss: to http. In configs like browser.cdpUrl: "wss://..." plus profiles.work.cdpPort, the work profile is silently rewritten to http://host:port, dropping WebSocket semantics and query auth, so CDP connections fail for those profiles.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Same issue addressed in the Greptile thread — port-only profiles inheriting from a global wss:// endpoint is a contrived config. Cloud WS providers use per-session URLs with auth params. Happy to address in a
follow-up if needed.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 19ead8653e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (isWebSocketUrl(cdpUrl)) { | ||
| // Direct WebSocket endpoint — probe via WS handshake. | ||
| return await canOpenWebSocket(cdpUrl, timeoutMs); |
There was a problem hiding this comment.
Use handshake timeout when probing WebSocket CDP URLs
The new WebSocket branch reuses timeoutMs from isChromeReachable for the WS handshake, but callers treat that value as an HTTP timeout: createProfileAvailability.isHttpReachable passes remoteCdpTimeoutMs (default 1500ms) while WS readiness checks use remoteCdpHandshakeTimeoutMs (default 3000ms). For direct ws/wss providers that need >1.5s to establish a session, ensureBrowserAvailable can now fail early with “Remote CDP … not reachable” even though isChromeCdpReady would succeed with the configured handshake timeout (src/browser/server-context.availability.ts).
Useful? React with 👍 / 👎.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1bc935ee44
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (isWebSocketUrl(cdpUrl)) { | ||
| // Direct WebSocket endpoint — probe via WS handshake. | ||
| return await canOpenWebSocket(cdpUrl, timeoutMs); |
There was a problem hiding this comment.
Respect WS handshake timeout in reachability checks
For direct ws:///wss:// CDP URLs, isChromeReachable now uses the HTTP timeout argument as the WebSocket handshake timeout, so remote profiles get probed with remoteCdpTimeoutMs (default 1500ms) instead of remoteCdpHandshakeTimeoutMs (default 3000ms). In ensureBrowserAvailable, that first probe gates the whole flow, so providers that need a slower handshake can be reported as unreachable even though the later CDP-ready check would succeed with the configured handshake timeout.
Useful? React with 👍 / 👎.
| const allowed = ["http:", "https:", "ws:", "wss:"]; | ||
| if (!allowed.includes(parsed.protocol)) { | ||
| throw new Error(`${label} must be http(s) or ws(s), got: ${parsed.protocol.replace(":", "")}`); |
There was a problem hiding this comment.
Reject loopback WS CDP URLs until tab ops support them
Allowing ws:/wss: in URL parsing makes loopback WebSocket CDP endpoints pass validation, but loopback tab operations still call HTTP discovery endpoints via fetchJson(appendCdpPath(profile.cdpUrl, ...)); with a ws:// base URL this produces ws://.../json/*, which Node fetch rejects (unknown scheme). That means newly accepted configs (including local Chrome-style webSocketDebuggerUrl values) can pass startup checks and then fail on tab list/focus/close flows. Fresh evidence in this commit is that the profile-creation test switched its invalid protocol case from ws://bad to ftp://bad, confirming WS URLs are now intentionally accepted.
Useful? React with 👍 / 👎.
Independent E2E VerificationTested the integration end-to-end on macOS with a live Browserbase account against build Config{
browser: {
enabled: true,
defaultProfile: "browserbase",
remoteCdpTimeoutMs: 3000,
remoteCdpHandshakeTimeoutMs: 5000,
profiles: {
browserbase: {
cdpUrl: "wss://connect.browserbase.com?apiKey=<redacted>",
color: "#F97316",
},
},
},
}Verified scenarios
Edge cases checked
What I did not verify
This covers the "End-to-end browser tool usage with a live session (page navigation, screenshots)" gap noted in the PR's Human Verification section. |
) (cherry picked from commit 9914b48)
) (#1314) (cherry picked from commit 9914b48) Co-authored-by: Peter Steinberger <[email protected]>
Summary
wss://) instead of HTTP-based/json/versiondiscovery are incompatible with the browser tool. The cdpUrl parser rejects non-HTTP protocols, and all connection paths assume/json/versionis available.cdpUrlnow acceptsws://andwss://protocols. When a WebSocket URL is detected, OpenClaw connects directly instead of attempting/json/versionHTTP discovery. The legacybrowser.cdpUrlconfig path preserves the full WebSocket URL (including query params like API keys) on the auto-created default profile. Added documentation for WebSocket-based CDP providers./json/versiondiscovery) is untouched — WebSocket detection only activates forws:///wss://URLs. No changes to the Playwright integration path or extension relay.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
/json/version)User-visible / Behavior Changes
browser.profiles.<name>.cdpUrlnow acceptsws://andwss://URLs in addition tohttp://andhttps://.browser.cdpUrl(legacy single-URL config) also works withwss://URLs — the full URL including query params is preserved./json/version.Security Impact (required)
NoNo— API key is passed as a query param, same pattern as existing token authYes— WebSocket URLs are probed via WS handshake instead of HTTP GET for reachability checksopenCdpWebSocketwhich already handles auth headers and proxy bypassNoNoassertBrowserNavigationAllowed) runs before any connection attempt regardless of CDP protocol.Repro + Verification
Environment
wss://CDP endpointSteps
browser.profiles.<name>.cdpUrlto awss://endpointExpected
/json/versioncall)Actual
pw-tools-core.waits-next-download-saves-it.test.tsunrelated to this PR — same failures onmain)Evidence
Test coverage:
isWebSocketUrlhelperparseHttpUrlWSS supportcreateTargetViaCdpdirect WS/json/version, query param preservation, SSRF still enforcedisChromeReachableWS probeisChromeReachableCDP healthbrowser.cdpUrlpreservationwss://URL with query params preserved through default profile resolutionHuman Verification (required)
/json/versiondiscovery (no regression); WSS URLs skip HTTP discovery; invalid protocols still rejected; query params preserved through WS connection and legacy config path; explicit ports respectedscripts/docs-i18n)Compatibility / Migration
Yes— HTTP(S) URLs behave exactly as beforeNo— existing configs work unchanged;ws:///wss://is a new optionNoFailure Recovery (if this breaks)
src/browser/config.ts,src/browser/cdp.ts,src/browser/cdp.helpers.ts,src/browser/chrome.ts,docs/tools/browser.mdwss://URLs failing to connect; existinghttp:///https://profiles breaking (regression)Risks and Mitigations
/json/listfallback for tab listing!cdpIsLoopback→listPagesViaPlaywright); HTTP fallback only applies to loopback profilesensureBrowserAvailablecall (not in a polling loop); the probe does a handshake-only check and immediately closes🤖 AI-assisted PR — Generated with Claude Code