← Back to home@drscrewdriver

dsh-browser-cdp

DSH 浏览器插件(原 dsh-ego-browser):让 Agent 通过 CDP 接入真实 Chrome —— 远程目标序列与激活、实时视频/截图回传、登录导入。Remote-CDP agent browser for DeepSeek Harness.

Stars
21
Language
TypeScript
Created
Sep 22, 2026
Updated
Oct 28, 2026

Introduction

dsh-browser-cdp — 看得见的 Agent 浏览器(CDP 接入)

简体中文 | Français | Deutsch | Italiano | Русский | Español

DSH 0.1.7-rc.1 ~ 0.2.1-rc.4 dsh-better-sidebar >= 0.12.2 (optional) Node >= 22

仓库:github.com/drscrewdriver/dsh-browser-cdp|版本历史见 CHANGELOG.md

版本兼容矩阵

依赖最低版本推荐版本说明
DSH (DeepSeek Harness)0.1.7-rc.10.1.7-rc.1 ~ 0.2.1-rc.4(精确枚举)精确枚举 8 个 rc 版本,engines 与全部 dsh peer 同串(无区间/caret):`0.1.7-rc.1
dsh-better-sidebar0.12.2(可选)≥ 0.17.1未安装时自动回退浮动观察球;< 0.12.2 可运行但外部链接拦截(urlTarget)静默降级
Node.js22—harness 环境自带

DSH 全版本适配说明:本版本(1.2.1 起;1.2.2 起追加 0.2.1-rc.2)面向 DSH 0.1.7-rc.1 ~ 0.2.1-rc.4(精确枚举 8 个 rc):相对 0.1.7 线(v0.17.x)零代码改动,纯依赖/元数据换代——0.2.0 宿主对插件 API 向后兼容,本插件经 ctx.get('conversation').input 门面投递选区(不调 submit()、不 override),不在 0.2.0 变更影响面内。声明式设置面自 0.1.7 引入:插件不再注册任何设置节(ctx.settings 注册类 API 已被宿主删除),改为在 Config schema 上以 .volatile() 标记可配置字段,由宿主设置页自动生成表单;volatile 字段变更通过 loader/volatile-update 事件热生效,无需重载插件。核心宿主 API(defineTool、ctx.tools.register、ctx.subprocess.spawn、ctx.webServer.register、ctx.inject、ModuleLoader CJS factory、cordis.patch.yml)沿用 0.1.2 以来形态。DSH 0.1.2-rc.1 ~ 0.1.5-rc.3 请使用 0.17.x 版本线。

dsh-better-sidebar 适配说明:本插件通过 ctx.betterSidebar 服务(try-catch 防御性获取)注册侧边栏 Tab 并监听外部链接。关键 API 引入版本:

API本插件用法better-sidebar 引入版本
registerTab() / openTab() / ctx.betterSidebarTab 注册 + 打开v0.9.0+
TabDescriptor.single单实例 Tabv0.9.0+
TabDescriptor.urlTarget外部链接拦截v0.12.2+(低于此版本链接拦截静默失效)

DSH 版本支持详情:v0.8.2 → v0.8.3 主要变更:合并 6 个社区 PR(root/xvfb/macOS headless 适配、rc.1 兼容、Windows 稳定性),修复无认证 /api/bcdp/* 路由安全漏洞、无 dsh-better-sidebar 宿主 client 启动失败(#29)、Windows 冷启动回归(#22 引入的 Xvfb 误判),并修复 gateway 设置白名单缺 runtimeArgs/chromeArgs。适配点:client 运行时改名(@deepseek-ai/dsh-client-store)、client 模块注册 id 与装载行名按声明包名、dsh.client.inject 仅声明真实模块图行、webServer 以嵌套注入交付(可选服务),并同步侧边栏 Tab(dsh-better-sidebar)模式。

侧边栏支持(dsh-better-sidebar):当宿主安装了 dsh-better-sidebar(推荐 ≥ v0.12.2)时,实时观察窗注册为侧边栏原生 Tab——「Agent 浏览器」出现在侧边栏「+」菜单中,点击即打开并随侧边栏抽屉固定展示;agent 首次调用 bcdp_* 工具时会自动打开该 Tab(v0.8.5 起按调用会话作用域打开,多会话不再弹错位置)。未安装 dsh-better-sidebar 时自动回退为右下角浮动观察球(#dsh-ego-fab)模式。两种形态共用同一套 SSE 实时推流 / 点击 / 输入 / 下载捕获能力。观察窗还提供一个「弹出窗口」按钮:无头(headless)运行的 agent 浏览器可一键替换为同 Profile 的有头窗口(标签页保留),方便手动接管。

登录态导入(v0.8.5 新增):工具 bcdp_login_import,把你日常 Chrome/Edge/Brave 里的登录 cookie 按域名复制进 agent 浏览器(真实二进制无头启动 + CDP 透传读取,兼容 Chrome 127+ 的 App-Bound Encryption,不做离线解密;源浏览器运行中可选择优雅关闭后导入,窗口下次启动自动恢复)。cookie 值不出现在任何日志与输出中;导入前自动备份源 cookie 库,异常清空自动还原。配合默认的磁盘持久化 Profile,导入的登录态跨重启永久保留。

一款 CDP 浏览器代理:内置 ego 兼容运行时(供 AI Agent 用的 Chromium,来源与许可逐文件见 THIRD_PARTY_NOTICES.md)接入 DeepSeek Harness,以 34 个结构化 bcdp_* 工具驱动浏览器,并配一套实时观察前端口——agent 后台操作网页时,你能像看直播一样看到它正在浏览的每个页面,还能直接操作它。

一点私藏的独特之处(self-observation):agent 用的就是这一个 Chromium——连它操作 DSH 自身(管理会话、任务看板、调设置)时,观察窗也实时显示、你能随时接手。不只是"看得见 agent 在网页上干活",连 agent 操作 DSH 界面本身都是全程可见、可掌控的。

开箱即用:插件包内置 ego 兼容运行时(runtime/,第三方 MIT,来源见 THIRD_PARTY_NOTICES.md)——无需额外浏览器、无需手动构建,--no-sandbox wrapper 随包自带,root / Docker / 无显示器一键跑。


我们的真正优势(不是口号,是能对照代码和竞品核实的能力)

同为把 ego 浏览器接进 DSH 的方案,市面上已有同类插件只做了 3 个工具——一个 run 脚本、一个 help 指南、一个 status 体检,浏览器仍是后台黑盒。本插件走的是另一条路:把黑盒打开,并且一上来就把"看"和"控"的能力做到位。

能力本插件(本仓库)同类插件
结构化工具数34 个,职责单一、可确定性调用3 个(run/help/status)
实时观察窗(CDP JPEG / FFmpeg H.264 双后端 + 标签条 + 历史抽屉)✅ 有❌ 无
监控窗鼠标直接操作真实浏览器(点击/拖拽/滚动回传 CDP)✅ 有❌ 无
观察窗点选元素 → 捕获并引用进对话输入框([CDP-PICKS] 结构化块,人工指认目标)✅ 有❌ 无
worker 单实例守卫 + 崩溃/重复自愈✅ 有❌ 无
下载捕获 bcdp_download / 人机验证检测 bcdp_captcha/bcdp_page_info✅ 有❌ 无
平台自适应(Linux/macOS/Windows 自动探测 + root/无头/--no-sandbox 兜底)✅ 全平台仅 Windows 预览宿主,需手动配
登录态落盘持久化 bcdp_auth_flush✅ 有⚠️ 仅文档级说明

关键差异三条:

  • 看得到:别家是"跑完告诉你结果"的黑盒;我们实时推流,你看着 agent 操作,卡在验证码/走岔立刻发现。
  • 控得住:别家只读;我们监控窗直接驱动同一个 agent 浏览器,需要时你亲手接管(缩放/拖拽/点击),不必打断 agent 重来。
  • 指得准:你在观察窗里点选页面元素,插件捕获其 backendNodeId/tag/id 与语义描述,以 [CDP-PICKS] 结构化块并入对话输入框草稿(同页多元素自动合并编号)——agent 拿到的是精确目标,不用猜"你说的按钮是哪个"。

以上对比基于公开可见的可核实事实:本仓库代码(bin/cdp-cast-worker.mjs 实时推流 + CDP 输入回传、lib/index.js 34 个注册工具、lib/cast-server.js host 桥接、lib/client.js 的 deliverPickToConversation 点选引用)与同类插件公开可见的源码/README。此文档不含对任何他人的贬低——我们只陈述自己多实现并验证了哪些能力。

相对内置运行时本体(一个无头 CLI),我们多做了这些(都可对照本仓库代码核实):

能力说明(对应代码)
观察窗前端口内置运行时本体是无头 CLI(只有 heredoc 脚本 + 文本输出);我们在其上加了 SSE 实时推流 + 标签条 + 历史抽屉 + 监控窗鼠标直操 + 点选元素引用进输入框(bin/cdp-cast-worker.mjs、lib/cast-server.js、lib/client.js 的 createPickControl/deliverPickToConversation),让"看"、"控"、"指"成为一等能力
开箱即用 + 跨平台自足resolveEgoEnv 自动探测 Chrome/Edge/Brave,内置 --no-sandbox wrapper,root / Docker / 无显示器免配置(lib/index.js);不必先装一个 GUI 宿主
健壮性层冷启动自动重试(只重试 CDP 瞬态,不吞真错)、worker 单实例守卫 + 崩溃自动重启、插件卸载 fire-and-forget 不阻塞宿主退出、前端帧缓存上限(withWarmupRetry / makeEnsureWorker / frameCache)
运维型工具bcdp_doctor(环境体检)、bcdp_captcha(人机验证探测)、bcdp_auth_flush(登录落盘)、bcdp_login_import(系统浏览器登录态导入)、bcdp_http(浏览器上下文请求)等,是原生 CLI helper 没有的一层
self-observationagent 操作 DSH 自身界面时同样实时可见、可接手

我们不声称媲美原生 macOS App 的内核级快照或多窗口体验;本仓库解决的是"把同一套浏览器能力带进 DSH + Linux/WSL + 看得见"这件事。


它解决什么问题

通用浏览器不是为 agent 设计的,而 Web 上大量交互(登录态、验证码、动态渲染、表单、需真人会话的站点)只有真浏览器能面对——这正是 ego 浏览器 "让 agent 用你已登录的浏览器,而不打扰你" 的设计初衷。

本插件把它接进 DSH,并把最痛的一点——你看不见 agent 在干什么、也插不上手——用一套观察窗解决:

🌐 小球一点看直播;🟦 标签条切换/关闭;🕘 历史抽屉回看;🔍 缩放拖拽;🖱️ 监控窗直接接管真实浏览器。一句话:让 agent 在浏览器里干活,你在旁边既看得见、又随时能接手。

几个常见的上手场景

  • 文献 / 数据抓取:让 agent 登录知网 / 谷歌学术翻页收集,你在观察窗看着它滚动、点下一页、下载 PDF,中途卡住立刻能发现。
  • 表单与登录:agent 填表到一半,观察窗弹出验证码——你直接接管把验证码点了,再交还给 agent 继续。
  • QA / 冒烟测试:让 agent 在自己产品上点一圈,观察窗等于一台"会说话的录屏",顺手还能回看历史轨迹。
  • 看 agent 操作 DSH 自身(self-observation):agent 在管理会话 / 调设置时,观察窗同样全程可见、可接手。


前置条件

要求说明
Node ≥ 22harness 环境自带
任意 Chrome / Chromium / Brave / Edge自动发现,或 EGO_LINUX_CHROME 指定;root 下用自带 wrapper
DSH + dshx插件装载机制
带图形界面的 DSH Web(观察窗)headless 会话仍可用 bcdp_* 工具,仅无观察窗

安装

方式一:GitHub 直装(推荐)

dsh plugin --profile web add github:drscrewdriver/dsh-browser-cdp
# 也可锁定到某个 commit / tag:
dsh plugin --profile web add github:drscrewdriver/dsh-browser-cdp#v0.9.0

github: 安装由 pnpm 经 codeload.github.com 拉取仓库 tarball,无需向 npm registry 发包;直接使用仓库中预构建入库的 lib/(files 仅含 lib/、bin/、runtime/、cordis.patch.yml、dsh-plugin.json),因此不触发任何构建脚本,也不需要 devDependencies。host 入口 lib/index.js、客户端 lib/client.js、worker bin/cdp-cast-worker.mjs 均随仓库分发。

方式二:本地 tarball / git URL

dshx install dsh-browser-cdp <dsh-browser-cdp.tgz>      # tarball 或 git URL 均可
dshx list                                                # 应显示:[on] dsh-browser-cdp

包名迁移说明:本插件包名是 dsh-browser-cdp(曾用名 dsh-ego-browser、别名 @dsh-external/ego-browser)。DSH Desktop 2.0.5 起增加了「profile 依赖名 == 包实际 name」的一致性校验,若 profile 仍用旧名引用,启动会挂进恢复模式。升级后请把 profile 的 package.json 依赖键 和 dsh.profile.bundles 条目两处都改为 dsh-browser-cdp:

- "dsh-ego-browser": "<旧 github 来源>",
+ "dsh-browser-cdp": "github:drscrewdriver/dsh-browser-cdp",
- "dsh-ego-browser",
+ "dsh-browser-cdp",

观察窗设置中可选 captureBackend=auto|cdp|ffmpeg(默认 auto,当前解析为 CDP)、画质档位、CDP FPS/JPEG 质量/最大宽度,以及 FFmpeg FPS/最大宽度/码率/编码器/自定义路径。插件先检测自定义路径、系统 PATH 和托管缓存;GitHub 下载可用 githubMirror 替换 https://github.com,例如 https://gh-proxy.com/github.com。FFmpeg 码率范围为 500-20000 kbps,低/平衡/高档默认 2000/4000/8000 kbps。

无需宿主侧任何配置:resolveEgoEnv 自动探测 root / 无显示器并兜底。观察窗 host 路由(/api/bcdp/spaces 等)仅在有 HTTP server 时注册,headless 是安全 no-op。

连接方式:CDP 端点 / 本机 ego CLI

连接目标直接在设置里增删改(0.18.1 起,与 0.17.x 线同源):设置卡有两处入口——「起子插件设置」family 区的本插件 tab(装了 dsh-thinking-levels 时),以及插件详情页的设置卡(plugins.bundle.config,不装 thinking-levels 时的独立入口);两处渲染同一份组件。每行支持:启用开关、激活、标签/端点/CLI 路径就位编辑、逐行探测、上移/下移、删除(删激活行会同时清空激活,绝不顺延);底部内联表单直接添加 CDP 端点或本机 ego CLI(第二条本机 CLI 会被拒绝)。写回落在活动 profile 的 cordis.patch.yml,跨重启保留。0.18.1 之前,连接序列只能在 profile 的 cordis.patch.yml links 中手工维护。

设置面板里的连接列表是有序的——顺序即优先级,被激活的那一项驱动每一次 bcdp_* 调用。列表元素有两种类型:

类型它做什么约束
CDP 端点把一个已开调试端口的浏览器地址交给内置运行时(注入 EGO_LINUX_CDP_URL),运行时附着上去可多条
本机 ego CLI由本机 ego CLI 自己驱动它本机的 ego 浏览器——不注入任何端点仅限本机,全局至多一条

为什么需要第二种:注入用的 EGO_LINUX_CDP_URL 是**内置运行时(Linux 线)**的私有契约,macOS 上用 ego lite app 附带的那个原生 ego-browser 根本不读它。所以在只有"CDP 端点"这一种类型时,macOS 用户无法用上自己那个已登录的 ego——只能连远端端口,或让内置运行时另起一个 stock Chromium。本机 CLI 类型补的就是这条路。

解析顺序(cliPath 留空时):显式路径 → PATH 上的 ego-browser → macOS app 包内 helper(/Applications/ego lite.app/…/Helpers/ego-browser)→ 插件内置运行时。

两个默认值值得知道:

  • useSdkPath 默认关闭:关闭时用 CLI 自带的 harness(配套的那套);打开后,探测与每一次工具执行都会注入 --sdk-path <插件内置 harness>。这一点很关键:只在探测时带、执行时不带,脚本首行就会崩 ReferenceError: taskSpaces is not defined(1.2.4 修复的 P0)。若 CLI 不认这个旗标,探测会记一条提示并自动重试一次不带它;此时只有 CLI 自带的 harness 确实暴露 taskSpaces / browser 门面才算就绪,否则显式判失败并给出缺失的门面名——不再假阳性。
  • 就绪探测是 nodejs 通道上的 facade 探针(与干活同一条通道):只检查注入后的 taskSpaces / browser / page 全局,不启动也不等待浏览器,实测约 300 ms;bcdp_status 走同一判据。

双 API 面共存风险

本插件通过注入 SDK 使用 taskSpaces.*(插件侧 API),而 ego-browser skill 脚本使用原生 taskSpace()(app 侧 API)。二者驱动的是同一个 ego,task space 也是共享的:同时用插件工具和该 skill 脚本操作同一空间,可能互相抢控制权。建议二选一,以插件为准;确需跑该 skill 脚本时,先停用本插件的 ego-cli link。

与其它开关的关系:

  • cdpMode:auto 按激活项(两种类型都行);remote 只接受 CDP 端点,激活项是本机 CLI 时会显式报 mode-kind-mismatch;local 走受管本地启动器。
  • remoteEnabled(远端总开关)只管远端 CDP,不影响本机 CLI 连接。
  • allowLocalFallback / localHeadless / localUserDataDir 只作用于 CDP 端点的回落与受管启动。
  • allowProbeColdStart(默认关):就绪探测绝不拉起 ego 浏览器——--status 证明浏览器已在跑才做 heredoc 验证,否则跳过,浏览器推迟到首个真实 bcdp_* 调用才启动(插件挂载/改设置不再弹浏览器窗口)。打开 = 挂载探测预先验证 SDK harness(可能当场弹出浏览器窗口)。

bcdp_doctor 会报告当前序列类型计数、CLI 实际解析到的路径与形态,以及(若同机装了旧版 dsh-ego-browser)一条共存提示。

Lightpanda 后端(可选引擎,beta)

Lightpanda 是面向 AI 的无头文本渲染浏览器(AGPL-3.0,本插件仅作 CDP 客户端使用)。它对插件来说就是一个普通 CDP 端点:设置面板添加并激活后,probe 会从 /json/version 的 Browser: Lightpanda/… 自动识别引擎,badge 与 bcdp_doctor 显示 engine,能力降级自动生效(无需任何配置)。

分系统安装

系统方式
Linux(建议本机直装测试)curl -fsSL https://pkg.lightpanda.io/install.sh | bash(装入 ~/.local/bin,确保 PATH 含它)→ lightpanda serve --host 127.0.0.1 --port 9222
macOS(建议本机直装测试)同上一键脚本(aarch64)→ 同 serve 命令
Windows(内置方案 = WSL2,无原生版)管理员 PowerShell wsl --install → 重启 → wsl --install -d Ubuntu → WSL 内同 Linux 一键脚本 → WSL 内 lightpanda serve …;WSL2 自动把端口映射到 Windows localhost(二进制只存在于 WSL 内,宿主侧没有文件,属预期)
Docker(备选)docker run -d --name lightpanda -p 127.0.0.1:9222:9222 lightpanda/browser:nightly

装好后在本插件设置面板添加 CDP 端点 http://127.0.0.1:9222 并激活即可。可选:export LIGHTPANDA_DISABLE_TELEMETRY=true 关遥测。

能力降级矩阵(识别后自动生效)

能力Lightpanda 上的行为
导航 / JS / 点击 / 填表 / 键鼠 / 快照(AX 树)/ HTTP / 登录导入 / 等待族无损可用
bcdp_screenshotPNG 文本级渲染(无像素引擎,视觉保真低于 Chrome;runtime 本就发 png,零改动兼容)
jev 视觉链(bcdp_jev_frame 等)投影自动改发 PNG;输出带 engine / rendering: text 元数据
观察窗实时画面关闭——无 screencast 且无窗口,worker 不启动采集循环,面板显示降级提示
面板点选(inspect 路径)关闭——无 Overlay 域 / bindingCalled 事件,返回 engine-unsupported(i18n);坐标回退路径保留
bcdp_download关闭——无 Page 下载事件,调用即返回结构化错误(不会挂起超时)
bcdp_auth_flush返回 degraded: true——引擎无 Storage.flushCookies,cookie 仅随引擎进程存活

识别失败的端点(engine: unknown)保持全能力——误判只应"少给",不应"错给"。

工具清单(34 个,前缀 bcdp_,完整索引见 bcdp_help)

类别工具
任务空间bcdp_space(open / close / list / handoff / takeover) bcdp_status
页面读取bcdp_snapshot(语义树) bcdp_page_info bcdp_read_element
导航/标签/等待bcdp_navigate(复用 tab / reload) bcdp_tab(list/current/new/switch/close) bcdp_wait(duration / selector / url / response / load / function / request / event)
交互bcdp_click bcdp_fill bcdp_hover bcdp_drag bcdp_select bcdp_check bcdp_key(type/insert) bcdp_scroll
执行/调试bcdp_js(页面求值) bcdp_cdp(原始 CDP) bcdp_script(多步脚本;verbatim:true = 原 bcdp_cli 的裸 heredoc)
输出bcdp_screenshot bcdp_download bcdp_upload
会话/安全bcdp_auth_flush(登录落盘) bcdp_login_import(跨浏览器登录态导入) bcdp_captcha bcdp_dialog
元工具bcdp_help bcdp_doctor bcdp_http
评审(JEV/Laya,外接判定服务可选)bcdp_jev_status bcdp_jev_frame bcdp_jev_ask bcdp_jev_run bcdp_jev_attempt

1.5.0 迁移表(旧名 → 新调用)

wait / space / escape 三族已按 op 合并,旧工具名移除(不保留别名——别名同样吃 schema 预算,等于没省)。其余工具名不变。

旧调用新调用
bcdp_wait {ms}bcdp_wait {op:"duration", ms}
bcdp_wait_for_selector {selector, state, timeout}bcdp_wait {op:"selector", selector, state, timeout}
bcdp_wait_for_url {pattern, timeout}bcdp_wait {op:"url", pattern, timeout}
bcdp_wait_for_response {url, body, timeout}bcdp_wait {op:"response", url, body, timeout}
bcdp_wait_page {op:"load"|"function"|"request"|"event", …}bcdp_wait 同名 op(op 取值不变,直接搬家)
bcdp_space_open {name}bcdp_space {op:"open", name}
bcdp_space_close {name, keep}bcdp_space {op:"close", name, keep}
bcdp_cli {script}bcdp_script {script, verbatim:true}

op 合并后各 op 的必需参数写在工具描述里;缺参会返回带 op 名的 reason(如 bcdp_wait: selector is required for op=selector),不会静默降级。

工具注入分级(懒注册 / 分级 / 锁定)

34 个工具全量注册时,每个会话每一轮都要为它们的 schema 付费(挂载级实测 description + parameters 序列化 = 24,631 字符 ≈ 7.1K token,1.7.0;1.5.0 曾测为 22,224,增量主要来自分级文档与工具描述互斥引导)。而不碰浏览器的会话一个也用不上。插件因此把「谁能出现在模型面前」收成一处策略(src/toolTiers.ts),分八批注入。

8 批 ≠ 8 个开关。批次是注入时机,开关是用户面(§9.3「开关与批次解耦」)。当前 4 个开关管 8 批:toolBrowse 管 act+observe+wait 三批并按波次放行,toolExtended 管 power+session,toolEscape、toolJudge 各管一批。所以「toolBrowse = 19 个」指的是并集上限,实际第一波只放 13 个。

批次成员schema何时出现
core 常驻(1)bcdp_status401永远在场,不可封印——它是 lazy 唯一的解锁入口
act 动手(13)space / navigate / click / fill / key / hover / scroll / select / check / drag / tab / help / doctor8,762lazy 下首次活动放第一波
observe 观察(5)screenshot / page_info / read_element / js / snapshot3,109第二次活动
wait 等待(1)bcdp_wait(duration / selector / url / response / load / function / request / event)1,976第三次活动
power 重量级(5)raw CDP / http / upload / download / dialog3,692首次活动(同 toolExtended)
session 登录态(3)captcha / auth_flush / login_import1,864首次活动(同 toolExtended)
escape 逃生舱(1)bcdp_script(verbatim:true = 原 cli 的裸 heredoc)1,484首次活动(1.7.0 起不再吃活动闸豁免,见下)
judge 评审链(5)bcdp_jev_*3,343除解锁外,还要求配了 JEV 或 Laya Key
合计3424,631—

表内 schema 与合计均为 1.7.0 挂载级实测(tests/tier-mount.test.ts 的度量用例打印),八批求和 = 24,631 与全量档逐字对齐。

设置项在插件设置页最上方的「工具封印」分块(全部 volatile,翻完当场生效,不重启插件)——一个开关管一组批次,形状照 dsh-prime-memory 的「工具封印」:

  • toolInjection lazy(默认:只常驻 bcdp_status——它是唯一的解锁入口,其余首次 bcdp_* 调用后按开关注入)/ always(挂载即全量,历史行为)。
  • toolBrowse(act + observe + wait = 19)/ toolExtended(power + session = 8)/ toolEscape(script = 1)/ toolJudge(5):开 = 开放注册,关 = 该批从模型工具列表移除。四组各自独立,封一组不波及别组;core 级不给开关(封了就没了活动入口)。

1.7.0 起 escape 不再吃活动闸的例外:bcdp_script 仍是唯一能在一次调用里干完任意多步任务的入口(page / browser / taskSpaces / fetch / cdp 全预置),但它不再常驻——先调一次常驻的 bcdp_status 即可拿到它。豁免的代价是它单独占 ≈1,700 字符/轮(比收窄后的整个常驻面还大四轮),而 lazy 会话里模型本来就要先调 status。如果你需要「冷启动直接写脚本」的旧行为,设 toolInjection = always(或留在 1.6.x)。

行为要点:

  • 封印 = 反注册(工具从模型的工具列表里消失),不是执行期拒绝。解锁信号挂在 bcdp_* 调用的公共出口上,工具体不知道分级存在;bcdp_status / bcdp_help / bcdp_doctor 自身也算活动信号(读了指南就得有工具可用)——但后两者已属 act 批,冷启动下只能靠 bcdp_status 触发。
  • 闲置自动收回:lazy + idleTimeoutMin > 0 时,空闲回收把后台浏览器停掉的同一刻收回扩展面;下次活动重新铺开。always 模式不受影响。
  • legacyEgoToolNames 的 ego_* 别名跟随其 bcdp_* 本体的级别。
  • 当前注入面写在 bcdp_doctor 报告首行(例:tool injection: mode=lazy browse=open extended=open escape=open judge=open judgeKey=none active=yes → live 28 / sealed 6,即一次 bcdp_status + 一次 doctor 之后的面),不用猜。注意 doctor 本身已属 act 批,要先调 bcdp_status 才能拿到它;冷启动时模型面上只有 bcdp_status(live 1 / sealed 33)。
  • 默认档下不碰浏览器的会话只有 1 个工具在场(bcdp_status = 401 字符 ≈115 tok,较 1.5.0 的 4,978 字符 −91.9%);它是解锁入口,调用即按批次铺开其余工具。要回到历史行为把 toolInjection 改成 always(34 件全在场需再配一个评审 Key)。
  • 各档实测注入面(挂载级口径,1.7.0;序列化工具的 name + description + parameters 求和,token 按 3.48 字符/token 折算):默认 1 个 / 401 字符 ≈115 tok → 首次活动(act 批 + power + session + escape)23 个 / 16,203 ≈4,660 tok → 第 2 波(+observe)与第 3 波(+wait)后 29 个 / 21,288 ≈6,120 tok → 全量(always + 评审 Key)34 个 / 24,631 ≈7,080 tok。这些数字由 tests/tier-mount.test.ts 的度量用例当场打印,不靠估算。
  • 五个字段都带 schema .default():volatile 字段的表单描述符直接拿 schema 默认值,不写默认会出现「面板显示 A、运行时用 B」。

取舍提醒:窄工具(一个动作一个工具)是模型写对参数、少重试的护栏。1.5.0 只合并了 wait / space / escape 三族同源冗余(41 → 34,省 1,869 字符 schema):五个 wait 共用同一 preamble 与 timeout 语义、三个 space 本就是同一个 op 工具的碎片、cli/script 是同一 heredoc 运行时。代价是这三族的旧工具名不再存在(见上方迁移表);交互原子(click/fill/key…)不合并,因为它们是模型写对参数的护栏。

任务空间命名与并发隔离(1.6.0)

默认就是会话隔离:不传 space 的调用落在本会话自己的任务空间 dsh-agent-<会话id前8位>,两个会话永远不会共用或抢占同一个浏览上下文。只有显式写 space: 'default'(或 bcdp_space op=open name='default')才会用那个跨会话共享的具名槽。

你怎么写落到哪个空间
省略 space(默认,defaultSpace 未设 = :auto)dsh-agent-<会话id前8位>,每会话独立
space: 'default' / op=open name='default'共享具名槽 default(此后该会话固定用它)
defaultSpace: 'default'(配置项改成具名)所有会话默认都用 default(退回旧行为,全局生效)
无 session 的调用(__global__)default,保证向后兼容
  • defaultSpace(A):volatile 标量,留空/缺失折叠为 :auto(1.6.0 起,此前折叠为 default),热更新不重注册。注意区分两个概念:配置值 cfg.defaultSpace(schema 里那个,可以是 :auto 或具名)与运行时当前会话活值 cfg.activeSpace(命令实际用的 useSpace(...) 入参);前者是设置,后者随会话与显式选择切换。
  • :auto 派生(C)::auto 在两种模式下都派生 dsh-agent-<sid8> —— 空间命名与 isolateSpaces(那个只管 Profile 落盘还是内存沙箱)是两条独立的轴。会话 id 会先剥掉 session- 前缀再取 8 位:真实 DSH id 形如 session-52adc205-…,直接 slice(0,8) 会得到 session-,让所有会话撞成同一个空间——这正是隔离失效的经典坑,已用 sessionSpaceToken() 修掉并有回归测试。
  • 会话级隔离(B):活动空间追踪器按 sessionId 键控成一张槽位表(Map<string,Slot>,无 session 的调用落 __global__ 槽保持向后兼容)。每个 bcdp_* 调用一进来就 setCurrentSession(callingSessionId(exec)),所以并发多会话各开各的空间互不漂移——A 会话重启空间不波及 B。
  • 不要再手动建编号空间:不需要 #4/#5 这类手工命名,隔离已经由会话派生自动完成;需要共享就走显式 default。

跨调用稳定定位(D1)

bcdp_snapshot 现在带稳定 locator:输出里每个可交互节点除了 [ref=N] 还附 loc=css:… / loc=role:… / loc:href:…。跨调用请优先用 loc= 而非 @N——@N 每次快照重新编号且其 ref 映射只活在取快照的那个子进程里,而 loc= 稳定、能跨子进程命中(bcdp_click / bcdp_fill 直接吃 loc=...)。这是插件层解法,未改内置运行时 SDK 的 ref 编号生成。

navigate 卡死自愈(F)

bcdp_navigate 包了一层 watchdog:当一次导航因页面主线程挂死而 eval 超时 / no active tab 时,先开一个空白 tab 再关掉挂死那个(顺序关键:先关会触发末-tab 守卫留下死 tab),然后重试一次,结果附 recoveredFrom(正常路径为 null)。重试硬上限一次,二次仍卡如实返回,不用反复换 tab 掩盖真错误。

观察窗怎么用

右下角 🌐 常驻小球 → 点开:

  • 主画面:agent 当前页面实况;点击/拖动/滚轮直接操作页面,Ctrl+滚轮缩放视图、Ctrl+拖动平移,双击复位。点击画面后可直接键盘输入,支持中文 IME、粘贴、Tab/Enter/方向键及 Ctrl/Cmd 快捷键。
  • 选择元素(点选引用):点一下工具条进入点选模式,再点实时画面里的任意元素——插件捕获其 backendNodeId/tag/id 与语义描述,以 [CDP-PICKS] 结构化块并入对话输入框草稿;同一页面的多次点选自动合并进同一块并依次编号,agent 按编号精确取用。浮动观察球与侧边栏 Tab 均可用。
  • 标签页条:顶部横排,点选切换,× 关闭。
  • 历史抽屉(🕘):按时间回看访问轨迹。
  • 操作时下方网址行就地显示提示,2 秒后恢复。
  • 面板关闭、sidebar Tab 隐藏或组件卸载后,1.5 秒宽限结束即停止画面生产。仅把 DSH 窗口切到后台不会停止串流,避免回前台时反复重建 WGC/FFmpeg;异常关闭由 120 秒 worker lease 超时兜底。

画面后端

  • cdp:Page.startScreencast JPEG,默认 20 FPS。每个源帧立即按 Chrome 提供的帧 ID ACK,只保留最新待发帧;仅捕获当前观看标签,静态页恢复截图默认 3 秒一次。
  • ffmpeg:Windows 以 gfxcapture(hwnd) 直接采集目标 Chrome 窗口的 D3D11 surface;其他平台使用显示来源 crop。随后编码 H.264 fragmented MP4 → HTTP 二进制 chunk → MediaSource <video>,不经过 Base64/SSE。
  • auto:默认选择 CDP,不检测成功也不会自动下载 FFmpeg。FFmpeg 安装并通过能力检查后才可选择;已保存的 FFmpeg 后端若后来失效,本次观察会回退 CDP 并展示原因。
  • Windows FFmpeg 必须包含 gfxcapture。插件按 browser PID、target title 和 CDP window bounds 匹配 HWND;窗口移动或被遮挡时仍捕获目标页面,并且不允许回退到桌面录制。若 target 是同一 Chrome 窗口里的后台 tab,会明确报错而不是展示当前可见 tab 或抢用户焦点。macOS 首次使用需要“屏幕录制”权限;X11 需要 Chromium 与 FFmpeg 共享 DISPLAY;Wayland 缺少 Portal/PipeWire 输入时会提示切回 CDP。

托管 FFmpeg 安装到 ~/.dsh/cache/ego-browser/ffmpeg/,不会写入插件目录。Windows/Linux 使用固定 BtbN release tag;macOS 使用固定的 ffmpeg-static GitHub release 资产(其 Intel/Apple Silicon 二进制分别来源于 Evermeet/OSXExperts)。所有下载均固定资源 SHA-256,只提取 FFmpeg 主程序,不安装 ffprobe 或 ffplay。Windows/Linux 解包使用系统 tar;缺少时会在下载前明确报错。

登录态说明:多任务空间 Cookie 相互隔离,请在对应空间内登录。重启 DSH 后运行期登录态被清空(Chrome 运行期 Cookie 仅优雅关闭时落盘),需重登——扫码很快。

工作原理

  • 工具层:每个工具把参数拼成 JS 脚本,经 ctx.subprocess 用 ego-browser nodejs 喂给 stdin 运行,宿主经 CDP 驱动共享 Chromium。结果以 @@DSH_RESULT@@ 哨兵行解析。所有 bcdp_* 经进程内互斥锁串行化,错误统一归一。
  • 观察窗:lib/client.js 管理 watcher lease、JPEG <img> 与 MSE <video>;lib/cast-server.js 代理元数据 SSE、watch API 和带背压的二进制视频;worker 中 CaptureManager 保证同时只有一个活动后端和一个当前 target。CDP 控制面(标签、viewport、输入、验证码)独立于画面后端。

JEV 风格管道:让 LLM 把控浏览器循环(阶段 10)

把"一帧 ≡ 截图 + 编号 DOM + 意图 + 操作进度"这个帧契约,连同"判定只回编号、绝不给选择器"的判定接缝,落地成一套可跑、可测、可归档的管道。判定由外部的 Laya / JEV 判定服务承担(见下),循环由本插件驱动。

四个工具(宿主侧 defineTool,判定请求从插件进程发 HTTP,不经过 agent 会话):

工具干什么何时用
bcdp_jev_status先跑这个:判定链可用性 + 配置体检,不发任何请求怀疑配置/链路问题时第一步
bcdp_jev_frame采一帧(截图 + 编号候选 + 意图),看判定器会看到什么调试帧内容、核对候选编号
bcdp_jev_ask组装请求体;dryRun 默认 true,可指定 round=control|chapter|pick|evaluate 逐级看,确认无误再发想先看清楚判定请求再发
bcdp_jev_run跑整条循环,返回逐步 trace(每步的候选数 / top / 预算 / 命中)真要让它动手

默认链路是 laya → rule。JEV 目前无法注册,所以默认不写它;将来可用时把 jev 加回 judgePrefer 跳序并填 jevUrl 即可。bcdp_jev_status 在缺 key 时会主动打印本地起法(ENGINE=laya … uvicorn laya_api.main:app,端口 8000 而非 7789,ALLOW_DEV_LOGIN=true 建 key)。不可用的跳跳过不调用(laya 强制鉴权无匿名分支,缺 key 必然 401),每一跳的跳过与失败都进 trace,绝不静默回落;refuse 是结果不是异常。

三级缩小(让 LLM 把控进程,而不是一次猜全部):

  1. control —— 要不要动手(固定 5 项:pick_button / sleep / next / prev / done);
  2. chapter —— 哪个章节(按 AX 容器聚簇,从 childIds 父链挑最近的结构化祖先,如 form#1 / form#2;单章节时此轮跳过);
  3. pick —— 章节内选哪个编号 + 一个风险度 score。

分章节不是装饰:阈值按候选数分桶,把 20 选 1 拆成「几选 1 × 几选 1」会让两轮都落进更严的桶(top≥0.5 且 top−second≥0.15),比一次性 20 选 1(top≥0.6)更可控。判定器只回编号,坐标/选择器由执行层每次重新量(DOM.getBoxModel + 点击前 DOM.getNodeForLocation 命中复核),帧里的矩形绝不作为点击依据。

判定上下文是隔离的:只允许 INTENT / PROGRESS / FRAME / HISTORY 四段,绝不带 agent 会话 session 前缀;多出任何一段都在组装时直接抛错(不靠约定,靠断言)。PROGRESS(步数 / 各章节尝试与结果 / 真正成功过的动作 / 剩余预算)由循环自己机械生成,不让模型写——模型写的进度是第二难发现的幻觉通道。

评估开关 jevEvaluate(默认开,设置面板可关):每一步执行后,判定器再判一遍进度是 inprogress / done / fail。fail 或「未验真的 done」不猜下一步,而是升级为 escalate——带一组有序的 RecoveryOption(如先 reload 刷新,因为陈旧渲染会藏住已写入的确认),交回 LLM 决定恢复还是停手。

循环终止态:done(对照 successCriteria 自验)/ blocked(预算拦)/ exhausted(预算耗尽,带 exhaustedKind)/ stuck(同锚同动作连续 3 次无变化)/ unavailable(无判定器)/ error / escalate。

现状诚实记录:本阶段已证明协议、阈值、终止、分章节、组装、隔离正确(bcdp_jev_* 单测覆盖),但尚未在真实浏览器上端到端跑过(T10.22 待做);判定准确率未经实测(阈值分桶是标定用的,不是选得对的证明)。存档的被操作元素会保留 class 等定位特征(只剥离检视器外壳 token),供 escalate 时回传给 LLM 恢复。

开发

源码在 src/(TypeScript),构建产物在 lib/(host + client bundle)与 bin/cdp-cast-worker.mjs(worker bundle)。

pnpm typecheck   # tsc 类型门禁(tsconfig.json 主 + tsconfig.client.json 客户端)
pnpm test        # vitest 单元测试
pnpm run build   # tsdown 三 bundle:lib/index.js + lib/client.js + bin/cdp-cast-worker.mjs

直接改 src/(src/index.ts 工具层、src/client/index.ts 前端、src/worker/cdp-cast-worker.ts worker)。新工具在 registerActionTools 里按 t({...}) 加,并在 bcdp_help 索引(src/help.ts)补一条,跑 pnpm typecheck && pnpm test && pnpm run build。lib/ 与 bin/cdp-cast-worker.mjs 是构建产物(预构建入库),不要手改。

node_modules/ 仅含指向 DSH checkout 的符号链接(编译期类型解析);运行时由 harness 解析 @deepseek-ai/dsh-tools。

已知限制(诚实说明)

  • Windows:插件层已做 v0.4.0 适配;内置运行时的宿主对 Windows 仍以社区适配为主,复杂多步流程稳定性可能弱于 macOS。
  • FFmpeg 平台捕获:Windows 已使用 gfxcapture(HWND),需要包含该 filter 的新构建;PATH 中的旧 FFmpeg 会被跳过并提示下载兼容版本。Linux 使用 x11grab、macOS 使用 avfoundation display crop;macOS ScreenCaptureKit、Wayland Portal helper 属后续增强。
  • 安装环境:本仓库的 DSH peer 包不全在公共 npm registry。普通 pnpm install 可能在解析 @deepseek-ai/* peer 时失败;DSH profile 安装应提供这些 peer。CDP 不依赖 FFmpeg,也不会在插件安装阶段下载二进制。
  • 快照质量:Linux 用 CDP DOMSnapshot 重建语义树,非 macOS 内核级,复杂 iframe/画布场景可能降级。
  • 宿主可靠性(Linux):未合并的社区 PR,跨 CLI 调用间可能丢 tab/空间状态;插件已内置防御,简单流程稳定,复杂流程可能需重试。
  • 登录态持久化:Chrome 运行期 Cookie 仅优雅关闭时落盘,强杀重启需重登。
  • 输出 schema 为宽松 additionalProperties: true,客户端以实际返回值为准。

许可与署名

插件本体 MIT。内置运行时嵌入第三方 MIT 代码;可选下载的 FFmpeg 构建涉及 GPL-3.0-or-later 义务。使用或再分发前请阅读构建来源的许可证与源码获取信息,来源与署名详见 THIRD_PARTY_NOTICES.md。

供应链与权限说明

为目录收录与审查提供的确定性事实:

  • 运行文件:lib/(构建产物,由 npm run build 从 src/ TypeScript 以 tsdown 确定性生成)、bin/(worker 与 ffmpeg-probe 的可执行入口脚本)、cordis.patch.yml(装配层)、dsh-plugin.json(manifest)。*.map 仅为调试用 sourcemap,不参与运行,已声明排除。
  • 原生/可执行工件:runtime/ 内置 ego 兼容运行时(第三方 MIT,来源与逐文件清单见 THIRD_PARTY_NOTICES.md)——它是本插件的核心功能(自带受管 Chrome/CDP 宿主),属有意携带的可执行工件,非构建副产物。runtime/PATCHES.md 记录对内置运行时的全部本地补丁。
  • 依赖:运行时依赖仅 @deepseek-ai/schemastery(由 DSH 宿主提供对等实现);peer 依赖全部为 @deepseek-ai/dsh-* 宿主服务。客户端 bundle 的外部模块由宿主模块表解析,不携带 npm 运行时依赖。
  • 外部服务:无遥测、无外部 API 调用。唯一的网络行为是可选的 FFmpeg 安装器按用户指令从 GitHub(或用户配置的镜像)下载构建件,来源校验与许可义务见 THIRD_PARTY_NOTICES.md。
  • 失败边界:宿主无 webServer(TUI/headless)时 watch 路由安全跳过;worker 启动失败时 watch 路由返回 ok:false 的 JSON 而非挂起;浏览器进程随宿主 teardown 一并终止(--stop fire-and-forget,不阻塞宿主退出)。
  • 权限:manifest permissions 为空——工具集的文件读写被限定在 ego 自管的空间目录与用户工作区,网络访问经由受管的 agent 浏览器而非宿主进程。

友链

同为 DeepSeek Harness 插件生态的作品,互相安利: