Documentation
¶
Overview ¶
Package browserlaunch launches a system-installed Chromium-based browser (Chrome / Edge / Brave / Chromium) with a CDP remote-debugging endpoint and exposes the resulting websocket URL so a driver — the Python browser-use sidecar, or chromedp elsewhere — can attach to the very same browser instead of each driver spawning its own.
The design borrows the CDP launch engineering from agent-browser (vercel-labs) and OpenWork's eval host launcher, adapted to Go:
- The PORT is allocated by the Go host via net.Listen("127.0.0.1:0") and handed to Chrome as --remote-debugging-port. This avoids the TOCTOU race in OpenWork's fixed-list probing ([9223..9227] may all be free at probe time but taken by bind time).
- A spawn-integrity check asserts the port Chrome actually bound (read from its DevToolsActivePort file) matches the one we asked for, so a collision never silently attaches to the wrong instance.
- Readiness is a three-way race: CDP /json/list responds vs. the process exits vs. the process is no longer alive — a browser that crashes before CDP is ready never hangs the caller.
Unlike builtin browser_open (which owns the chromedp allocator internally), this package launches the browser as a raw subprocess and exposes only the wsURL. It does NOT drive the browser itself; that is the sidecar's job.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoBrowser = errors.New("no Chromium-based browser found")
ErrNoBrowser is returned when no Chromium-based browser can be found.
Functions ¶
func NormalizeCDPEndpoint ¶ added in v0.2.0
NormalizeCDPEndpoint canonicalizes user input into an http://host:port base. Accepts "http://h:p", "ws://h:p/devtools/...", bare "h:p", a hostname (":9222" is appended — the CDP convention), or a bare port ("9222" → "http://127.0.0.1:9222"). Returns "" for input that carries nothing usable.
func OpenTab ¶ added in v0.2.0
OpenTab opens target as a new tab in the browser behind the CDP endpoint. Chrome 111+ rejects GET on /json/new, so this issues a PUT. It is the companion-window tier of the preview pane (pane-system spec §3.6): the managed browser keeps the user's logins while the pane hands a URL over.
Types ¶
type AttachInfo ¶ added in v0.2.0
type AttachInfo struct {
// CDPURL is the normalized HTTP endpoint, e.g. "http://127.0.0.1:9222".
CDPURL string
// WSURL is the browser-level websocket debugger URL resolved from
// /json/version — what connect_over_cdp / chromedp.NewRemoteAllocator /
// the browser-use sidecar take.
WSURL string
// BrowserName is the product name parsed from /json/version's Browser
// field ("Chrome/137.0.7151.68" → "Chrome").
BrowserName string
// Version is the raw Browser string from /json/version.
Version string
}
AttachInfo describes a reachable CDP endpoint of an already-running browser.
func ProbeAttach ¶ added in v0.2.0
func ProbeAttach(ctx context.Context, endpoint string) (*AttachInfo, error)
ProbeAttach verifies that a CDP endpoint answers and resolves its browser-level websocket URL. Timeout is bounded by the caller's ctx.
type CDPVersionInfo ¶
type CDPVersionInfo struct {
Browser string `json:"Browser"`
WebKitVersion string `json:"WebKit-Version"`
UserAgent string `json:"User-Agent"`
V8Version string `json:"V8-Version"`
WebSocketDebuggerURL string `json:"webSocketDebuggerUrl"`
}
CDPVersionInfo is the JSON returned by /json/version. We only need the webSocketDebuggerUrl to derive the browser-level ws endpoint, but the rest is useful for diagnostics.
type Handle ¶
type Handle struct {
// CDPURL is the base HTTP endpoint, e.g. "http://127.0.0.1:54321". Drivers
// that speak raw HTTP discovery (/json/version, /json/list) use this.
CDPURL string
// WSURL is the browser-level websocket debugger URL, e.g.
// "ws://127.0.0.1:54321/devtools/browser/...". Drivers using
// connect_over_cdp / chromedp.NewRemoteAllocator use this.
WSURL string
// BrowserName is the display name ("Chrome" / "Edge" / …) of what was driven.
BrowserName string
// Port is the bound remote-debugging port.
Port int
// contains filtered or unexported fields
}
Handle is a launched browser process plus its CDP endpoint. Close must be called to release the process and temp profile.
func Launch ¶
func Launch(ctx context.Context, opts LaunchOptions) (*Handle, error)
Launch detects and starts a Chromium-based browser with a CDP endpoint and returns a Handle once CDP is reachable. The caller owns the Handle and must Close it.
If ExecutablePath is empty and no Chromium browser can be found, Launch returns an error wrapping ErrNoBrowser with install guidance — it never spawns a non-Chromium binary.
func (*Handle) Close ¶
Close terminates the browser, then (if Launch created a temp profile) removes it. It is safe to call multiple times.
When the caller supplied a PERSISTENT --user-data-dir (LoginOpts.UserDataDir), Close asks Chrome to exit gracefully and waits briefly so it can flush cookies/localStorage to disk before the process is killed — otherwise a persistent login session can be lost on a hard kill (a real issue agent-browser guards against with its wait_or_kill). The temp profile is only deleted when Launch created it (ownTempDir); a user-supplied persistent dir is left intact.
func (*Handle) Done ¶
func (h *Handle) Done() <-chan struct{}
Done returns a channel closed when the browser process exits. Useful for the screencast/keepalive loops to detect that the browser died under them.
func (*Handle) ExitErr ¶
ExitErr returns the process exit error after Done is closed (nil for a clean exit). Before Done is closed the result is meaningless.
func (*Handle) VersionInfo ¶
func (h *Handle) VersionInfo(ctx context.Context) (*CDPVersionInfo, error)
VersionInfo fetches /json/version from the running browser. Useful for diagnostics and confirming the browser is a real Chromium.
type LaunchOptions ¶
type LaunchOptions struct {
// ExecutablePath overrides browser auto-detection. Empty = auto-detect
// (Chrome → Edge → Brave → Chromium). This mirrors builtin's CHROME_PATH /
// [cowork] browser_path.
ExecutablePath string
// UserDataDir is the --user-data-dir. Empty = a fresh temp dir per launch
// (zero pollution, zero conflict with the user's real profile).
UserDataDir string
// Port pins the remote-debugging port instead of allocating a free one.
// Used for the persistent "managed" browser whose endpoint is stable
// across restarts (so a saved attach URL keeps working). 0 = allocate.
Port int
// Headless launches with --headless=new. When the in-app screencast panel
// is the primary view, headless avoids a second visible window stealing
// focus; for local debugging set false to watch the real window.
Headless bool
// Proxy is the --proxy-server value (e.g. "http://127.0.0.1:7890"). Empty =
// no proxy (direct). The driven browser should reach the network the same way
// fairpeer's other traffic does, so this mirrors the chromedp browser_* tools
// (see boot.go resolveBrowserProxyURL). Authenticated proxies (user:pass@)
// are NOT supported by --proxy-server; the caller must strip credentials.
Proxy string
// ExtraArgs are appended to the Chrome command line after the defaults.
ExtraArgs []string
// StartURL, if non-empty, is opened as the initial page.
StartURL string
}
LaunchOptions controls how a browser is launched.