angelky7/dsh-browser-control ↗★ 42

@angelky7/dsh-browser-control

建立WebSocket桥接以操控Chrome浏览器扩展 适合需要让智能体通过专用浏览器扩展自动执行网页操控任务的用户。

套件
@angelky7/dsh-browser-control
相容性
待驗證
Harness 依賴範圍
0.2.0-rc.2 || >=0.1.0-rc.1 <0.3.0-0
Cordis 依賴範圍
~4.0.4
版本
1.1.0
授權
AGPL-3.0
最近更新
2026年10月24日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:angelky7/dsh-browser-control

使用

  1. 装好即默认开启(v1.0.6+),不需要手动启用
  2. Chrome 扩展自动连接(端口 9777,Token 默认 dsh-local)
  3. 对话说自然语言,Agent 自动操控浏览器

「设置 → 插件」里有没有这张卡片,取决于宿主版本(issue #5)

npm 发布的 bundle 只含宿主侧 lib/index.js,没有浏览器端模块,所以卡片不是插件自己画的:

宿主设置页改配置的办法
dsh 0.1.7+ / 0.2.x(dsh-settings 0.1.7-rc.2 起)有——宿主按插件自身 Config schema 自动投影出这一节设置页,或 profile 覆盖层
dsh 0.1.2 及更早没有条目(没有客户端半区认领命名空间,宿主就不渲染)只能改 profile 覆盖层 $DSH_HOME/profiles//cordis.patch.yml

两边都能改的就是配置文件,写法见 dsh-config/README.md。

访问 http://127.0.0.1:9777/ 查看连接状态。

不想让它点,只想让它看:只读档与脚本开关

profile 覆盖层里给 browser-bridge 配两项(完整写法见 dsh-config/README.md):

config:
  readOnly: true      # 只读:点击/输入/按键/导航/滚动/标签页操作/视口模拟全拒
  allowScript: false  # 再把 browser_evaluate(在页面里跑任意 JS)关掉

被拦的命令不会发到浏览器(零副作用),错误码 blocked_by_policy,文案写明改哪个字段。 注意 readOnly 单独打开不等于拿掉了任意脚本能力:browser_evaluate 能做其它工具能做的所有事, 所以「能不能执行脚本」是单独一项。这两个开关只管模型那条工具面,HTTP 面(带 token)不受影响。

agent 开的标签长什么样(自动归进 DSH 分组)

Agent 新建的标签页会自动收进当前窗口的一个 DSH 蓝色标签分组里(每个窗口一组),所以你扫一眼就知道哪些页面是机器开的、哪些是自己开的,事后也能一把清掉:

  • browser_tabs action: "ungroup" 拆掉本窗口的组(带 windowId 只拆那一个窗口,不带则所有窗口)——只拆标签归属,不关标签。
  • browser_tabs action: "open" 加 group: false 可以显式让新标签留在组外;action: "group" 能把早就存在的散标签拉进来。
  • 归组是锦上添花:Chrome 版本太旧、权限没给、或刚建标签就被用户拖动导致失败时,开标签本身照常成功,结果里带 grouped: false 和原因。分组动作不改页面内容,重试安全。
  • browser_tabs action: "close" 一次可以关一个(tabId)或一批(tabIds);一批在链路上只发一条命令。结果把每个 id 的去向都写出来:closed / alreadyGone(早就没了=幂等成功,重试安全)/ failed(带原因)——部分失败不会被吞掉,单数与批量同一个形状。

一键冻结:扩展弹窗里的「锁定」

点扩展图标,按钮行中间那个「锁定」,单击即生效:此后扩展对模型拒绝一切浏览器操作 (点击、输入、按键、导航、执行脚本全拦),而 WebSocket 连接照常——调用方看到的是 「连得上但被冻结」,不是掉线,所以不会误当作网络问题去重试。

  • 解锁只有一条路:在同一个弹窗里点「已锁定」。桥接侧没有任何解锁命令,模型的工具面里也没有。
  • 冻结状态落在 chrome.storage.local,所以 MV3 把 service worker 回收掉也不会「自己解冻」。
  • 万一读不出这个状态,扩展按「已冻结」处理(fail-closed):一个以为锁着、其实没锁的 panic switch 等于没有。这时拒绝文案与弹窗都会写「锁状态读不出来」,不会说成「已被用户锁定」, 而你照样能在弹窗里点一下把状态写明确。
  • 图标徽标在上锁期间显示红色 LOCK,不会继续显示绿色的 ON 骗人。
  • 为什么客户端而不是桥接侧管这件事:桥的 token 就写在 profile 的 cordis.patch.yml 里,模型读得到也改得动; 把「冻结」放在浏览器里、且只留一个人工通道,才真的挡得住。

用脚本直连 HTTP(curl 等)

/api/command 与 /api/cleanup 现在要求 Token,用的就是扩展连 WS 时那个值:

curl -X POST http://127.0.0.1:9777/api/command \
  -H 'Authorization: Bearer ' \
  -H 'Content-Type: application/json' \
  -d '{"command":"nav","params":{"url":"https://example.com"}}'

也可以写 ?token= 或 X-Token: 。不带、或带错,一律 401;/ 与 /api/status 仍是免鉴权的只读信息。之前「不带 Token 也能调通命令面」的行为已经失效(issue #3 / #6)。状态页上的清理按钮会要求你把 Token 输一次,只记在浏览器会话里。