From a3fdb664d2b078fafa4de0ed31452a0f4e36bc4b Mon Sep 17 00:00:00 2001 From: ShawnPana Date: Wed, 11 Mar 2026 11:30:08 -0700 Subject: [PATCH] browser: scope wss:// special handling to remote profiles only Loopback ws:// profiles are locally-managed and need the normal launch/attach/probe flow. Only skip throwaway WebSocket probes and use lazy connection for remote (non-loopback) WebSocket profiles. --- src/browser/server-context.availability.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/browser/server-context.availability.ts b/src/browser/server-context.availability.ts index 9831bc25751..5afcec8be1c 100644 --- a/src/browser/server-context.availability.ts +++ b/src/browser/server-context.availability.ts @@ -62,10 +62,11 @@ export function createProfileAvailability({ }); const isReachable = async (timeoutMs?: number) => { - // For direct WebSocket endpoints (e.g. Browser Use), each raw CDP health check - // opens a new WebSocket which may provision a new browser session. Check the - // cached Playwright connection instead — it reflects the actual connection state. - if (isWebSocketUrl(profile.cdpUrl)) { + // For remote WebSocket endpoints (e.g. Browser Use, Browserbase), each raw CDP + // health check opens a new WebSocket which may provision a new browser session. + // Check the cached Playwright connection instead — it reflects the actual state. + // Loopback ws:// profiles are locally-managed and use normal HTTP-based probes. + if (capabilities.isRemote && isWebSocketUrl(profile.cdpUrl)) { return hasActivePlaywrightConnection(profile.cdpUrl); } const { httpTimeoutMs, wsTimeoutMs } = resolveTimeouts(timeoutMs); @@ -73,7 +74,7 @@ export function createProfileAvailability({ }; const isHttpReachable = async (timeoutMs?: number) => { - if (isWebSocketUrl(profile.cdpUrl)) { + if (capabilities.isRemote && isWebSocketUrl(profile.cdpUrl)) { return hasActivePlaywrightConnection(profile.cdpUrl); } const { httpTimeoutMs } = resolveTimeouts(timeoutMs); @@ -180,10 +181,11 @@ export function createProfileAvailability({ } if (!httpReachable) { - // Direct WebSocket endpoints (e.g. Browser Use) are on-demand — no need to probe + // Remote WebSocket endpoints (e.g. Browser Use) are on-demand — no need to probe // reachability up front. The Playwright connection is established lazily in // connectBrowser when a tab operation actually needs it. - if (isWebSocketUrl(profile.cdpUrl)) { + // Loopback ws:// profiles still need the normal launch/attach flow below. + if (capabilities.isRemote && isWebSocketUrl(profile.cdpUrl)) { return; } if ((attachOnly || remoteCdp) && opts.onEnsureAttachTarget) { @@ -255,9 +257,9 @@ export function createProfileAvailability({ const stopRunningBrowser = async (): Promise<{ stopped: boolean }> => { await reconcileProfileRuntime(); - // For direct WebSocket endpoints (e.g. Browser Use), there's no local Chrome process + // For remote WebSocket endpoints (e.g. Browser Use), there's no local Chrome process // to stop. Instead, close the cached Playwright connection to the cloud provider. - if (isWebSocketUrl(profile.cdpUrl)) { + if (capabilities.isRemote && isWebSocketUrl(profile.cdpUrl)) { await closePlaywrightBrowserConnection({ cdpUrl: profile.cdpUrl }); return { stopped: true }; }