Handstage offers four connection modes, ranging from zero-configuration local launch to full control over the underlying CDP transport. In most cases you callDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/l-xiaoshen/handstage/llms.txt
Use this file to discover all available pages before exploring further.
V3.connectLocal() and Handstage handles everything else. The other modes exist for CI pipelines, cloud browsers, and embedding Handstage inside a larger framework.
Modes at a glance
connectLocal()
Launch a fresh Chrome process. The most common path — Handstage picks the binary, creates a temporary profile, and cleans up on close.
connectLocal({ cdpUrl })
Attach to a Chrome you already started by connecting to its CDP WebSocket endpoint.
connectTransport()
Supply a custom
CDPTransport object (for proxied or embedded CDP connections).connectSession()
Wrap an existing
ExternalCDPSession (useful when another tool owns the CDP session).Launch Chrome automatically
CallV3.connectLocal() with no arguments to launch Chrome with default settings. Handstage creates a temporary profile directory, sets a sensible viewport, and kills the process when you call close().
Connection modes
- Auto-launch
- Attach via CDP URL
- Custom transport
- Existing session
LocalBrowserLaunchOptions reference
These options are passed insidelocalBrowserLaunchOptions when calling V3.connectLocal().
| Option | Type | Description |
|---|---|---|
headless | boolean | Run without a visible window. |
executablePath | string | Path to the Chrome binary. Defaults to the system-installed Chrome. |
port | number | Remote debugging port. |
viewport | { width, height } | Initial window size in CSS pixels. Defaults to 1288×711. |
args | string[] | Extra Chrome command-line flags appended after the defaults. |
userDataDir | string | Path to a Chrome profile directory (see note below). |
preserveUserDataDir | boolean | Keep the profile directory after close() even if Handstage created it. |
proxy | { server, bypass?, username?, password? } | HTTP/HTTPS proxy settings. |
devtools | boolean | Auto-open DevTools for every tab. |
locale | string | Sets --lang flag (e.g. "fr-FR"). |
ignoreHTTPSErrors | boolean | Pass --ignore-certificate-errors to Chrome. |
ignoreDefaultArgs | boolean | string[] | Skip all default flags (true) or remove specific ones by substring. |
chromiumSandbox | boolean | Enable the Chromium sandbox (disabled by default in many environments). |
deviceScaleFactor | number | Override the device pixel ratio via --force-device-scale-factor. |
hasTouch | boolean | Enable touch events via --touch-events=enabled. |
cdpUrl | string | Connect to an existing browser instead of launching one. |
cdpHeaders | Record<string, string> | Headers sent with the CDP WebSocket upgrade (requires cdpUrl). |
connectTimeoutMs | number | Timeout for establishing the CDP connection. |
downloadsPath | string | Directory where downloads are saved. |
acceptDownloads | boolean | Allow (true) or deny (false) file downloads. |
Shared options
All connection methods accept a shared options object with these fields:| Option | Type | Description |
|---|---|---|
keepAlive | boolean | When true, calling close() does not kill the Chrome process. SIGINT handling is also relaxed so your Node process can exit while Chrome keeps running. |
sessionId | string | Optional identifier for this session. Falls back to an internal UUID. |
verbose | LogLevel | Minimum log level to emit: Error, Info (default), or Debug. |
logger | (line: LogLine) => void | Custom log sink. Defaults to createConsoleLogger(). |
connectTransport and connectSession, you can also pass viewport, deviceScaleFactor, downloadsPath, and acceptDownloads directly on the options object (not nested under localBrowserLaunchOptions).
The HEADLESS environment variable
When the HEADLESS environment variable is set but its value is not exactly "true", Handstage removes the variable from process.env before launching Chrome. This means setting HEADLESS=false or HEADLESS=0 in your environment is the same as not setting it at all — Chrome will be launched in headed (visible window) mode. Only HEADLESS=true causes Handstage to pass the headless flag.
Persisting a browser profile
By default Handstage creates a temporary profile directory and deletes it when you callclose(). To keep state between runs (cookies, localStorage, cached credentials), point userDataDir at a directory you control:
Keep Chrome running after your script exits
SetkeepAlive: true to leave Chrome running after close(). This is useful when you want to inspect the browser state manually or hand off a profile to another process:
Creating isolated contexts
After connecting, callhandstage.create() to open an additional browser context (similar to an incognito profile). Each context has its own cookies and storage but shares the same Chrome process: