dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
网页操作录制器:录制用户引导会话中的真实点击、输入、导航与网络请求,生成 Markdown 操作时间线报告(report.md + events.jsonl),用于沉淀接口级 skill。
语言:简体中文 · English
宿主版本要求:本插件适配
@deepseek-ai/dsh^0.2.1-rc.2(依赖其@deepseek-ai/dsh-tools@0.2.1-rc.2),即 profile 的package.json需满足"@deepseek-ai/dsh": "^0.2.1-rc.2"。

网页操作录制器插件:面向「用户手动操作浏览器,插件在后台录制」的场景。它可以 attach 到已打开的浏览器窗口(如带调试端口的 Playwright MCP 浏览器,这个是可选的),也可以自己启动一个有头浏览器窗口(默认本机 Edge);用户在窗口里正常点网页,插件在后台记录每次点击 / 输入 / 表单提交和每个网络请求 / 响应 / 失败,停止后生成 Markdown 摘要报告。
要让 agent 直接通过接口对接某个业务平台(例如 EIP 类系统),前提是先搞清楚三件事:平台暴露了哪些接口、每个业务步骤对应调用哪个、参数和响应是什么格式。传统做法是查接口文档或逆向前端代码,但文档往往缺失或过时,而且就算有了接口清单,也很难把「用户在页面上的业务操作」和「背后触发的接口调用」一一对上。
本插件换一种思路:不逆向,只留操作依据。开发者先让记录仪就位(recorder_start),再让目标业务流程在一个受监控的有头浏览器窗口里完整跑一遍即可——操作可以由真人手动完成,也可以交给 playwright MCP / browser-use 等浏览器自动化工具按步骤驱动——之后不需要再翻文档,因为这次操作留下的原始数据已经回答了上面三个问题:
xhr/fetch 请求——method、URL、请求头、请求体、响应体、失败原因。两类事件按时间交错实时写入 events.jsonl,两条线天然对齐:任何一个业务步骤,都能在原始数据里找到「它调了哪个接口、参数怎么填、返回了什么」。停止后生成的 report.md 再把这套操作时间线与请求明细整理成便于阅读的摘要。
得到这些原始依据后,交给大模型做分析:归纳出完整的「业务流程 × 接口调用序列 × 数据契约」,再沉淀为 skill——把流程拆成步骤,注明每步该调哪个接口、请求 / 响应长什么样、出错怎么办——让 agent 之后照着 skill 直接走接口完成任务,整个过程不再依赖人工演示。
一句话概括:页面操作一遍 = 留下可分析的操作依据 = 大模型据此制作接口级 skill 的原料。录制本身只是采集,分析和沉淀交给模型完成。
下面是一次真实录制的产物截图(tests/smoke.mjs 冒烟测试在一个本地页面执行「输入关键词 → 查询 → 去第二页」的完整流程)。
① report.md 摘要——统计概览 + 操作时间线 + 网络请求明细,把「业务步骤」和「接口调用」整理在同一张表里:

② events.jsonl 原始事件流——每一行一个事件(导航 / 点击 / 输入 / 请求 / 响应…),按发生时间交错实时落盘,是分析时最完整的操作依据:

两张图对照着看:一次「输入关键词并查询」的操作,在 events.jsonl 里留下了对应的 POST /api/query 请求与 200 响应事件,在 report.md 里则汇总成一行请求明细——这正是设计初衷里「页面操作一遍,留下可分析的操作依据」的直观体现。
前置:已装好 DSH(dsh web 能正常运行,@deepseek-ai/dsh 需为 ^0.2.1-rc.2),Node.js ≥ 18、pnpm ≥ 8。
dsh plugin --profile web add dsh-web-recorder@latest
装完硬刷新浏览器(Cmd/Ctrl+Shift+R)即可在 DSH 会话工具列表里看到 recorder_* 工具(DSH 对 client 改动热加载,无需重启;仅 host 半更新时需要重启)。
让 DSH 自己装——把下面这段提示词发给任意一个 DSH 会话:
帮我安装 dsh-web-recorder 插件(网页操作录制器),步骤:
1. 执行 dsh plugin --profile web add dsh-web-recorder@latest
2. 完成后提醒我硬刷新浏览器(Cmd/Ctrl+Shift+R)
dsh plugin --profile web add dsh-web-recorder@latest
更新完硬刷新浏览器(Cmd/Ctrl+Shift+R)即可生效。
| 现象 | 原因与解决 |
|---|---|
报 Ignored build scripts | pnpm 拦截了构建脚本。在 profile 目录(~/.dsh/profiles/web)跑 pnpm approve-builds --all。 |
| 报「找不到 profile 目录」 | 先跑一次 dsh web,让它初始化 ~/.dsh/profiles/web。 |
提示 dsh: command not found | 先安装 DSH;或直接用 npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-web-recorder@latest。 |
调试本地改动时,把依赖指向本地克隆并自行构建:
1. git clone https://github.com/kakajun/dsh-web-recorder.git ~/Code/dsh-web-recorder
cd ~/Code/dsh-web-recorder && pnpm install && pnpm build
2. ~/.dsh/profiles/web/package.json 的 dependencies 写 "dsh-web-recorder": "link:<克隆目录绝对路径>"
3. 在 ~/.dsh/profiles/web 执行 pnpm install
4. 硬刷新浏览器(Cmd/Ctrl+Shift+R)即可生效
更新:git pull && pnpm install && pnpm build → 硬刷新浏览器即可。
| 工具 | 说明 |
|---|---|
recorder_start(url?, waitSeconds?) | 开始录制:优先 attach 到已有浏览器窗口(cdpUrl / 自动发现本机任何开了调试端口的 Chromium,Playwright MCP 优先),否则启动有头浏览器并可导航到起始 URL。自启窗口自带 CDP 调试端口并在返回里给出 cdpUrl,驱动方可直接连上来接管这个窗口。waitSeconds 可让录制先等 N 秒再记录(跳过登录 / 初始化);事件实时落盘 events.jsonl。返回时会把本次录制登记为后台任务并返回 jobId |
recorder_stop() | 停止录制:生成 report.md 摘要报告;attach 模式只断开录制连接、不关闭外部浏览器,插件自启的窗口才关闭;幂等 |
recorder_status() | 查询状态:是否在录制、事件分类计数、产物目录、本次录制窗口的 CDP 地址、jobId、上一次收尾结果 |
用户直接关掉浏览器窗口会自动收尾(reason: browser-closed),已录数据不丢。
录制结束会主动把模型接回来:recorder_start 同时把本次录制登记为宿主的后台任务(ctx.jobs,<jobId> 形如 recorder-1,归属发起调用的 agent)。用户操作完直接关掉浏览器(或调用 recorder_stop)时任务结算,模型在会话内收到完成通知——空闲时被唤醒开新的一轮,繁忙时注入下一步——随后即可用 job_output 取回产物路径与统计并接着总结,不必轮询 recorder_status。宿主未加载后台任务能力(dsh-jobs / dsh-tool-jobs)时自动退化为原行为(无 jobId,仍需 recorder_stop 或用户一句话把结果交给模型)。
插件的 3 个工具会随插件启用出现在 DSH 会话的工具列表里,「命中」发生在模型做工具选择时——依据是工具名与描述。当用户任务属于「查清某个网页业务流程背后调用了哪些接口、参数/响应长什么样,并据此生成接口级流程说明或 skill」时,模型应主动调用 recorder_*。这类任务通常要分步完成:先打开目标页面开始录制,再由驱动方(真人或自动化工具)在页面上完成实际操作,最后基于录制结果分析沉淀。典型任务表述:
http://目标地址,开始录制"http://目标地址,先等我 30 秒,然后再开始录"如果只是回答不涉及真实页面操作的问题(例如直接查文档就能答),模型通常不会命中本插件。
本仓库即 DSH 插件:入口 lib/index.js(源码 src/index.ts),经 package.json 的 dsh.bundle.patch 指向仓库根 cordis.patch.yml 并入宿主 profile(其中 id/name 均为 dsh-web-recorder);@deepseek-ai/* 由宿主按 peerDependencies 提供。插件启用后工具即进入模型工具列表,无需额外声明即可被选择。
本插件不直接操作页面——工具只有 recorder_start / recorder_stop / recorder_status,职责是「旁观并记录」:它启动一个受监控的有头浏览器窗口,把窗口里发生的每一步 UI 操作与每个网络请求原样落盘。真正「打开 XXXX 页面并按步骤操作」的是浏览器驱动方,可以是:
playwright MCP 不是必需依赖:没有装它、没有 browser-use,插件照样能录——真人在弹出的窗口里手动操作一遍即可,这也是最省事的方式。MCP / browser-use 的价值只在于把「打开页面 + 按步骤操作」自动化,属于可选项。
模型(agent)在会话里做编排:先让记录仪就位,再指挥驱动方执行流程,最后收尾并读取产物分析。
--remote-debugging-port 启动(见下文「协同前提」),recorder_start 会自动发现它的 CDP 端口并 attach 到这个已打开的窗口,无需新开浏览器,MCP 也能继续在这个窗口上操作。没有 playwright MCP 就跳过本步,直接进第 2 步,由真人在插件弹出的窗口里操作recorder_start()——attach 到已有窗口,或弹出受监控浏览器并打开起始页,从此刻起窗口内一切操作与网络请求都开始实时落盘 events.jsonl(注意:从这一刻起发生的页面加载也会被录进来,见下文「初始化请求噪音」)recorder_status()——查看是否在录制、事件分类计数、产物目录、jobIdrecorder_stop()——生成 report.md 摘要报告(attach 模式只断开 CDP 连接,不关闭外部浏览器;插件自启的窗口会关闭;用户直接关窗口也会自动收尾)。录完可以什么都不做直接关窗口:后台任务会结算并通知模型report.md + events.jsonl,归纳「流程步骤 × 接口调用 × 请求/响应契约」,进一步沉淀为 skill 或接口对接文档以前要让「浏览器自动化工具」和录制器共用同一个窗口,得先给 playwright MCP 加 --remote-debugging-port(要写一份 playwright-mcp.config.json)。现在这步可以省掉了:
① 谁开了调试端口就接管谁——自动发现的范围已从「只认 Playwright MCP」扩展为「本机任何带 --remote-debugging-port 的 Chromium」:chrome --remote-debugging-port=9222 手工起的浏览器、chrome-devtools MCP 的、其他工具开的,都能被自动 attach。端口写成 0(让 Chrome 自选)时,会去读该 user-data-dir 下的 DevToolsActivePort 拿实际端口。Playwright MCP 的浏览器优先级最高。
② 插件自己开的窗口也能被连——自启窗口现在固定带 --remote-debugging-port=0,recorder_start 的返回里会带上它的 CDP 地址(cdpUrl)。要让驱动方在这个窗口里继续操作,直接连这个地址即可:
| 驱动方 | 怎么连 |
|---|---|
| playwright MCP | 启动 MCP 时加 --cdp-endpoint=<cdpUrl> |
| browser-use | BrowserSession(cdp_url=<cdpUrl>) |
| 自己的 playwright-core 脚本 | chromium.connectOverCDP(cdpUrl) |
于是「先让 MCP 打开页面、再起录」不再是唯一路径——反过来也成立:录制器先开窗口(recorder_start),把 cdpUrl 交给驱动方连上来操作,同样录得到。
③ 登录态只付一次——自启窗口使用持久化 profile(默认 ~/.dsh-web-recorder/browser-profile,可用 Config 的 userDataDir 改)。目标站登录一次,之后每次录制都直接是已登录状态,顺带省掉「登录跳转」那批噪音请求。持久 profile 被残留进程占用而启动失败时会自动改用临时 profile,不影响录制。
attach 模式下
recorder_stop只断开录制连接,不会关闭外部浏览器(MCP 会话与用户手上那个窗口都照常可用);这一条在pnpm smoke:cdp/pnpm smoke:mcp里作为断言校验。
--remote-debugging-port。此时录制器无法 attach 上去,只能走「接管」回退——记下 MCP 浏览器当前页面 URL 后把 MCP 浏览器进程关掉,再用插件自启窗口打开同一页面;此后 MCP 与原来那个浏览器的连接已经断了,无法再驱动它(MCP 若重新开一个窗口,那个新窗口同样不在录制范围内)。想让 MCP 与录制器共存有两条路:让 MCP 浏览器带调试端口(或用显式 cdpUrl 指向它),录制器 attach 过去;或者反过来——接管回退后插件自启的窗口自带 CDP 端口(返回里的 cdpUrl),让 MCP 以 --cdp-endpoint=<cdpUrl> 连到这个新窗口继续操作。@playwright/mcp 传一个配置文件(--config=path/to/playwright-mcp.config.json),内容为:
{
"browser": {
"launchOptions": {
"args": ["--remote-debugging-port=0"]
}
}
}
端口为 0 时 Chrome 自动挑选空闲端口并写入其 user-data-dir 下的 DevToolsActivePort 文件,recorder_start 会自动读取并完成 attach(Windows 上通过进程命令行里的 ms-playwright-mcp user-data-dir 定位)。未配置调试端口时记录仪无法 attach,会回退到接管模式:记录 MCP 浏览器当前页面 URL 后关闭它,再用插件自启窗口打开同一页面。cdpUrl(如 http://127.0.0.1:9222)指定 attach 目标,优先级高于自动发现;此时 recorder_stop 只会断开 CDP 连接,不会关闭外部浏览器进程。channel: msedge),可配 chrome 或 executablePath;插件不下载浏览器cdpUrl 或 MCP 浏览器带 --remote-debugging-port 时,记录仪优先通过 Chrome DevTools Protocol attach 到已有浏览器实例;attach 失败会回退到接管/新开窗口recorder_stop 不会关闭外部浏览器进程,仅断开录制连接(pnpm smoke:cdp / pnpm smoke:mcp 会校验进程与页面都还在);普通模式(未 attach)下结束会关闭插件自启的窗口events.jsonl 含请求头/请求体/响应体等完整数据,按敏感数据处理,勿外发录制从 recorder_start 那一刻开始,凡是开始之后发生的页面加载,其请求都在录制范围内。所以 recorder_start(url) 直接打开页面(或录制开始后再由驱动方导航)时,页面自身的初始化请求会被完整录进来——登录态校验、字典/枚举、菜单、配置、用户信息、埋点上报等等。
实测(本地页面加载时发 2 个初始化接口 /api/init、/api/config,点击按钮才发业务接口 /api/query):
| 方式 | 初始化接口事件 | 业务接口事件 |
|---|---|---|
recorder_start(url) 直接打开页面 | 2(/api/init + /api/config) | 点击后同样会录到 |
| 页面先加载完成再 attach 录制 | 0 | 1(/api/query) |
差异来自「页面加载发生在录制开始之前还是之后」,与页面本身无关。
影响:这些初始化接口通常与目标业务流程无关,会稀释有效信息——尤其分析「点『查询』发了什么请求」时,需要额外分辨哪些是页面固有的、哪些是操作触发的。
怎么减少这类噪音:
--remote-debugging-port,或显式 cdpUrl)先把目标页面打开、等它加载稳定,再 recorder_start()(不传 url)——录制从「页面已就绪」开始,初始化接口不计入。waitSeconds(见下一节),让插件先等 N 秒再开始记录;已经录了的话,按时间把开头的加载段裁掉。events.jsonl 里紧跟在 navigate 事件之后、且没有对应 UI 事件(click / change / submit)的请求,基本都是初始化噪音。recorder_status() 记下 eventCount,之后只看 seq 大于该基线的事件,即可跳过开头那批加载请求。report.md 把 UI 事件与请求按时间交错列出,能对应上某次点击/输入的请求才是真正的业务流程接口。recorder_start 的 waitSeconds(起录即跳过登录 / 初始化)对「目标站要登录」或「首页加载会刷一堆初始化接口」的场景,不用事后裁剪——起录时直接让插件先等一会儿:
recorder_start({ url: "https://目标地址/login", waitSeconds: 10 })
recorder_start 被调用的时刻。浏览器启动与起始页导航都算在这段时间里,所以「登录 → 跳首页 → 首页初始化」这一整段都在被跳过的范围内。recorder_status() 的 waitRemainingSec 告诉你还要等多久才开始记录(> 0 时此刻的操作不会被记录),skippedEvents 是等待期内已丢弃的事件数;recorder_stop() 的结果与 report.md 头部同样会写「等待 10s 后开始记录: 等待期内 N 个事件已丢弃」。context.exposeBinding + addInitScript 捕获阶段监听实现,覆盖新标签页与 iframe。xhr/fetch 接口调用(requestResourceTypes 可调)——每个请求的 method/URL/resourceType/请求头/请求体,响应的状态码与响应体(截断),失败请求的错误原因。可选记录 console 消息。<outputDir>/rec-<时间戳>/ 下生成 events.jsonl(完整数据,逐行实时写入)和 report.md(操作时间线 + 请求明细表 + 失败请求摘要)。| 字段 | 默认值 | 说明 |
|---|---|---|
channel | msedge | playwright-core 浏览器渠道(msedge/chrome),使用本机已装浏览器,不下载 Chromium |
executablePath | '' | 自定义浏览器可执行文件路径,非空时覆盖 channel |
cdpUrl | '' | CDP 调试地址(如 http://127.0.0.1:9222),非空时优先 attach 到已有浏览器;留空时自动发现带 CDP 端口的 Playwright MCP 浏览器;都失败则回退到接管/新开窗口 |
userDataDir | '' | 自启窗口使用的浏览器 profile 目录;空则默认 ~/.dsh-web-recorder/browser-profile。持久化 profile 让登录态跨次录制保留(目标站只需登录一次),并让插件窗口自身暴露 CDP 端口供驱动方连接 |
outputDir | '' | 产物输出根目录;空则默认当前工作目录(用户正在操作的文件夹,harness 会话 cwd)下 reports/recorder |
captureResponseBodies | true | 是否抓取 xhr/fetch 响应体 |
maxBodyBytes | 16384 | 单个请求体/响应体最大记录字节数,超出截断 |
recordConsole | false | 是否记录 console 消息 |
requestResourceTypes | xhr,fetch | 逗号分隔的 resourceType 白名单,只有命中的请求才录制(响应/失败事件随请求一并过滤);默认只录接口调用,静态资源/文档不录 |
redactHeaders | cookie,authorization,proxy-authorization,x-api-key,set-cookie | 逗号分隔的脱敏请求头名(大小写不敏感),命中头值记为 <redacted> |
pnpm install # 安装依赖(devDependencies 与 peerDependencies 对齐)
pnpm typecheck # tsc --noEmit
pnpm test # vitest 单元测试(report 生成纯函数)
pnpm build # tsdown 产物到 lib/(插件加载与 smoke 依赖产物)
pnpm smoke # 真实 Edge 端到端冒烟(需本机已装浏览器, 产物在 reports/)
pnpm smoke:cdp # CDP attach 模式端到端冒烟(需本机已装浏览器, 产物在 reports/)
pnpm smoke:mcp # MCP 浏览器自动发现 attach 冒烟(模拟带调试端口的 MCP Chrome)
pnpm smoke:wait # 等待期(前置剔除)冒烟: 验证 waitSeconds 等待期内不记录、等待期后正常记录
本仓库已按 awesome-dsh-plugin/contributing 对齐:
package.json 声明 dsh.bundle.patch: ./cordis.patch.yml(见仓库根 cordis.patch.yml, 其中 id 与插件内部注册名 dsh-web-recorder 一致)@deepseek-ai/* 包放在 peerDependencies: dsh-tools 的范围带显式预发布分支(harness 以 rc 版本发布, 无分支的宽范围会被 node-semver 静默排除), cordis/schemastery 同为 peer 由宿主提供reports/recorder,可用 Config.outputDir 显式指定tests/smoke.mjs, 非占位仓库--remote-debugging-port 时记录仪会自动 CDP attach(见「协同前提」),插件自启的窗口同样自带 CDP 端口、可反过来让驱动方连上来(见「零配置接入」)。cdpUrl 让 MCP 重新连到这个窗口。events.jsonl 应按敏感数据处理,不要外发。插件还在持续打磨,任何形式的参与都欢迎:
report.md 里缺的字段,都可以直接开 issue。附上日志与截图会更快定位;请勿粘贴 events.jsonl 原文(含完整请求头/请求体)。pnpm typecheck && pnpm test;改动了 session.ts 的事件采集逻辑,再跑一遍 pnpm build && pnpm smoke。想做但不确定合不合适的改动,欢迎先开 issue 聊一聊思路。