# dsh-playwright

[English](README.md) | 中文

你可以和你的 DeepSeek Harness AI 一起使用一个真实浏览器。每个 DSH 会话拥有一张隔离的 Playwright 页面：你在 Browser Use 面板中观看和操作，模型读取、看图、点击、输入和导航的也是同一张实时页面。

![用户和 AI 在 DSH 中共同操作同一个 Playwright 浏览器](assets/dsh-playwright.png)

## 你和 AI 可以做什么

Browser Use 面板与模型工具操作同一份页面状态。

- **一起观看：** Chrome CDP 通过仅限 loopback 的二进制 WebSocket 推送重绘帧，客户端绘制到稳定 canvas。面板与聊天共享宽度，分界线可按比例拖动。
- **导航：** 打开 HTTP(S) URL，刷新当前页面。
- **读取页面：** 获取 URL、标题、可见正文，以及可见链接、按钮、输入框等交互元素的临时 ID。
- **鼠标操作：** 按元素 ID 或截图坐标定位；移动鼠标；点击、按下和释放左键、右键、中键。
- **拖拽：** 使用同一个鼠标键依次发送 `down`、一个或多个 `move`、`up`。
- **键盘操作：** 输入文字；按 `Enter`、`ArrowUp`、`Control+A`、`Meta+K` 等按键或组合键；通过 `down`／`up` 持续按住和释放游戏按键。
- **查看截图：** 通过 DSH 原生图片链路向支持图片的模型返回持久 PNG。
- **确认点击：** 可在上次成功点击的位置后处理绘制 RGB 方框；用户侧面板有独立的“显示上次点击”开关。
- **按会话隔离：** 每个 DSH 会话拥有独立 BrowserContext 和 Page；Chrome 意外退出后会恢复最后一个顶层 URL。

## 模型工具

### `browser_navigate`

打开 URL 并返回语义快照。

```json
{"url":"https://example.com"}
```

### `browser_snapshot`

返回有界的页面正文和 `e1` 等元素 ID。ID 只对当前快照有效；导航或动态界面变化后应重新获取快照。

### `browser_click`

默认执行左键单击。必须提供 `element_id`，或同时提供 `x`、`y`。

```json
{"element_id":"e1"}
```

```json
{"action":"click","button":"right","x":640,"y":400}
```

只移动鼠标：

```json
{"action":"move","x":640,"y":400,"steps":20}
```

拖拽：

```json
{"action":"down","button":"left","x":200,"y":200}
{"action":"move","x":600,"y":400,"steps":30}
{"action":"up","button":"left","x":600,"y":400}
```

支持的鼠标键：`left`、`right`、`middle`。支持的动作：`click`、`down`、`move`、`up`。

### `browser_type`

替换一个可见可编辑元素的值，并可用 Enter 提交。

```json
{"element_id":"e3","text":"DeepSeek Harness","submit":true}
```

### `browser_keyboard`

对当前焦点模拟键盘事件，也可以先聚焦指定元素。

```json
{"action":"type","value":"hello","element_id":"e3","delay_ms":50}
```

```json
{"action":"press","value":"Control+A"}
```

```json
{"action":"down","value":"ArrowRight"}
{"action":"up","value":"ArrowRight"}
```

支持的动作：`type`、`press`、`down`、`up`。

### `browser_screenshot`

通过 DSH 原生 attachment 和 image block 链路返回 PNG。当前模型路由必须声明图片输入。

可以在不修改网页 DOM 的情况下标记上次点击：

```json
{
  "show_last_click": true,
  "marker_r": 0,
  "marker_g": 255,
  "marker_b": 0,
  "marker_size": 60
}
```

RGB 通道是 0–255 的整数。`show_last_click` 是可选参数，默认 `false`。

## 要求

- DeepSeek Harness `0.1.0-rc.6`。
- Node.js `^22.19.0` 或 `>=24.0.0`。
- Host 已安装 Google Chrome 或 Chromium。
- 使用 `browser_screenshot` 时，当前模型路由必须支持图片输入。

## 安装

发布后安装：

```sh
dsh plugin --profile web add dsh-playwright
dsh web --port 3081
```

本地开发：

```sh
pnpm install
pnpm run verify
dsh plugin --profile web add link:/absolute/path/to/dsh-playwright
dsh web --port 3081
```

使用页面右侧边缘的 **Browser Use** 按钮展开或收起面板。拖动左侧分界线可把 Browser Use 占比调至 10%–80%。超过 55% 时，插件会收起 DSH 最左侧 Workspace／会话选择栏，但保留中间聊天区；降到 48% 以下时，仅恢复由本插件收起的选择栏。

## 多模态模型配置

配置 `llm-pi-ai` 路由时声明图片输入：

```yaml
llm-pi-ai:
  providers:
    my-provider:
      models:
        - id: my-vision-model
          name: My Vision Model
          input:
            - text
            - image
```

声明图片输入只是对端点能力的配置；如果端点实际不接受图片，请求仍会被 provider 拒绝。

## 配置

- `browserExecutablePath`：Chrome/Chromium 可执行文件路径；省略时检查常见 macOS/Linux 路径。
- `viewportWidth` / `viewportHeight`：共享页面视口。
- `navigationTimeoutMs`：Playwright 操作超时。
- `actionSettleMs`：操作完成后返回状态前的等待时间。
- `screencastQuality`：CDP screencast JPEG 质量，范围 30–90，默认 60。
- `maxSnapshotChars` / `maxSnapshotElements`：语义快照上限。
- `allowPrivateHosts`：设为 `true` 时允许 localhost／私网目标，默认 `false`。

## 安全与生命周期

RPC 与 screencast 端点只允许 loopback 同源客户端。默认拒绝 localhost、私网 IP、解析到私网的 hostname，以及网页发出的私网子资源请求。只有可信任务才应启用 `allowPrivateHosts`。

插件按需启动 Chrome。每个 DSH 会话拥有独立 BrowserContext 和 Page；插件卸载时等待已排队操作结束，再关闭 Chrome。Chrome 意外退出时，下一次操作会启动新浏览器，并把受影响的会话恢复到最后一个顶层 URL。

浏览器会话不会复用用户 Chrome 的 profile、Cookie 或登录状态。暂不暴露 popup 和下载。私网拦截是网络防护，不是面向不可信网页的完整沙箱。

## Model Experience

语义工具把有界文本写入持久 tool result。`browser_screenshot` 通过 DSH attachment 保存 PNG，并返回原生 image block，因此仅对声明图片输入的模型路由可用。提示词会要求模型刷新过期元素 ID、在 Canvas 页面使用截图坐标、释放持续按下的鼠标键和键盘按键，并在需要确认点击位置时请求带标记的截图。

## 许可证

[MIT](LICENSE)
