browser-control

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Browser Control

浏览器控制

Browser Control is a driver, not an agent. The calling agent decides what to do; Browser Control runs deterministic Playwright code in the user's visible browser.
Use one loop throughout: inspect, act, verify. Inspect the real page before choosing locators, act through the narrowest stable control, then verify the result through a URL or fresh page read. Never treat a successful click or human acknowledgment as proof that the task succeeded.
Browser Control 是一个驱动程序,而非代理。调用方代理决定执行操作;Browser Control 在用户可见的浏览器中运行确定性的 Playwright 代码。
全程遵循一个循环流程:检查、执行、验证。在选择定位器前先检查真实页面,通过最窄的稳定控件执行操作,然后通过URL或重新读取页面来验证结果。切勿将点击成功或人工确认视为任务完成的证明。

Core Workflow

核心工作流

1. Run The Task Directly

1. 直接运行任务

Start with the requested browser work. Relay-backed commands start the detached relay and wait for the extension; do not start
browser-control serve
first.
bash
browser-control execute 'return { url: page.url(), title: await page.title() }'
Use
browser-control doctor
only when setup or runtime behavior is unclear.
status
and
doctor
are observational and never start the relay.
Ordinary CLI/MCP/SDK calls never replace a running relay. On a build mismatch, coordinate with other agents before running
browser-control relay restart
. It preserves browser tabs and durable sessions but resets JavaScript state and snapshot refs. A busy or timed-out drain leaves the old relay running; finish recordings/captures and disconnect raw CDP clients rather than forcing a stop.
bash
browser-control doctor
browser-control status --json
Completion: one execute returns a page result and a readable session id, or
doctor
identifies the concrete setup failure.
从请求的浏览器操作开始。中继支持的命令会启动分离的中继并等待扩展;无需先启动
browser-control serve
。
bash
browser-control execute 'return { url: page.url(), title: await page.title() }'
仅在设置或运行时行为不明确时使用
browser-control doctor
。
status
和
doctor
仅用于观察,绝不会启动中继。
普通的CLI/MCP/SDK调用绝不会替换正在运行的中继。若出现版本不匹配,在运行
browser-control relay restart
前需与其他代理协调。该命令会保留浏览器标签页和持久会话,但会重置JavaScript状态和快照引用。若中继繁忙或超时未关闭,需先完成录制/捕获并断开原始CDP客户端,而非强制停止。
bash
browser-control doctor
browser-control status --json
完成标志:一次execute调用返回页面结果和可读的会话ID,或
doctor
识别出具体的设置故障。

2. Choose The Page Deliberately

2. 谨慎选择页面

A bare CLI execute creates a fresh session-owned page and prints the exact
--session <id>
continuation command. Every later CLI call must pass that id or set
BROWSER_CONTROL_SESSION
; bare execute never guesses from human-shell current state.
bash
browser-control execute 'return page.url()'
browser-control execute --session cosmic-otter-866 'return page.url()'
MCP keeps one implicit process session. Omit
session
for that normal path, or call
session_new
and pass an explicit id when one MCP process needs multiple sessions.
To control a tab already open in the user's browser, ask the user to click the Browser Control toolbar button on that tab. Select it for one execute or adopt it for sticky reuse:
bash
browser-control execute --target-url github.com 'return page.url()'
browser-control session adopt --target-url github.com --session github
targetUrl
and
targetIndex
select existing attached pages; they never navigate. A URL selector must match exactly one page, and URL and index selectors cannot be combined. Adoption makes that tab the session default, closes the session's previous relay-created page, and is exclusive to one Browser Control session. Reset or delete releases an adopted user tab without closing it.
Prefer adoption for authenticated browser state rather than reproducing login in a fresh page.
Each relay controls one browser/profile at a time. A second extension connection cannot replace a healthy active connection. If
status
or
doctor
reports rejected competing connections, keep the extension enabled only in the intended browser/profile. To switch browsers, disconnect the incumbent extension first; creating a new execute session does not switch browsers.
Completion: the selected page URL is the intended page, and later work either retains the returned session id or intentionally uses the MCP process session.
裸CLI execute调用会创建一个新的会话专属页面,并打印出精确的
--session <id>
续接命令。后续所有CLI调用必须传入该ID或设置
BROWSER_CONTROL_SESSION
;裸execute调用绝不会根据用户shell的当前状态猜测会话。
bash
browser-control execute 'return page.url()'
browser-control execute --session cosmic-otter-866 'return page.url()'
MCP会保留一个隐式的进程会话。正常流程可省略
session
参数,当一个MCP进程需要多个会话时,调用
session_new
并传入明确的ID。
要控制用户浏览器中已打开的标签页,请让用户点击该标签页上的Browser Control工具栏按钮。可选择该标签页进行单次execute调用,或采用该标签页进行粘性复用:
bash
browser-control execute --target-url github.com 'return page.url()'
browser-control session adopt --target-url github.com --session github
targetUrl
和
targetIndex
用于选择已连接的现有页面;它们绝不会触发导航。URL选择器必须精确匹配一个页面,且URL和索引选择器不能组合使用。采用操作会将该标签页设为会话默认页,关闭会话之前由中继创建的页面,且该标签页仅专属一个Browser Control会话。重置或删除操作会释放已采用的用户标签页,但不会关闭它。
优先采用已认证的浏览器状态,而非在新页面中重新执行登录流程。
每个中继一次只能控制一个浏览器/配置文件。第二个扩展连接无法替换健康的活动连接。若
status
或
doctor
报告存在被拒绝的竞争连接,请仅在目标浏览器/配置文件中启用扩展。要切换浏览器,需先断开当前扩展的连接;创建新的execute会话不会切换浏览器。
完成标志:所选页面的URL为预期页面,后续操作要么保留返回的会话ID,要么有意使用MCP进程会话。

3. Inspect, Act, Verify

3. 检查、执行、验证

Inspect before guessing roles or selectors:
js
return await snapshot()
Then act from the returned structure and verify the destination:
js
await ref("e12").click()
await page.getByRole("heading", { name: "Settings" }).waitFor()
if (!page.url().includes("/settings")) {
  throw new Error(`Unexpected destination: ${page.url()}`)
}
return { url: page.url(), heading: await page.getByRole("heading").first().innerText() }
Use normal Playwright first. Keep dependent interactions in one execute when they rely on transient UI such as an open menu, selected rows, hover state, or an in-progress form.
If native
locator.fill()
hangs because a browser extension interferes with focus, use the explicit
input
/
textarea
fallback:
js
await fillInput(page.getByPlaceholder("Username"), "standard_user")
Completion: the final return value contains evidence of the requested outcome, not merely evidence that an action was attempted.
在猜测角色或选择器前先进行检查:
js
return await snapshot()
然后根据返回的结构执行操作并验证目标:
js
await ref("e12").click()
await page.getByRole("heading", { name: "Settings" }).waitFor()
if (!page.url().includes("/settings")) {
  throw new Error(`Unexpected destination: ${page.url()}`)
}
return { url: page.url(), heading: await page.getByRole("heading").first().innerText() }
优先使用原生Playwright功能。当操作依赖临时UI(如打开的菜单、选中的行、悬停状态或正在填写的表单)时,将相关交互放在同一个execute调用中。
若原生
locator.fill()
因浏览器扩展干扰焦点而挂起,请使用显式的
input
/
textarea
回退方案:
js
await fillInput(page.getByPlaceholder("Username"), "standard_user")
完成标志:最终返回值包含请求结果的证据,而非仅表明操作已尝试。

4. Continue Or Finish Cleanly

4. 平稳续接或结束

Named sessions preserve their default page across short-lived CLI and MCP processes. They also survive relay restarts: Browser Control restores the id, read-only mode, and exact default target. JavaScript
state
and snapshot refs are process-local and reset after a relay restart with an explicit warning.
bash
browser-control session list
browser-control session reset github
browser-control session delete github
Deletion is idempotent for an explicit session id, so cleanup can be safely retried when that session is already absent.
Every execute is journaled under
~/.browser-control/sessions/<id>/journal.jsonl
. The journal records code, status, duration, URL movement, warnings, handoffs, and bounded diagnostics. Never place credentials directly in execute source.
bash
browser-control journal --session github --limit 50
Completion: retain the session only when follow-up work is expected; otherwise reset or delete session-owned pages and report any warnings that affect later work.
命名会话会在短期CLI和MCP进程之间保留其默认页面。它们还能在中继重启后存活:Browser Control会恢复ID、只读模式和精确的默认目标。JavaScript
state
和快照引用是进程本地的,中继重启后会重置并给出明确警告。
bash
browser-control session list
browser-control session reset github
browser-control session delete github
删除操作对于明确的会话ID是幂等的,因此当会话已不存在时,可安全重试清理操作。
每个execute调用都会被记录在
~/.browser-control/sessions/<id>/journal.jsonl
中。日志记录了代码、状态、时长、URL变化、警告、交接和有限的诊断信息。切勿将凭据直接放入execute源代码中。
bash
browser-control journal --session github --limit 50
完成标志:仅在预期有后续操作时保留会话;否则重置或删除会话专属页面,并报告任何影响后续操作的警告。

Canonical Authenticated Flow

标准认证流程

The distinguishing Browser Control workflow is an authenticated tab plus a human-only prompt:
Attach and adopt the existing tab, inspect its real UI, fill ordinary fields, then register
handoff
before triggering WebAuthn, 2FA, CAPTCHA, or payment UI. After the user completes it, verify the authenticated destination. The same session can continue after an MCP process or relay restart.
When the prompt-triggering action may itself block, put only that action in
start
. Browser Control presents and acknowledges WAIT before invoking it:
js
await handoff("Complete the security-key prompt, then continue", {
  timeoutMs: 600_000,
  start: () => page
    .getByRole("button", { name: /passkey|security key|sign in/i })
    .click({ timeout: 600_000 }),
})

await page.waitForURL((url) => !url.pathname.startsWith("/login"))
await page.getByRole("heading", { name: /account|dashboard/i }).waitFor()
return { authenticatedUrl: page.url(), title: await page.title() }
After a resolved handoff, Browser Control waits through transient destination context replacement before returning, so this verification can remain in the same execute.
For a handoff on another page, pass
{ page: otherPage }
. Readiness checks that page, not the session default. If a non-default page was replaced or closed, inspect the remaining pages rather than assuming an old Playwright reference now identifies its replacement.
Tell the user what action is waiting. Human acknowledgment is not verification: always assert the expected URL or stable element after
handoff
. If the action was already completed and only the human step remains, call
handoff(message)
without
start
. The default timeout is ten minutes.
Completion: the prompt was presented only after WAIT was registered, the action settled, and the authenticated result was independently verified.
Browser Control的独特工作流是:已认证的标签页加上仅人工操作的提示:
连接并采用现有标签页,检查其真实UI,填写普通字段,然后在触发WebAuthn、2FA、CAPTCHA或支付UI前注册
handoff
。用户完成操作后,验证已认证的目标页面。同一个会话可在MCP进程或中继重启后继续使用。
若触发提示的操作本身可能阻塞,请仅将该操作放入
start
中。Browser Control会在调用前显示并确认WAIT状态:
js
await handoff("完成安全密钥提示后继续", {
  timeoutMs: 600_000,
  start: () => page
    .getByRole("button", { name: /passkey|security key|sign in/i })
    .click({ timeout: 600_000 }),
})

await page.waitForURL((url) => !url.pathname.startsWith("/login"))
await page.getByRole("heading", { name: /account|dashboard/i }).waitFor()
return { authenticatedUrl: page.url(), title: await page.title() }
交接完成后,Browser Control会等待临时目标上下文替换完成后再返回,因此验证操作可放在同一个execute调用中。
若要在其他页面进行交接,请传入
{ page: otherPage }
。就绪检查会针对该页面,而非会话默认页。若非默认页面已被替换或关闭,请检查剩余页面,而非假设旧的Playwright引用仍指向其替换页面。
告知用户等待执行的操作。人工确认并非验证:
handoff
后务必断言预期的URL或稳定元素。若操作已完成,仅剩余人工步骤,请调用
handoff(message)
而不带
start
参数。默认超时时间为10分钟。
完成标志:仅在注册WAIT状态后显示提示,操作已完成,且已独立验证认证结果。

Password Manager Prompts

密码管理器提示

Ordinary webpage fields and accessible open-shadow-root controls remain usable. 1Password's inline menus are extension-owned iframes, not ordinary webpage DOM. Chromium blocks one extension from debugging another extension's pages; toolbar popups and native unlock, Touch ID, and Windows Hello prompts are not supported Playwright control surfaces.
target/cross-extension-page
means a permission boundary. Ask the user to finish or dismiss the prompt and retry; do not reset the page, read vault contents, or weaken browser security to get around it. Register
handoff
on the originating webpage before triggering a human-only prompt when possible. If the prompt already prevents attachment, give the user the required action directly rather than assuming an in-page handoff can be displayed. Verify the intended webpage state after the prompt is completed.
普通网页字段和可访问的开放阴影根控件仍可使用。1Password的内联菜单是扩展拥有的iframe,而非普通网页DOM。Chromium会阻止一个扩展调试另一个扩展的页面;工具栏弹窗、原生解锁、Touch ID和Windows Hello提示均不支持Playwright控制。
target/cross-extension-page
表示权限边界。请让用户完成或关闭提示后重试;切勿重置页面、读取保管库内容或削弱浏览器安全来绕过该限制。可能的话,在触发仅人工操作的提示前,在源网页上注册
handoff
。若提示已阻止连接,请直接告知用户所需操作,而非假设可在页面内显示交接提示。提示完成后验证预期的网页状态。

Inspection Tools

检查工具

Use the least expensive view that answers the question:
  • snapshot()
    is the compact read-before-act default. It prioritizes semantic groups, alerts, lists, tables, headings, links, and controls. Text input and textarea values are omitted.
  • ref("e12")
    resolves a control from the latest snapshot. Refs fail closed after navigation or incompatible DOM drift.
  • snapshot({ diff: true })
    reports semantic changes from the compatible prior baseline. A diff invalidates earlier refs and exposes refs only for added or changed current lines.
  • ariaSnapshot(target?, { timeout })
    returns Playwright's detailed YAML aria tree when the compact snapshot omits needed structure. Native text-control values, custom ARIA range values, and editable content are omitted so they do not enter tool output. Await it separately; do not run other operations on the same page concurrently.
  • screenshotWithLabels({ page, path? })
    adds visual labels and metadata when layout matters.
js
return await snapshot({ within: "main", maxItems: 200 })
// When layout matters, return the image through MCP so it can be inspected.
return await screenshotWithLabels({ page })
Saving an image and returning only
"ok"
proves file creation, not visual correctness. Return screenshot buffers through MCP when visual evidence matters.
使用能回答问题的最低成本视图:
  • snapshot()
    是“先检查后执行”的紧凑默认选项。它优先处理语义组、警告、列表、表格、标题、链接和控件。文本输入和textarea的值会被省略。
  • ref("e12")
    根据最新快照解析控件。导航或不兼容的DOM变化后,引用会失效。
  • snapshot({ diff: true })
    报告与兼容基线相比的语义变化。差异会使之前的引用失效,仅暴露新增或修改的当前行的引用。
  • 当紧凑快照遗漏所需结构时,
    ariaSnapshot(target?, { timeout })
    会返回Playwright的详细YAML ARIA树。原生文本控件值、自定义ARIA范围值和可编辑内容会被省略,以免进入工具输出。需单独等待该操作;不要在同一页面上同时运行其他操作。
  • screenshotWithLabels({ page, path? })
    在布局重要时添加视觉标签和元数据。
js
return await snapshot({ within: "main", maxItems: 200 })
// 当布局重要时,通过MCP返回图像以便检查。
return await screenshotWithLabels({ page })
保存图像并仅返回
"ok"
只能证明文件已创建,无法证明视觉正确性。当视觉证据重要时,请通过MCP返回截图缓冲区。

Execute Interface

Execute接口

Execute code can use
page
,
context
,
browser
, persistent
state
, selected Node modules through
modules
and aliases such as
fs
and
path
, plus the Browser Control helpers documented here. Single expressions auto-return; multi-statement scripts need
return
. Use
--file
for longer scripts:
bash
browser-control execute --session github --file ./perform-flow.js
Human CLI output includes logs, warnings, and a concise aftermath. Use
--json
when another command needs to branch on
ok
,
value
,
error
,
warnings
, or
aftermath
:
bash
browser-control execute --json --session github '({ url: page.url() })' | jq .value.url
Playwright downloads are unavailable through extension-backed tabs because Chromium blocks download artifact control through
chrome.debugger
. If the page exposes the payload through fetch or an API response, read the bytes in the page and write them with
fs
. Do not retry
page.waitForEvent("download")
.
Execute代码可使用
page
、
context
、
browser
、持久化
state
、通过
modules
选择的Node模块及别名(如
fs
和
path
),以及此处记录的Browser Control辅助函数。单表达式会自动返回;多语句脚本需要
return
。较长脚本请使用
--file
参数:
bash
browser-control execute --session github --file ./perform-flow.js
用户CLI输出包含日志、警告和简洁的结果摘要。当其他命令需要根据
ok
、
value
、
error
、
warnings
或
aftermath
分支时,请使用
--json
参数:
bash
browser-control execute --json --session github '({ url: page.url() })' | jq .value.url
扩展支持的标签页无法使用Playwright下载功能,因为Chromium会阻止通过
chrome.debugger
控制下载工件。若页面通过fetch或API响应暴露负载,请在页面中读取字节并使用
fs
写入。切勿重试
page.waitForEvent("download")
。

Safety

安全

Browser Control blocks CDP commands that would destroy shared browser state, including browser close and cookie/cache clearing. Never work around those guardrails.
For inspect-only work, use a read-only session:
bash
browser-control session new inspect-prod --read-only
browser-control execute --session inspect-prod 'await page.goto("https://example.com"); return page.title()'
Read-only sessions reject
Input.*
, so they cannot click or type through Playwright.
page.evaluate
can still mutate the DOM; read-only prevents trusted mistakes, not malicious code.
For destructive UI work, use a two-phase read, confirm, verify flow:
  1. Read candidates and return exact stable identifiers or row text.
  2. Obtain user approval for those exact items.
  3. Re-select only approved items and assert the selected count.
  4. Read the confirmation dialog and throw unless it matches the approved action.
  5. Confirm, then verify through a fresh page read or independent CLI/API path.
Do not discover and confirm destructive candidates in one script unless the user already approved exact stable identifiers. Never globally auto-accept native dialogs; wait for the expected dialog and assert its type and message before accepting it.
Browser Control会阻止会破坏共享浏览器状态的CDP命令,包括关闭浏览器、清除Cookie/缓存。切勿绕过这些防护措施。
对于仅检查的操作,请使用只读会话:
bash
browser-control session new inspect-prod --read-only
browser-control execute --session inspect-prod 'await page.goto("https://example.com"); return page.title()'
只读会话会拒绝
Input.*
操作,因此无法通过Playwright点击或输入。
page.evaluate
仍可修改DOM;只读模式仅能防止可信错误,无法阻止恶意代码。
对于破坏性UI操作,请使用两阶段的读取、确认、验证流程:
  1. 读取候选对象并返回精确的稳定标识符或行文本。
  2. 获取用户对这些精确对象的批准。
  3. 仅重新选择已批准的对象并断言选中数量。
  4. 读取确认对话框,若与批准的操作不匹配则抛出错误。
  5. 确认操作,然后通过重新读取页面或独立的CLI/API路径进行验证。
除非用户已批准精确的稳定标识符,否则请勿在一个脚本中同时发现并确认破坏性候选对象。切勿全局自动接受原生对话框;请等待预期的对话框,并在接受前断言其类型和消息。

TypeScript Client

TypeScript客户端

Applications can import
BrowserControlClient
for schema-decoded, same-origin requests authenticated by a session page. Use
sensitive: true
for token-bearing responses and reveal them through Browser Control's API, not the application's own Effect
Redacted
import; package-manager layouts may resolve separate Effect runtimes.
ts
import { BrowserControlClient } from "@opencode-ai/browser-control"

const sensitive = yield* origin.json({
  path: "/api/session",
  method: "POST",
  body: {},
  response: SessionResponse,
  sensitive: true,
})
const session = BrowserControlClient.reveal(sensitive)
应用可导入
BrowserControlClient
,用于通过会话页面认证的同源请求的模式解码。对于包含令牌的响应,请使用
sensitive: true
,并通过Browser Control的API暴露它们,而非应用自身的Effect
Redacted
导入;包管理器布局可能会解析不同的Effect运行时。
ts
import { BrowserControlClient } from "@opencode-ai/browser-control"

const sensitive = yield* origin.json({
  path: "/api/session",
  method: "POST",
  body: {},
  response: SessionResponse,
  sensitive: true,
})
const session = BrowserControlClient.reveal(sensitive)

Authenticated Network Capture

认证网络捕获

Use network capture when the browser is needed to authenticate or discover a workflow, but repeated direct HTTP calls would be faster and more reliable. Capture each flow at least twice with different inputs so constants and parameters can be distinguished.
bash
browser-control network start --session github --url /api/ \
  --resource-type fetch --resource-type xhr
browser-control execute --session github --file ./perform-flow.js
browser-control network status --session github
browser-control network stop --session github \
  --output ./github.har --secrets github
Written artifacts replace credential-bearing headers, cookies, query fields, and structured body fields with stable references such as
${BC_SECRET_1}
.
--secrets github
stores lossless values separately in a mode-
0600
Secret Profile. Never copy profile values into source, output, diagnostics, or journals, and never deliberately return or log credentials.
Inspect the redacted artifact offline, generate one typed function per observed flow, then verify each function with a harmless live request. Run generated clients without exposing values:
bash
browser-control secrets status github
browser-control secrets run github -- ./github-cli repositories
Generated TypeScript applications can own that wrapper internally through the public SDK:
ts
import { SecretProfile } from "@opencode-ai/browser-control"
import { Effect } from "effect"

const result = await Effect.runPromise(SecretProfile.run({
  name: "github",
  command: process.execPath,
  args: ["./github-cli.js", "repositories"],
}))
The trusted worker receives
BC_SECRET_N
variables and its bounded output is redacted. The public SDK exposes profile metadata but never raw profile values.
Refresh credentials normally renewed by a page reload with:
bash
browser-control secrets refresh github --session github --url /api/
If refresh requires login or a human prompt, reauthenticate in the adopted tab and repeat capture with the same profile. MCP exposes equivalent
network_*
and
secrets_*
tools.
Completion: the artifact contains references rather than credential values, the generated operation passes a harmless live check, and no secret value appears in source or output.
当需要浏览器进行认证或发现工作流,但重复直接HTTP调用更快更可靠时,请使用网络捕获功能。请使用不同输入至少捕获两次每个流程,以便区分常量和参数。
bash
browser-control network start --session github --url /api/ \
  --resource-type fetch --resource-type xhr
browser-control execute --session github --file ./perform-flow.js
browser-control network status --session github
browser-control network stop --session github \
  --output ./github.har --secrets github
生成的工件会将包含凭据的头、Cookie、查询字段和结构化体字段替换为稳定引用,如
${BC_SECRET_1}
。
--secrets github
会将无损值单独存储在权限为
0600
的Secret Profile中。切勿将配置文件值复制到源代码、输出、诊断或日志中,切勿故意返回或记录凭据。
离线检查已脱敏的工件,为每个观察到的流程生成一个类型化函数,然后通过无害的实时请求验证每个函数。运行生成的客户端时请勿暴露值:
bash
browser-control secrets status github
browser-control secrets run github -- ./github-cli repositories
生成的TypeScript应用可通过公开SDK内部封装该包装器:
ts
import { SecretProfile } from "@opencode-ai/browser-control"
import { Effect } from "effect"

const result = await Effect.runPromise(SecretProfile.run({
  name: "github",
  command: process.execPath,
  args: ["./github-cli.js", "repositories"],
}))
受信任的工作进程会接收
BC_SECRET_N
变量,其有限输出会被脱敏。公开SDK仅暴露配置文件元数据,绝不会暴露原始配置文件值。
通过页面刷新更新通常会续期的凭据:
bash
browser-control secrets refresh github --session github --url /api/
若刷新需要登录或人工提示,请在已采用的标签页中重新认证,并使用相同配置文件重复捕获。MCP提供等效的
network_*
和
secrets_*
工具。
完成标志:工件包含引用而非凭据值,生成的操作通过无害的实时检查,且源代码或输出中未出现任何机密值。

Recording

录制

Record an attached or session-owned tab with:
bash
browser-control recording start ./tmp/demo.mp4 --session github --mode cdp
browser-control recording status --session github
browser-control recording stop --session github
--mode auto
uses tab capture for user-owned tabs and CDP for relay-owned tabs. Tab capture can include audio; CDP requires
ffmpeg
and has no audio. Use the command's
--help
for format and cursor options.
Completion: stop the recorder, inspect the resulting media rather than only its existence, and report the viewport, state, and interaction path actually tested.
使用以下命令录制已连接或会话专属的标签页:
bash
browser-control recording start ./tmp/demo.mp4 --session github --mode cdp
browser-control recording status --session github
browser-control recording stop --session github
--mode auto
对用户拥有的标签页使用标签捕获,对中继拥有的标签页使用CDP。标签捕获可包含音频;CDP需要
ffmpeg
且不支持音频。请使用命令的
--help
查看格式和光标选项。
完成标志:停止录制器,检查生成的媒体(而非仅检查其存在),并报告实际测试的视口、状态和交互路径。

Troubleshooting

故障排除

  1. Run
    browser-control doctor
    ; it checks package metadata, CLI/relay build identity, extension protocol compatibility, sessions, targets, and artifacts.
  2. Use
    status --json
    to inspect exact sessions and target ownership.
  3. Reproduce once with the smallest execute before changing code.
Common diagnoses:
  • connected:false
    : run a relay-backed command and allow the extension startup or alarm wake-up to reconnect. Reload the unpacked extension only if that loop does not recover.
  • Incompatible extension protocol: update either the extension or npm package; exact extension and relay release versions do not need to match.
  • Competing browser/profile connections: the active browser is preserved and additional connections are rejected. Use one browser/profile per relay; repeatedly creating sessions or resetting tabs does not switch browsers.
  • Stale relay build: inspect
    doctor
    , then coordinate an explicit
    browser-control relay restart
    . It requires an exact managed instance and safe shutdown protocol 2. Legacy relays need a one-time coordinated manual stop; foreground/source or newer relays are never force-killed or downgraded. MCP observational tools remain available on a mismatch.
  • Unexpected restart: inspect private endpoint-scoped
    ~/.browser-control/relays/<port>/lifecycle.jsonl
    for requester/build/instance metadata. Preparing or selecting a development candidate must not restart the daemon. Use isolated
    runtime:prepare
    /
    runtime:select
    , not a live checkout link, when developing Browser Control itself.
  • Target not found
    : attach the intended tab, then select or adopt it using a unique URL substring or explicit index.
  • All targets disappeared: dismissing Chromium's debugging banner detaches every tab. Reattach through the toolbar.
  • Relay restarted: named sessions reclaim exact targets, but JavaScript
    state
    and snapshot refs reset. Continue after the warning.
  • Reset/delete after an extension update may wait briefly for target re-announcement; if the old relay-owned target is absent from the completed inventory, Browser Control forgets the dead identity without closing a guessed tab.
  • Repeated execution-context errors: run one short follow-up so Browser Control can health-check the page. It may recreate a relay-owned page, but it never replaces an unhealthy adopted user tab; reset or re-adopt that tab.
  • Fill timeout on login fields: inspect first, then try
    fillInput
    after confirming the selector or locator resolves. String selectors search open shadow roots recursively; closed shadow roots remain unavailable.
  • Download wait fails: use fetch plus
    fs
    ; extension-backed Playwright cannot retain a native download artifact.
  • Hover on an infinitely animated target: Playwright may never consider the element stable. Read its current
    getBoundingClientRect()
    and use
    page.mouse.move()
    when coordinate input is appropriate.
  • Chromium-protected pages such as the Chrome Web Store developer dashboard may detach
    chrome.debugger
    . Do not retry or bypass that boundary; open the page for manual operation.
For deeper relay diagnosis, restart with
BROWSER_CONTROL_DEBUG=1
. Debug traces must never include expressions, arguments, results, headers, cookies, or form values.
Whenever Browser Control fails, wedges, replaces a page/session, or behaves unexpectedly, create or update a
browser-control
project todo with the Browser Control version, safe session/page context, exact error, deterministic reproduction, expected versus actual behavior, and recovery attempted. Never include credentials, form values, or private account data.
  1. 运行
    browser-control doctor
    ;它会检查包元数据、CLI/中继构建标识、扩展协议兼容性、会话、目标和工件。
  2. 使用
    status --json
    检查精确的会话和目标所有权。
  3. 在修改代码前,先用最小的execute调用重现一次问题。
常见诊断:
  • connected:false
    :运行一个中继支持的命令,等待扩展启动或警报唤醒以重新连接。仅当该循环无法恢复时,才重新加载未打包的扩展。
  • 不兼容的扩展协议:更新扩展或npm包;扩展和中继的精确版本无需匹配。
  • 竞争浏览器/配置文件连接:活动浏览器会被保留,额外连接会被拒绝。每个中继使用一个浏览器/配置文件;重复创建会话或重置标签页不会切换浏览器。
  • 过时的中继构建:检查
    doctor
    输出,然后协调执行显式的
    browser-control relay restart
    。这需要精确的托管实例和安全关闭协议2。旧版中继需要一次性协调手动停止;前台/源或新版中继绝不会被强制终止或降级。版本不匹配时,MCP观察工具仍可使用。
  • 意外重启:检查私有端点范围的
    ~/.browser-control/relays/<port>/lifecycle.jsonl
    ,查看请求方/构建/实例元数据。准备或选择开发候选版本不得重启守护进程。开发Browser Control本身时,请使用隔离的
    runtime:prepare
    /
    runtime:select
    ,而非实时检出链接。
  • Target not found
    :连接目标标签页,然后使用唯一的URL子串或明确的索引选择或采用它。
  • 所有目标消失:关闭Chromium的调试横幅会断开所有标签页的连接。请通过工具栏重新连接。
  • 中继重启:命名会话会重新获取精确目标,但JavaScript
    state
    和快照引用会重置。警告后可继续操作。
  • 扩展更新后的重置/删除可能会短暂等待目标重新公告;若已完成的清单中不存在旧的中继拥有的目标,Browser Control会忽略该无效标识,不会关闭猜测的标签页。
  • 重复的执行上下文错误:运行一个简短的后续操作,以便Browser Control对页面进行健康检查。它可能会重新创建中继拥有的页面,但绝不会替换不健康的已采用用户标签页;请重置或重新采用该标签页。
  • 登录字段填充超时:先检查,确认选择器或定位器可解析后尝试
    fillInput
    。字符串选择器会递归搜索开放阴影根;闭合阴影根仍不可访问。
  • 下载等待失败:使用fetch加
    fs
    ;扩展支持的Playwright无法保留原生下载工件。
  • 在无限动画的目标上悬停:Playwright可能永远不会认为元素稳定。读取其当前的
    getBoundingClientRect()
    ,当坐标输入合适时使用
    page.mouse.move()
    。
  • Chromium保护的页面(如Chrome网上应用店开发者控制台)可能会断开
    chrome.debugger
    连接。请勿重试或绕过该边界;请打开页面进行手动操作。
如需更深入的中继诊断,请使用
BROWSER_CONTROL_DEBUG=1
重启。调试跟踪绝不能包含表达式、参数、结果、头、Cookie或表单值。
每当Browser Control失败、卡住、替换页面/会话或行为异常时,请创建或更新
browser-control
项目的待办事项,包含Browser Control版本、安全的会话/页面上下文、精确错误、确定性重现步骤、预期与实际行为,以及已尝试的恢复操作。切勿包含凭据、表单值或私人账户数据。