fix(browser): make extension relay zero-config

This commit is contained in:
Peter Steinberger
2026-01-15 08:26:23 +00:00
parent b77b47bb98
commit 75d2785d20
6 changed files with 85 additions and 31 deletions

View File

@@ -52,6 +52,7 @@
- Agents: scrub tuple `items` schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4.
- Apps: use canonical main session keys from gateway defaults across macOS/iOS/Android to avoid creating bare `main` sessions.
- Browser: ship a built-in `chrome` profile for extension relay and start the relay automatically when running locally.
- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight.
- Daemon: clear persisted launchd disabled state before bootstrap (fixes `daemon install` after uninstall). (#849) — thanks @ndraiman.

View File

@@ -32,7 +32,9 @@ clawdbot browser snapshot
## Profiles
Profiles are named browser instances with their own data directory and CDP settings.
Profiles are named browser routing configs. In practice:
- `clawd`: launches/attaches to a dedicated Clawdbot-managed Chrome instance (isolated user data dir).
- `chrome`: controls your existing Chrome tab(s) via the Chrome extension relay.
```bash
clawdbot browser profiles
@@ -103,4 +105,3 @@ clawdbot browser serve --bind 127.0.0.1 --port 18791 --token <token>
Then point the Gateway at it using `browser.controlUrl` + `browser.controlToken` (or `CLAWDBOT_BROWSER_CONTROL_TOKEN`).
Security + TLS best-practices: [Browser tool](/tools/browser), [Tailscale](/gateway/tailscale), [Security](/gateway/security)

View File

@@ -197,13 +197,14 @@ Notes:
## Profiles (multi-browser)
Clawdbot supports multiple named profiles. Each profile has its own:
- user data directory
- CDP port (local) or CDP URL (remote)
- accent color
Clawdbot supports multiple named profiles (routing configs). Profiles can be:
- **clawd-managed**: a dedicated Chrome instance with its own user data directory + CDP port
- **remote**: an explicit CDP URL (Chrome running elsewhere)
- **extension relay**: your existing Chrome tab(s) via the local relay + Chrome extension
Defaults:
- The `clawd` profile is auto-created if missing.
- The `chrome` profile is built-in for the Chrome extension relay (points at `http://127.0.0.1:18792` by default).
- Local CDP ports allocate from **1880018899** by default.
- Deleting a profile moves its local data directory to Trash.
@@ -233,26 +234,30 @@ Chrome extension relay takeover requires host browser control, so either:
### Setup
1) Create a profile that uses the extension driver:
1) Load the extension (dev/unpacked):
```bash
clawdbot browser extension install
```
- Chrome → `chrome://extensions` → enable “Developer mode”
- “Load unpacked” → select the directory printed by `clawdbot browser extension path`
- Pin the extension, then click it on the tab you want to control (badge shows `ON`).
2) Use it:
- CLI: `clawdbot browser --browser-profile chrome tabs`
- Agent tool: `browser` with `profile="chrome"`
Optional: if you want a different name or relay port, create your own profile:
```bash
clawdbot browser create-profile \
--name chrome \
--name my-chrome \
--driver extension \
--cdp-url http://127.0.0.1:18792 \
--color "#00AA00"
```
2) Load the extension (dev/unpacked):
- Chrome → `chrome://extensions` → enable “Developer mode”
- `clawdbot browser extension install`
- “Load unpacked” → select the directory printed by `clawdbot browser extension path`
- Pin the extension, then click it on the tab you want to control (badge shows `ON`).
3) Use it:
- CLI: `clawdbot browser --browser-profile chrome tabs`
- Agent tool: `browser` with `profile="chrome"`
Notes:
- This mode relies on Playwright-on-CDP for most operations (screenshots/snapshots/actions).
- Detach by clicking the extension icon again.

View File

@@ -49,20 +49,24 @@ After upgrading Clawdbot:
- Re-run `clawdbot browser extension install` to refresh the installed files under your Clawdbot state directory.
- Chrome → `chrome://extensions` → click “Reload” on the extension.
## Create a browser profile for the extension
## Use it (no extra config)
Clawdbot ships with a built-in browser profile named `chrome` that targets the extension relay on the default port.
Use it:
- CLI: `clawdbot browser --browser-profile chrome tabs`
- Agent tool: `browser` with `profile="chrome"`
If you want a different name or a different relay port, create your own profile:
```bash
clawdbot browser create-profile \
--name chrome \
--name my-chrome \
--driver extension \
--cdp-url http://127.0.0.1:18792 \
--color "#00AA00"
```
Then target it:
- CLI: `clawdbot browser --browser-profile chrome tabs`
- Agent tool: `browser` with `profile="chrome"`
## Attach / detach (toolbar button)
- Open the tab you want Clawdbot to control.
@@ -94,7 +98,7 @@ If the Gateway is running on the same machine as Chrome and your `browser.contro
you typically **do not** need `clawdbot browser serve`.
The Gateways built-in browser control server will start on `http://127.0.0.1:18791/` and Clawdbot will
auto-start the local relay server when you use a profile with `driver="extension"`.
auto-start the local relay server on `http://127.0.0.1:18792/`.
### Remote Gateway (Gateway runs elsewhere) — **yes**

View File

@@ -13,6 +13,11 @@ describe("browser config", () => {
expect(profile?.cdpPort).toBe(18800);
expect(profile?.cdpUrl).toBe("http://127.0.0.1:18800");
expect(profile?.cdpIsLoopback).toBe(true);
const chrome = resolveProfile(resolved, "chrome");
expect(chrome?.driver).toBe("extension");
expect(chrome?.cdpPort).toBe(18792);
expect(chrome?.cdpUrl).toBe("http://127.0.0.1:18792");
});
it("derives default ports from CLAWDBOT_GATEWAY_PORT when unset", () => {
@@ -24,6 +29,11 @@ describe("browser config", () => {
const profile = resolveProfile(resolved, resolved.defaultProfile);
expect(profile?.cdpPort).toBe(19012);
expect(profile?.cdpUrl).toBe("http://127.0.0.1:19012");
const chrome = resolveProfile(resolved, "chrome");
expect(chrome?.driver).toBe("extension");
expect(chrome?.cdpPort).toBe(19004);
expect(chrome?.cdpUrl).toBe("http://127.0.0.1:19004");
} finally {
if (prev === undefined) {
delete process.env.CLAWDBOT_GATEWAY_PORT;
@@ -108,4 +118,14 @@ describe("browser config", () => {
/must be http/i,
);
});
it("does not add the built-in chrome extension profile if the derived relay port is already used", () => {
const resolved = resolveBrowserConfig({
controlUrl: "http://127.0.0.1:18791",
profiles: {
clawd: { cdpPort: 18792, color: "#FF4500" },
},
});
expect(resolveProfile(resolved, "chrome")).toBe(null);
});
});

View File

@@ -9,7 +9,7 @@ import {
DEFAULT_CLAWD_BROWSER_ENABLED,
DEFAULT_CLAWD_BROWSER_PROFILE_NAME,
} from "./constants.js";
import { CDP_PORT_RANGE_START } from "./profiles.js";
import { CDP_PORT_RANGE_START, getUsedPorts } from "./profiles.js";
export type ResolvedBrowserConfig = {
enabled: boolean;
@@ -104,6 +104,31 @@ function ensureDefaultProfile(
}
return result;
}
/**
* Ensure a built-in "chrome" profile exists for the Chrome extension relay.
*
* Note: this is a Clawdbot browser profile (routing config), not a Chrome user profile.
* It points at the local relay CDP endpoint (controlPort + 1).
*/
function ensureDefaultChromeExtensionProfile(
profiles: Record<string, BrowserProfileConfig>,
controlPort: number,
): Record<string, BrowserProfileConfig> {
const result = { ...profiles };
if (result.chrome) return result;
const relayPort = controlPort + 1;
if (!Number.isFinite(relayPort) || relayPort <= 0 || relayPort > 65535) return result;
// Avoid adding the built-in profile if the derived relay port is already used by another profile
// (legacy single-profile configs may use controlPort+1 for clawd CDP).
if (getUsedPorts(result).has(relayPort)) return result;
result.chrome = {
driver: "extension",
cdpUrl: `http://127.0.0.1:${relayPort}`,
color: "#00AA00",
};
return result;
}
export function resolveBrowserConfig(cfg: BrowserConfig | undefined): ResolvedBrowserConfig {
const enabled = cfg?.enabled ?? DEFAULT_CLAWD_BROWSER_ENABLED;
const envControlUrl = process.env.CLAWDBOT_BROWSER_CONTROL_URL?.trim();
@@ -160,11 +185,9 @@ export function resolveBrowserConfig(cfg: BrowserConfig | undefined): ResolvedBr
const defaultProfile = cfg?.defaultProfile ?? DEFAULT_CLAWD_BROWSER_PROFILE_NAME;
// Use legacy cdpUrl port for backward compatibility when no profiles configured
const legacyCdpPort = rawCdpUrl ? cdpInfo.port : undefined;
const profiles = ensureDefaultProfile(
cfg?.profiles,
defaultColor,
legacyCdpPort,
derivedCdpRange.start,
const profiles = ensureDefaultChromeExtensionProfile(
ensureDefaultProfile(cfg?.profiles, defaultColor, legacyCdpPort, derivedCdpRange.start),
controlPort,
);
const cdpProtocol = cdpInfo.parsed.protocol === "https:" ? "https" : "http";