fix(browser): surface detection details and docs

This commit is contained in:
Peter Steinberger
2026-01-16 06:57:20 +00:00
parent 2b16a87f04
commit 028eed5fe8
6 changed files with 70 additions and 4 deletions

View File

@@ -112,3 +112,18 @@ curl -s http://127.0.0.1:18791/tabs
| `browser.noSandbox` | Add `--no-sandbox` flag (needed for some Linux setups) | `false` | | `browser.noSandbox` | Add `--no-sandbox` flag (needed for some Linux setups) | `false` |
| `browser.attachOnly` | Don't launch browser, only attach to existing | `false` | | `browser.attachOnly` | Don't launch browser, only attach to existing | `false` |
| `browser.cdpPort` | Chrome DevTools Protocol port | `18800` | | `browser.cdpPort` | Chrome DevTools Protocol port | `18800` |
### Problem: "Chrome extension relay is running, but no tab is connected"
Youre using the `chrome` profile (extension relay). It expects the Clawdbot
browser extension to be attached to a live tab.
Fix options:
1. **Use the managed browser:** `clawdbot browser start --browser-profile clawd`
(or set `browser.defaultProfile: "clawd"`).
2. **Use the extension relay:** install the extension, open a tab, and click the
Clawdbot extension icon to attach it.
Notes:
- The `chrome` profile uses your **system default Chromium browser** when possible.
- Local `clawd` profiles auto-assign `cdpPort`/`cdpUrl`; only set those for remote CDP.

View File

@@ -14,8 +14,10 @@ control server.
Beginner view: Beginner view:
- Think of it as a **separate, agent-only browser**. - Think of it as a **separate, agent-only browser**.
- It does **not** touch your personal browser profile. - The `clawd` profile does **not** touch your personal browser profile.
- The agent can **open tabs, read pages, click, and type** in a safe lane. - The agent can **open tabs, read pages, click, and type** in a safe lane.
- The default `chrome` profile uses the **system default Chromium browser** via the
extension relay; switch to `clawd` for the isolated managed browser.
## What you get ## What you get
@@ -39,6 +41,14 @@ clawdbot browser --browser-profile clawd snapshot
If you get “Browser disabled”, enable it in config (see below) and restart the If you get “Browser disabled”, enable it in config (see below) and restart the
Gateway. Gateway.
## Profiles: `clawd` vs `chrome`
- `clawd`: managed, isolated browser (no extension required).
- `chrome`: extension relay to your **system browser** (requires the Clawdbot
extension to be attached to a tab).
Set `browser.defaultProfile: "clawd"` if you want managed mode by default.
## Configuration ## Configuration
Browser settings live in `~/.clawdbot/clawdbot.json`. Browser settings live in `~/.clawdbot/clawdbot.json`.
@@ -71,11 +81,21 @@ Notes:
- `cdpUrl` defaults to `controlUrl + 1` when unset. - `cdpUrl` defaults to `controlUrl + 1` when unset.
- `attachOnly: true` means “never launch a local browser; only attach if it is already running.” - `attachOnly: true` means “never launch a local browser; only attach if it is already running.”
- `color` + per-profile `color` tint the browser UI so you can see which profile is active. - `color` + per-profile `color` tint the browser UI so you can see which profile is active.
- Auto-detect order: default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary. - Default profile is `chrome` (extension relay). Use `defaultProfile: "clawd"` for the managed browser.
- Auto-detect order: system default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary.
- Local `clawd` profiles auto-assign `cdpPort`/`cdpUrl` — set those only for remote CDP.
## Use Brave (or another Chromium-based browser) ## Use Brave (or another Chromium-based browser)
Set `browser.executablePath` to override auto-detection: If your **system default** browser is Chromium-based (Chrome/Brave/Edge/etc),
Clawdbot uses it automatically. Set `browser.executablePath` to override
auto-detection:
CLI example:
```bash
clawdbot config set browser.executablePath "/usr/bin/google-chrome"
```
```json5 ```json5
// macOS // macOS
@@ -339,6 +359,10 @@ Playwright. If Playwright isnt installed, those endpoints return a clear 501
error. ARIA snapshots and basic screenshots still work for clawd-managed Chrome. error. ARIA snapshots and basic screenshots still work for clawd-managed Chrome.
For the Chrome extension relay driver, ARIA snapshots and screenshots require Playwright. For the Chrome extension relay driver, ARIA snapshots and screenshots require Playwright.
If you see `Playwright is not available in this gateway build`, install the full
Playwright package (not `playwright-core`) and restart the gateway, or reinstall
Clawdbot with browser support.
## How it works (internal) ## How it works (internal)
High-level flow: High-level flow:

View File

@@ -13,6 +13,9 @@ export type BrowserStatus = {
cdpPort: number; cdpPort: number;
cdpUrl?: string; cdpUrl?: string;
chosenBrowser: string | null; chosenBrowser: string | null;
detectedBrowser?: string | null;
detectedExecutablePath?: string | null;
detectError?: string | null;
userDataDir: string | null; userDataDir: string | null;
color: string; color: string;
headless: boolean; headless: boolean;

View File

@@ -63,7 +63,11 @@ export async function requirePwAi(
jsonError( jsonError(
res, res,
501, 501,
`Playwright is not available in this gateway build; '${feature}' is unsupported.`, [
`Playwright is not available in this gateway build; '${feature}' is unsupported.`,
"Install the full Playwright package (not playwright-core) and restart the gateway, or reinstall with browser support.",
"Docs: /tools/browser#playwright-requirement",
].join("\n"),
); );
return null; return null;
} }

View File

@@ -1,5 +1,6 @@
import type express from "express"; import type express from "express";
import { resolveBrowserExecutableForPlatform } from "../chrome.executables.js";
import { createBrowserProfilesService } from "../profiles-service.js"; import { createBrowserProfilesService } from "../profiles-service.js";
import type { BrowserRouteContext } from "../server-context.js"; import type { BrowserRouteContext } from "../server-context.js";
import { getProfileContext, jsonError, toStringOrEmpty } from "./utils.js"; import { getProfileContext, jsonError, toStringOrEmpty } from "./utils.js";
@@ -36,6 +37,19 @@ export function registerBrowserBasicRoutes(app: express.Express, ctx: BrowserRou
]); ]);
const profileState = current.profiles.get(profileCtx.profile.name); const profileState = current.profiles.get(profileCtx.profile.name);
let detectedBrowser: string | null = null;
let detectedExecutablePath: string | null = null;
let detectError: string | null = null;
try {
const detected = resolveBrowserExecutableForPlatform(current.resolved, process.platform);
if (detected) {
detectedBrowser = detected.kind;
detectedExecutablePath = detected.path;
}
} catch (err) {
detectError = String(err);
}
res.json({ res.json({
enabled: current.resolved.enabled, enabled: current.resolved.enabled,
@@ -48,6 +62,9 @@ export function registerBrowserBasicRoutes(app: express.Express, ctx: BrowserRou
cdpPort: profileCtx.profile.cdpPort, cdpPort: profileCtx.profile.cdpPort,
cdpUrl: profileCtx.profile.cdpUrl, cdpUrl: profileCtx.profile.cdpUrl,
chosenBrowser: profileState?.running?.exe.kind ?? null, chosenBrowser: profileState?.running?.exe.kind ?? null,
detectedBrowser,
detectedExecutablePath,
detectError,
userDataDir: profileState?.running?.userDataDir ?? null, userDataDir: profileState?.running?.userDataDir ?? null,
color: profileCtx.profile.color, color: profileCtx.profile.color,
headless: current.resolved.headless, headless: current.resolved.headless,

View File

@@ -47,7 +47,10 @@ export function registerBrowserManageCommands(
`cdpPort: ${status.cdpPort}`, `cdpPort: ${status.cdpPort}`,
`cdpUrl: ${status.cdpUrl ?? `http://127.0.0.1:${status.cdpPort}`}`, `cdpUrl: ${status.cdpUrl ?? `http://127.0.0.1:${status.cdpPort}`}`,
`browser: ${status.chosenBrowser ?? "unknown"}`, `browser: ${status.chosenBrowser ?? "unknown"}`,
`detectedBrowser: ${status.detectedBrowser ?? "unknown"}`,
`detectedPath: ${status.detectedExecutablePath ?? status.executablePath ?? "auto"}`,
`profileColor: ${status.color}`, `profileColor: ${status.color}`,
...(status.detectError ? [`detectError: ${status.detectError}`] : []),
].join("\n"), ].join("\n"),
); );
} catch (err) { } catch (err) {