dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
在 DSH 原生面板里聊 DeepSeek 网页端(chat.deepseek.com)——由真实浏览器以你已有的网页登录驱动,支持流式回复、深度思考与智能搜索开关,以及图片与文档附件。会话可从网页端恢复、导出为 Markdown,并蒸馏或原文迁移成 harness 会话;agent 另可通过 dschat_status、dschat_send、dschat_stop、dschat_recover、dschat_import、dschat_transfer 六个工具使用同一套网页聊天。
在 DeepSeek Harness 的原生面板里聊 DeepSeek 网页端(chat.deepseek.com),聊完把整段对话迁移成 harness 会话继续开发。复用你的网页登录,不消耗 API 额度。
包名 dsh-dschat(npm 强制小写),界面上显示为 dsh-DSchat;面板文案跟随 Harness 的界面语言,在中文与
英文之间自动切换。
| 浅色 | 深色 |
|---|---|
![]() | ![]() |
思考用时、正文里的 [citation:N] 芯片、默认收起的参考来源:

图是真实组件在真实浏览器 + DSH 自己的主题表里渲染出来的(
node scripts/ui-shot.mjs --synthetic), 会话内容是合成样例,公开的图里不会出现任何人的真实对话。
[citation:N] / [reference:N] 标记被清掉,「参考来源」列表不跟着走。「导出 markdown」同样如此
—— 导出文件是这场问答本身的记录:用户问了什么、DeepSeek 答了什么,不再带上搜索来源与思考过程。
(按钮 0.12 之前叫「DSH 迁移」,右边那个展开箭头也去掉了:它开的是一个弹出菜单,不是折叠区。)~/Downloads,可配置)。对话框里可以直接选文件夹(系统选择窗口),
也可以手动改路径;改过一次之后默认就是它,直到你下次再改 —— 这个选择记在 host 侧,重启也还在。
选择器默认全选,取消某一组时问题和回答一起取消;只导一部分时文件名带 -选中N条,不会覆盖整篇导出
的那份。文件里只有用户的问题与 DeepSeek 的回答。··· 菜单里的诊断卡,看引擎、登录、浏览器与错误原因。用 DSH 自带的插件安装器(不要手改 profile,也不要在 profile 里跑 pnpm)。两种装法都能用,区别只在于谁 来准备插件自己的依赖:
方式一 · tarball(交付、给别人装)
npm pack --pack-destination ~/.dsh/dschat-dist # 打成 tarball
plugin_manager action=install_bundle target=file:<tarball 绝对路径>
pnpm 会把插件自己的依赖(playwright-core)装进 profile 的 node_modules,这条路径不需要你先做什么。
方式二 · link:(开发;本机 desktop profile 现在就是这么装的)
cd <插件源码目录> && npm install --ignore-scripts # 先在插件目录装好依赖
plugin_manager action=install_bundle target=link:<插件源码目录绝对路径>
link: 能正常工作的前提只有一个:插件目录自己有一份 node_modules。插件是按真实路径解析第三方
依赖的,playwright-core 从这一份里取,@deepseek-ai/* 由宿主提供;少了它才会出现
1 required plugin did not activate。
早先这里写的是「必须用 tarball,符号链接解析不到依赖」,那个结论是错的。2026-10-01 那次起不来的 真实原因写在崩溃日志里:
client-modules: dsh-dschat dsh.client.platform must be a string——package.json的dsh.client.platform缺失,跟link:没有关系,现在已修。
确认装上了:重启 DSH 后面板能打开,或者
curl -s http://127.0.0.1:57531/api/dsh-dschat/state | head -c 120
返回以 {"ok":true 开头就是装载成功。
插件是声明式装载的:任何一个插件在 apply() 里抛异常,整个 profile 都可能装不起来。真遇到时按顺序做:
~/Library/Logs/DeepSeek Harness/ 里最新的一份,搜 dsh-dschat。
1 required plugin did not activate 这类消息后面通常紧跟真正的原因。dsh-DSchat 停用,或把它在 profile composition 里的那一段
(- id: dschat / name: 'dsh-dschat')注释掉,让 DSH 先起来。~/.dsh/dsh-dschat/transcripts.json,附件在同目录的 attachments/,
都与插件能否装载无关;读不出来时插件会把原文件改名备份成 transcripts.json.corrupt-<时间戳>,
不会覆盖它。dschat_recover。其余工具:dschat_status / dschat_send / dschat_stop /
dschat_import / dschat_transfer(默认也只返回预览,用户确认后带 confirm: true 再调用一次才写入)。··· → 「运行状态」 → 复制诊断:一次带上运行版本与构建时间、引擎与登录状态、最后的
错误(含错误码)、数据/配置目录,以及网页端的实时探测结果,直接贴进反馈即可。网页端没起来、没登录或
出错时,会话末尾也会出现对应的提示卡,每张卡带一个能真正解决问题的按钮(重试启动 / 打开登录窗口)。这一版没有自带设置表单:插件不注册 plugins.row.config 页面,插件页里不会为它渲染任何表单。配置来自
profile 的 cordis.patch.yml —— 在 dschat 那一行(name: 'dsh-dschat')下面加 config: 块,重启生效:
- id: dschat
config: { browserHeadless: false, exportDir: ~/Notes }
| 键 | 默认 | 说明 |
|---|---|---|
enabled | true | 插件总开关 |
announceToAgent | false | 是否在 system prompt 里加一行说明这组工具。默认关闭:工具的 description 已自带触发词与用法,而提示词由每个会话、每个子 agent 共同承担 |
browserChannel | auto | chrome / msedge / chromium / auto |
browserExecutablePath | 空 | 自备 Chromium 时可执行文件路径 |
browserProxy | direct | direct 或代理地址 |
browserHeadless | true | 登录窗口不受它影响,始终可见 |
replyTimeoutMs | 180000 | 单次回复超时(毫秒) |
dataDir | 空 | 数据目录(对话记录),空则 ~/.dsh/dsh-dschat |
profileDir | 空 | 浏览器 profile(登录态),空则复用 dsh-webchat 的目录 |
exportDir | 空 | 导出目录的默认值,空则 ~/Downloads;相对路径按家目录解析。读者在导出对话框里选过的目录优先于它(记在 host 侧,重启还在) |
transferDistill | true | 迁移默认蒸馏成简报,false 为按原文迁移 |
transferProvider / transferModel | 空 | 蒸馏用的模型,空则自动挑(优先名带 deepseek 的 provider 与带 chat 的模型) |
transferMaxTokens / transferChunkTokens | 4096 / 1024 | 蒸馏的输出与分块上限 |
~/.dsh/dsh-webchat/browser-profile —— 从 dsh-webchat 切过来不用重新登录;
两个插件同时开着会争同一份 profile。GET /api/dsh-dschat/state 取 csrfToken,再在后续请求里带
x-dschat-token 头;这是为了挡住本机其他网页用一张 <img> 就把记录清掉。/send 还要求路径落在插件自己的 attachments/ 目录内(agent 的 dschat_send 不受目录限制,但同样
受类型与大小规则约束)。GET /api/dsh-dschat/chat?id= 取
—— 这是把轮询流量从 MiB 级降到 KB 级的原因。脚本要搜会话请用
GET /api/dsh-dschat/search-conversations?q=,它返回会话 id,不返回正文。npm install --ignore-scripts # 依赖(含 @deepseek-ai/* 的类型,供类型检查用)
npm run build # 产出 lib/index.js(Host)与 lib/client.js(浏览器)
npm run typecheck # tsc --noEmit,覆盖 src/(strict)
npm test # node --test,180 个用例,含真实浏览器 e2e
typecheck 只覆盖 src/(发布产物由它构建);test/ 是直接用 Node 的类型擦除运行的 ESM
TypeScript,由执行验证,不参与类型检查。
⚠️ 链接安装(
link:)时不要把@deepseek-ai/*装进插件自己的node_modules。共享实例的 dsh 包必须由宿主提供:profile 内的副本会按「profile > runtime」的优先级遮蔽宿主版本。本仓库把 它们写在devDependencies(只为类型检查),如果你把这份 checkout 以link:方式装进了正在使用的 profile,请在另一份 clone 里跑类型检查,或先dsh plugin remove,避免副本进入运行期解析路径。
引擎、网页端解析、蒸馏与迁移机制沿用 Apache-2.0 的 dsh-webchat;界面已重写为 DSH 原生面板(main 与
sidebar.panellist 两个槽位),命名空间独立成 dsh-dschat,两个插件可以共存。许可:Apache-2.0。
Chat with DeepSeek's web app (chat.deepseek.com) inside a native DeepSeek Harness panel, then hand the whole conversation off to a harness session and keep building on it. It reuses your browser sign-in, so no API credit is spent.
The npm package is dsh-dschat (npm forces lowercase); the product appears as dsh-DSchat. Every string in
the panel follows the Harness UI language and switches between Chinese and English on its own.
| Light | Dark |
|---|---|
![]() | ![]() |
Thinking time, the [citation:N] chips in the body, and the source list (collapsed by default):

These are real components rendered in a real browser with DSH's own theme tokens (
node scripts/ui-shot.mjs --synthetic). The conversations shown are synthetic samples; no real user conversation appears in any published screenshot.
[citation:N] / [reference:N] markers are removed, and the search-source list is not transferred.
Export markdown works the same way — the exported file is a record of the exchange itself: what you asked
and what DeepSeek answered, with no search sources and no reasoning block.…-选中N条.md so it cannot overwrite the whole-conversation
file. The dialog carries an Export to row: choose a folder with the system picker or edit the path by hand, and
that folder becomes the default from then on — the choice is stored on the host side, so it survives a restart.
User questions and DeepSeek answers only.··· menu covering the engine, the sign-in, the browser, and the
last error.Use DSH's own plugin installer (do not hand-edit the profile, and do not run pnpm inside it). Both install forms work; the only difference is who prepares the plugin's own dependencies.
Option 1 — tarball (shipping it to someone else)
npm pack --pack-destination ~/.dsh/dschat-dist # build a tarball
plugin_manager action=install_bundle target=file:<absolute tarball path>
pnpm installs the plugin's own dependency (playwright-core) into the profile's node_modules, so nothing has
to be prepared first.
Option 2 — link: (development; what this machine's desktop profile uses today)
cd <plugin checkout> && npm install --ignore-scripts # install the plugin's deps first
plugin_manager action=install_bundle target=link:<absolute plugin checkout path>
A link: install works as long as the plugin directory has its own node_modules: third-party imports
resolve from the real path, so playwright-core comes from there while @deepseek-ai/* comes from the host.
Without it the symptom is 1 required plugin did not activate.
This section used to say "install from the tarball, never with
link:, because a symlink cannot resolve its dependencies" — that was wrong. The 2026-10-01 startup failure says what it really was, in the crash log:client-modules: dsh-dschat dsh.client.platform must be a string— a missingdsh.client.platforminpackage.json, nothing to do withlink:. It is fixed.
Confirm it loaded: restart DSH and open the panel, or
curl -s http://127.0.0.1:57531/api/dsh-dschat/state | head -c 120
An answer starting with {"ok":true means it mounted.
Loading is declarative: a plugin that throws inside apply() can take the whole profile down with it. In order:
~/Library/Logs/DeepSeek Harness/, searching for dsh-dschat.
1 required plugin did not activate is usually followed by the real cause.dsh-DSchat off in the plugin manager, or comment out its entry
(- id: dschat / name: 'dsh-dschat') in the profile composition, so Harness starts again.~/.dsh/dsh-dschat/transcripts.json and attachments in
attachments/ beside it, independent of whether the plugin loads. An unreadable store is renamed to
transcripts.json.corrupt-<timestamp> rather than overwritten.dschat_recover; the other tools are dschat_status, dschat_send, dschat_stop, dschat_import and
dschat_transfer (which also previews by default and writes only on a second call with confirm: true).··· → Status → Copy diagnostics: one click carrying the running version and
build time, the engine and sign-in state, the last error with its code, the data and configuration paths, and a
live probe of the web page — ready to paste into a report. A web page that is down, signed out or broken also
names itself at the end of the conversation, with the button that fixes it (retry / open the sign-in window).This build ships no settings form: the plugin registers no plugins.row.config page, so the Plugins page
renders no form for it. Configuration lives in the profile's cordis.patch.yml — add a config: block under the
dschat row (the one with name: 'dsh-dschat') and restart Harness:
- id: dschat
config: { browserHeadless: false, exportDir: ~/Notes }
| Key | Default | Meaning |
|---|---|---|
enabled | true | Master switch for the plugin |
announceToAgent | false | Add one line naming these tools to the system prompt. Off by default: each tool's own description already carries its trigger words and usage, and prompt text is paid by every session and every subagent |
browserChannel | auto | chrome / msedge / chromium / auto |
browserExecutablePath | empty | Path to your own Chromium build |
browserProxy | direct | direct or a proxy URL |
browserHeadless | true | The sign-in window ignores it and stays visible |
replyTimeoutMs | 180000 | Timeout for a single reply (ms) |
dataDir | empty | Transcript directory; defaults to ~/.dsh/dsh-dschat |
profileDir | empty | Browser profile (sign-in); defaults to the dsh-webchat one |
exportDir | empty | The export folder's default; ~/Downloads when empty, relative paths resolve against home. A folder chosen in the export dialog outranks it (stored host-side, kept across restarts) |
transferDistill | true | Hand-offs distill by default; false replays the transcript |
transferProvider / transferModel | empty | Distillation model; empty auto-picks (a deepseek provider, a chat model) |
transferMaxTokens / transferChunkTokens | 4096 / 1024 | Output and per-chunk token caps |
~/.dsh/dsh-webchat/browser-profile, so switching over from dsh-webchat needs no
second sign-in; running both plugins at once makes them share it.csrfToken from GET /api/dsh-dschat/state and send it
back in x-dschat-token; that is what stops another page on this machine from clearing your history with one
<img> tag./send additionally requires the path to be inside the plugin's own attachments/ directory;
the agent's dschat_send is not confined to that directory but obeys the same type and size rules.GET /api/dsh-dschat/chat?id= — which is what took the poll from megabytes to kilobytes. Scripts that want to
search conversations should use GET /api/dsh-dschat/search-conversations?q=, which answers ids, not bodies.npm install --ignore-scripts # deps, including the @deepseek-ai/* types typechecking needs
npm run build # writes lib/index.js (host) and lib/client.js (browser)
npm run typecheck # tsc --noEmit over src/ (strict)
npm test # node --test, 180 cases including real-browser e2e
typecheck covers src/ only — that is what ships, and what lib/ is built from. test/ is ESM TypeScript
run directly by Node's type stripping and is verified by EXECUTION, not by the compiler.
⚠️ With a
link:install, do not put@deepseek-ai/*into this checkout's ownnode_modules. Packages that must be a single shared instance come from the host: a copy inside the profile shadows the runtime one (profile > runtime). They are listed underdevDependencieshere purely so typ