diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb01b047..6e7496c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/docs/cli/browser.md b/docs/cli/browser.md index 9c25d563f..826362c03 100644 --- a/docs/cli/browser.md +++ b/docs/cli/browser.md @@ -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 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) - diff --git a/docs/tools/browser.md b/docs/tools/browser.md index 3d8183ef6..c42a243c5 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -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 **18800–18899** 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. diff --git a/docs/tools/chrome-extension.md b/docs/tools/chrome-extension.md index c96a45a78..1b1d0e9e0 100644 --- a/docs/tools/chrome-extension.md +++ b/docs/tools/chrome-extension.md @@ -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 Gateway’s 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** diff --git a/src/browser/config.test.ts b/src/browser/config.test.ts index da54656ea..a63b34fa2 100644 --- a/src/browser/config.test.ts +++ b/src/browser/config.test.ts @@ -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); + }); }); diff --git a/src/browser/config.ts b/src/browser/config.ts index 51c181162..fc4742e48 100644 --- a/src/browser/config.ts +++ b/src/browser/config.ts @@ -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, + controlPort: number, +): Record { + 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";