Skip to content

DeepSeek Harness 原生浏览器插件:源码、生命周期与测试 ​

插件源码目录是 deepseek-browser-use/plugins/deepseek-browser-use。它保留 Java 服务现有协议,只在 Harness 和 HTTP 之间增加适配层。安装步骤见 上一章。

1. 文件分工 ​

text
plugins/deepseek-browser-use/
  package.json             npm 包与 Harness bundle 清单
  package-lock.json        固定开发依赖
  cordis.patch.yml         宿主插件配置
  locale/                  插件管理器的中英文标题
  src/
    index.ts               Cordis apply、Config 和资源释放
    client.ts              有界 HTTP JSON 客户端
    backend.ts             后台部署操作、进度与就绪等待
    sessions.ts            按活跃 Agent 绑定任务、队列与作业
    tools.ts               14 个原生工具(dsb_backend 由 index.ts 注册,共 15 个)、参数校验和结果展示
    commands.ts            Java 命令表名称快照
  test/plugin.test.mjs     协议、生命周期和 ToolRuntime 测试
  test/backend.test.mjs    后端管理、就绪等待与 Windows 脚本测试
  scripts/smoke.mjs        对运行中服务做真实浏览器验证
  scripts/backend.ps1      Windows 依赖安装、克隆、更新、构建与启动
  scripts/isolated-smoke.mjs  启动独立 Java 服务后验证并清理

2. 插件装载与工具注册 ​

index.ts 导出 name、inject、Config 和 apply,依赖 Harness 的 tools 与 agents 服务。Schemastery 负责配置默认值与范围检查;apply 创建 HTTP 客户端、后端管理器和会话管理器,再把工具交给 ctx.tools.register。启用 backendAutoStart 时,HTTP 客户端先等待后端就绪(close、cancel_job、get_job 例外,避免清理动作把已停的后端重新拉起);新增 dsb_backend 管理工具后共 15 个工具。

tools.ts 使用 Zod 校验运行参数。工具 schema 投影成 Harness 支持的 JSON Schema 子集,保留对象字段、类型、必填项、枚举和数组;URL 格式、长度、整数范围等更严格的规则仍在执行前通过 Zod 验证。

这样做是因为 PTC 需要从同一份 schema 生成工具调用类型,而 Zod 的完整 JSON Schema 包含递归引用和其他关键字,不能假设 Harness 的 schema 子集全都支持。

工具返回规范 JSON,output.render 负责面向模型的文本与图片展示。普通结果超过 maxTextChars 时明确标记展示截断,程序化返回仍保留完整 JSON。HTTP 层另有 maxResponseBytes 硬上限,避免无限制读取响应。

3. HTTP 协议保持不变 ​

BrowserClient.command 发出:

json
{
  "id": 123456789,
  "method": "get_browser_state",
  "params": { "highlight": false, "includeElements": false },
  "responseMode": "compact"
}

responseMode 位于信封层,与 id/method/params 平级。HTTP 请求使用 fetch,正文通过 JSON.stringify 编码,中文、引号与换行不经过 shell 转义。

客户端同时组合调用者的取消信号与单请求截止时间,拒绝重定向,按块累计响应并检查字节上限。非 2xx、无效 JSON、没有 ok:boolean 的响应和超时都会变成传输错误,不尝试自动重发。ok:false 的合法服务业务响应则作为完整结果返回。

4. 会话、任务与清理 ​

浏览器状态归活跃 Agent 对象所有。模型不接受 taskId 参数,不能用工具切换到任意任务。工具执行还会比较 ctx.agents.get(exec.agent.id) 与执行上下文里的对象,拒绝过期或伪造的活跃身份。

任务 ID 使用 48 位随机正整数,可在 Java long 与 JavaScript number 之间精确传输。没有使用服务端自动生成的 Snowflake 数字,以避免 JSON number 超过 JavaScript 安全整数范围。

每个 Agent 维护一条 Promise 队列。首次浏览器调用先执行 start,后续轮次复用;同一会话两个并发调用也按队列顺序送达。已取消的排队调用在发送 HTTP 前被拒绝,后续调用不会因前一条失败而永远卡住。

显式 dsb_close 只关闭自己任务,下一次浏览器调用可以重新启动。Agent scope 释放和插件卸载都会触发资源清理,这条路径不发送 shutdown。后端管理器的显式 restart 是单独的部署操作,只对已验证身份且无活动任务的自有后端执行浏览器关闭与 Java 重启,见 35。

恢复或重新加载的会话是新的活跃 Agent,使用新任务。插件不声称从 Session 日志恢复了真实浏览器页面。共享 profile 仍保留其自身登录态,这与恢复页面是两件事。

5. 异步作业与未知结果 ​

普通 HTTP 请求返回后,队列可以放行下一项;异步批次只返回接收回执,浏览器工作仍在服务端运行。因此队列还需要 activeJob 状态:

  1. dsb_batch 验证每一步的准确方法名,转换为服务单键格式,带 async:true 提交。
  2. 保存返回的 jobId 到本会话集合;其他 Agent 无权通过 dsb_job 使用这个句柄。
  3. 作业运行期间拒绝其他普通浏览器操作。
  4. 查询读到 done/failed/cancelled 后解除阻塞。
  5. Agent 清理时请求 cancel_job,在有界时间内等待终态,再 close 自己任务。

cancel_job 是协作式取消,当前动作不会被撤销。请求取消后不能立即声称所有操作都已停止。

如果异步提交响应丢失或缺少 jobId,插件没有可靠句柄判断作业是否完成。此时设置 batchUnknown,阻止再次操作,也拒绝自动关闭可能仍在工作的任务。错误报告 task ID,交给运维在服务端查询 list_jobs、匹配 browserId、等待或取消作业并关闭任务。资源清理失败会向宿主报告,不能伪装成清理成功。

普通单条命令超时也可能已经执行,错误会提示先观察再决定;插件没有服务器端事务回滚或远程硬取消能力。

6. 通用命令与边界 ​

通用工具必须使用 commands.ts 中的准确命令名,不能依赖服务端模糊别名来绕过适配。commands、run_recipe 和全局管理等方法禁止通过通用入口调用;批次逐项执行相同检查,避免把全局操作藏进嵌套命令。

这套限制用于任务管理和避免误操作,不是安全沙箱。页面 JavaScript、文件与 Cookie 功能仍然存在,同一服务的 profile 可以共享登录态。需要租户隔离时应在部署层使用不同实例、profile 与访问控制。

新增 Java 命令后更新名称快照;一致性测试会对照 CommandTable.java 检查名称与顺序,防止文档宣称支持而插件拒绝。测试用全文正则提取该文件的 put("…") 名单,因此名单末尾的 commands 其实来自 runRecipe 的参数注册,不是命令表条目。

7. 图片与上传 ​

dsb_screenshot 默认 view:false,让服务落盘并返回 URL。view:true 分为两步:

  1. 检查 Harness 附件服务、当前 provider/model 路由以及该模型的 image 输入声明。
  2. 调用 screenshot(inline:true),把 base64 解码并交给 attachments.saveImage,从规范结果中移除 base64,保留持久附件引用。

output.render 由这个引用产生 image 内容块,避免把图片字节当作普通文本塞进模型上下文。实际图像校验、存储与展示仍由宿主附件实现负责。

dsb_upload 在 Harness 主机检查普通文件大小,读取字节并发送 filename/contentBase64。因此“客户端文件路径”和“服务端文件路径”不会混淆。当前实现是有大小上限的内存传输,大文件不在第一版支持范围内。

8. 自动化测试 ​

插件目录运行 npm test(等于 npm run build && node --test test/*.test.mjs):

powershell
npm test

覆盖重点:

  • 中文、引号、换行完整传输;responseMode 层级正确。
  • HTTP 非 2xx、无效协议、重定向、超限、超时和取消不自动重试。
  • 同会话只启动一次、顺序执行,不同会话 task ID 不同。
  • 取消排队动作不发送 HTTP,也不破坏后续队列。
  • 阻止模型注入 task ID、全局管理、模糊别名和嵌套批次。
  • 作业归属、终态解锁、清理时取消并等待、未知提交结果冻结。
  • 文件实际字节传输及大小上限。
  • 附件服务缺失拦截、附件保存并剥离 base64、文本展示截断。
  • 命令名称快照与 Java 注册表一致。
  • 使用真实 @deepseek-ai/dsh-tools ToolRuntime 注册并派发,而不只调用工具函数。
  • 后端管理:本机端点与绝对路径校验、操作单飞与失败保留、健康服务复用不跑安装、失败后普通动作不重复触发安装、取消等待不打断共享安装、就绪检查先于命令、清理类请求不触发安装,以及 Windows 脚本的 status/prepare(其中两项仅 win32 运行)。

9. 真实浏览器联调 ​

服务已运行且和 Harness 测试进程在同一台机器时:

powershell
$env:DSB_BASE_URL = 'http://127.0.0.1:10049'
npm run smoke

脚本启动本地 HTML 页面,经过真实 ToolRuntime、HTTP 和浏览器完成导航、读取、中文填表、点击回读、文件上传回读、截图、批次查询和关闭。它只关闭自己任务,不关闭外部共享服务。该测试页使用 loopback,因此默认脚本不适用于异机浏览器服务。脚本还认 DSB_BROWSER(默认 chrome)并默认以无头模式创建任务;隔离 runner 另认 JAVA(默认 java)。

使用独立 Java 服务验证,在浏览器仓库根目录执行:

powershell
mvn -q -o -pl playwright-server compile dependency:build-classpath '-Dmdep.outputFile=target/plugin-smoke-classpath.txt'
cd plugins/deepseek-browser-use
npm run build
node scripts/isolated-smoke.mjs

第一条的 -o 是离线模式,前提是本地 Maven 仓库已经有该工程的全部依赖(此前至少完整构建过一次);依赖不全时去掉 -o,让它联网补齐。

runner 使用独立随机端口、工作目录、profile 和 jdk.net.unixdomain.tmpdir。本机 JDK 21 的默认 Unix-domain loopback 曾在启动 Selector 时失败,测试 runner 将临时 socket 文件也放入自己目录后通过;它不修改系统 JDK 设置。日志和页面产物保留在仓库 logs/plugin-smoke/,runner 最后仅关闭自己启动的服务。

已验证 TypeScript 构建、真实 ToolRuntime 派发以及上述 Java/浏览器链路。当前开发环境未连接目标 Harness 的插件管理器,因此 bundle 在用户实际运行 profile 中的安装、激活和 Web UI 图片显示仍需按上一章回读确认;这些结果不能由 HTTP smoke 代替。