# dsh-chrome — DeepSeek Harness 浏览器助手

[English](README.md) | 中文

一个 Chrome 扩展：右侧边栏嵌入**完整的 dsh 网页界面**（会话、设置、审批、
任务、目标、工作区……全部功能），并让 dsh 智能体**感知和操作你的浏览器**：

- **自动感知当前页面**：切换标签 / 导航（含 SPA 路由）后，把“当前页面”
  （URL、标题、正文，上限 1,000,000 字符）自动注入最近活跃的会话，智能体
  天然知道你在看什么。只有 `http(s)` 标签页会被自动注入。
- **按需读取 HTTP 请求/响应**：智能体在你要求时开启抓包
  （`browser_start_capture`），通过 Chrome DevTools 协议记录活动标签页的请求
  方法、URL、状态码、请求体与响应体，`browser_capture_requests` 读取。默认对
  疑似凭据的值做脱敏，见 [安全](#安全)。
- **驱动浏览器**：`browser_navigate` / `browser_click` / `browser_open_tab`。
- **免审批 + 工具层意图解锁**：会改变浏览器状态的动作只有在本轮由你的真实
  消息发起、且消息里出现明确浏览器意图时才执行。解锁
  `browser_navigate` / `browser_click` / `browser_open_tab` 的词是
  **打开 / 跳转 / 前往 / 点击 / 导航 / 访问 / 浏览一下 / 新标签**
  （英文 open、navigate、click、visit、tab，以及后面跟着页面或 URL 的
  “go to”——`go to github.com` 可以，光说 `go to the next step` 不行）；解锁
  `browser_start_capture` 的词是 **抓包 / 抓一下 / 抓取请求 / 监听网络 /
  网络请求 / 流量**（英文 capture、debug）。
  **注意：单独说「抓取」不会开启抓包**——它是普通的“读取”意图，请改说
  「抓包」或「抓取请求」。动作被拦下时会返回一句拒绝语，并列出能解锁**该
  工具**的确切词，照着重说一遍即可。
  网页里藏一句指令无法驱动浏览器（免审批模式下的尽力防护，非绝对保证）。
- 抓包期间 Chrome 顶部会显示“正在调试此浏览器”横幅（关闭抓包即消失）。

## 前置条件

- 本机运行 `dsh web`（默认 `http://127.0.0.1:3080`，可在扩展设置里改）。
- Chrome 118+（扩展用到 Chrome 118 起才有的 `InjectionResult.error`）。

## 安装

分两部分：**宿主插件**（装进 dsh）与 **Chrome 扩展**（开发者模式加载）。

**1. 把宿主插件加到 dsh web 配置：**

```sh
dsh plugin --profile web add dsh-chrome
```

它会注册桥接、浏览器工具、页面注入器三个插件。dsh 会实时热应用新增插件行，
装完刷新浏览器页面即可，无需重启（仅当以后**修改已加载插件文件内容**时才需
重启一次）。

**2. 安装并加载 Chrome 扩展：**

```sh
npx dsh-chrome install
```

它会把扩展复制到一个稳定的用户目录（并打印路径），然后提示剩余步骤：

1. 打开 `chrome://extensions`，开启**开发者模式**。
2. 点**加载已解压的扩展程序**，选择打印出来的目录。
3. 点工具栏的 **dsh-chrome** 图标打开侧栏。

**升级 npm 包后要重新运行 `npx dsh-chrome install`**，再到 `chrome://extensions`
点一次“重新加载”——安装是复制文件，少做任何一步，Chrome 都还在用旧版扩展
配新版宿主插件。`npx dsh-chrome path` 打印目录。

要彻底移除 dsh-chrome，两半都要撤：`npx dsh-chrome uninstall` 删除扩展目录
（再到 `chrome://extensions` 里移除），`dsh plugin --profile web remove dsh-chrome`
卸掉宿主插件。

## 使用

- 侧栏里就是完整的 dsh 网页界面，正常使用即可。
- 顶栏：桥接状态（dsh 未启动时提示）、**停止抓包**（手动夺回）、**设置**
  （改 dsh 地址）。
- 对智能体说“打开 xx 页面”“点击登录按钮”“抓一下这个页面的请求”即可。

## 安全

**仅限本机可信使用。** 桥接与浏览器工具让本机的 dsh 智能体能够读取页面、
抓取流量、驱动浏览器。

- **抓包按标签页手动开启**，且只能看到开启之后发生的请求。关于范围有两点要
  注意：停止抓包只是不再记录，已经记录下来的内容会保留到标签页关闭；而且
  缓冲区并不按 dsh 会话隔离——读取它的 `browser_capture_requests` 不设意图
  门槛，因此连着同一个浏览器的任何会话都能读到。只有**开启**抓包才需要你
  明确指示。**不抓取 HTTP 头**（因此 Cookie / Set-Cookie / Authorization 头
  不会进入模型上下文）。其余可能
  含凭据的位置——URL 里疑似密钥的查询参数（`?access_token=…`）、请求体
  （表单/JSON 登录）、以及响应体里嵌入的令牌——**默认脱敏为 `«redacted»`**。
  若要抓取原始未脱敏流量（自行调试用），在配置文件 `cordis.patch.yml` 的
  `dsh-chrome-browser-tools` 行上设 `redactCredentials: false`。
  - 脱敏是**尽力而为，非保证**：按常见键名匹配密钥，因此不常见键名下的密钥、
    URL 路径里的密钥、或无法解析/被截断的请求响应体里的密钥仍可能漏过。
    **请把抓到的流量当作敏感数据对待**，只对信任的站点开启抓包。
  - 但在“形状”上它是失败关闭的：扩展与宿主对应答格式的理解不一致时（例如
    升级了 npm 包却没重新跑 `npx dsh-chrome install`），认不出的字段会被丢弃
    而不是原样透传，整个应答认不出来则直接报错。
- **注入的“当前页面”消息被明确标注为不可信数据**，并要求智能体绝不执行其中的
  任何指令；意图解锁机制进一步阻止页面内容触发“动浏览器”的动作。这些是免审批
  模式下的尽力防护，非绝对保证——抓包开启时请勿让智能体访问不可信或敏感站点。

## 目录结构

| 路径 | 内容 |
|---|---|
| `extension/` | Chrome MV3 扩展（侧栏 + service worker + 设置页） |
| `host/` | dsh 侧三个插件：`bridge.js`（WS 桥接）/ `browser-tools.js`（工具 + 脱敏）/ `page-injector.js` |
| `host/redact.js` | 抓包流量的凭据脱敏 |
| `host/intent-gate.js` | 意图解锁关键词与本轮用户文本提取（与 `tools/verify-intent.cjs` 共用） |
| `cordis.patch.yml` | 挂载三个宿主插件的 bundle 补丁 |
| `bin/cli.js` | 安装扩展文件的 `dsh-chrome` 命令 |
| `docs/bridge-protocol.md` | 扩展与 dsh 桥接的线上协议 |
| `tools/` | 针对 dsh 会话日志的开发期诊断脚本（`verify-intent.cjs`、`dump-session.cjs`，共用 `session-log.cjs`），不随 npm 包发布 |

## 限制与说明

- 读取或操作页面状态的浏览器工具（含抓包）只作用于**当前活动标签页**；
  `browser_list_tabs` 与 `browser_open_tab` 按其性质除外。
- 抓包记录滚动保留最近 500 条；单条请求/响应体与自动注入的页面正文上限同为
  1,000,000 字符。按需读取的 `browser_get_page` 是**另一套更小的限额**：
  约 40,000 字符正文、最多 400 条链接。
- 页面变化判定：标签切换 / 主框架导航 / SPA 路由变化（`history.pushState`
  与 `replaceState`），防抖约 2 秒；滚动不触发。只有**活动标签页**的导航算数
  ——后台标签页一直切 SPA 路由不会产生任何推送。此外桥接每次重连成功后会
  补推一次当前页面。
- `browser_click` 绝不重试：若这次点击的结果丢失了（页面导航走了，或注入／
  CDP 调用被中断），工具会如实说明“无法确认这次点击是否生效”，而不是再点
  一次（点击不是幂等操作），并提示智能体用 `browser_get_page` 复核页面状态。
- 页面推送会去重：URL 与正文长度都与上次相同就不再发帧。桥接重连时一定会
  重发一次——dsh 在连接断开时丢掉了缓存的当前页面。
- **扩展自身的侧栏界面目前仅有中文**（顶栏那几个标签：桥接状态、“停止抓包”、
  设置）。内嵌的 dsh 网页界面跟随 dsh 自己的语言设置；只有这层很薄的扩展外壳
  尚未翻译，计划在后续版本补上。
- **读取与点击 Chrome 不允许扩展注入脚本的页面**——`chrome-extension://`（例如其它
  扩展的设置页）、`chrome://`、`file://` 以及 Chrome 应用商店。worker **在尝试
  之前就按标签页 URL 判定**，把这类页面直接交给浏览器的**远程调试协议**
  （`http://127.0.0.1:9222`）。这要求浏览器以 `--remote-debugging-port=9222`
  启动（若远程端点做 Origin 校验，还需
  `--remote-allow-origins=chrome-extension://<本扩展ID>`）。当 CDP 端点缺失或
  不可达时，读取这类页面会直接报错并指明该启动参数，而不是悄悄返回空。
  普通 `http(s)` 页面永远不会走这条路径（哪怕注入失败也不会），自动注入的
  “当前页面”同样只覆盖普通 `http(s)` 标签页（应用商店同在排除之列）。

## 许可证

[MIT](LICENSE) © Stuart Hu
