dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
随手做的一个dsh归档架,可以查看,还原,彻底清除已归档的会话,纯大肥鱼手工打造
给 DeepSeek Harness Web GUI 用的归档架。
DSH 的会话可以归档,但产品没有回程票:侧栏菜单只能把它藏起来,没有取消归档入口, 也没有办法删除会话日志。这个插件补上这一页:列出所有已归档会话,并给出缺的那几个动作: 还原、排队删除(可取消)、以及在有宿主支持时的即时释放。
English | 中文
设置 → 归档架(在「Agent 预设」下面那一栏):
session/disposed 从列表里移除),并把新的归档集合对齐给产品的工作区状态。两者都做完,
侧栏才会立刻显示它。唯一的例外:还原的是最后一条归档会话时没有对齐锚点,那时提示会明说
「刷新页面即可」;dsh 时自动删除;随时可以取消,取消后什么都不会发生;agents.release(id) 能力时出现(见下),
点一下把会话的运行实例卸载掉,于是不用重启就能立刻删除它;排队不会立刻删任何东西,它只是记下你的意图:
.archive-shelf/pending.json(会话根目录的上一级),重启后仍在;dsh 之后(启动后几秒的清扫);读取或刷新归档架永远不会删任何东西,
所以"排队"和"删除"是两件分开的事。清扫时若会话仍在常驻/运行中,条目会留到再下一次启动;会话一旦在宿主进程里被载入,就再也没有公开的卸载入口 —— 这是 DSH 当前的形态
(agents 服务没有按 id 释放的方法,创建者拿到的那份 dispose 句柄被丢弃了)。
所以插件的做法是能力探测:
agents.release(id),列表接口会报 canRelease: true,
行上出现释放按钮:释放 → 该会话不再常驻 → 立刻可删,全程不用重启;这份宿主侧能力目前不在上游 DSH 里。需要它就得给自己的 DSH 打补丁
(源码装改 packages/core/agent,npm 装用 pnpm patch),或者等它被上游接受。
补丁随仓库提供:patches/host-release.patch(只动 packages/core/agent —— 保留 agent
handle 的 dispose 能力,并加一个按 id 释放的 release(id),外加一个 161 行的测试)。
cd /path/to/deepseek-harness
git apply /path/to/dsh-plugin-archive-shelf/patches/host-release.patch
pnpm exec tsc -b packages/core/agent && pnpm --filter @deepseek-ai/dsh-agent exec tsdown
# 然后重启 dsh:归档架里常驻的行就会多出「释放」按钮
git pull 拒绝。 冲突时先
git checkout -- packages/core/agent 再 git apply 一次即可,改完必须重新构建并重启。lib/,需要用
pnpm patch @deepseek-ai/dsh-agent 做等价改动(改动语义相同,落点不同)。web profile 的 DeepSeek Harness。开发与验证版本:0.1.5-rc.2。^22.19 || >=24(与 harness 自身要求一致)。一条命令 —— 装依赖与挂载一次完成,重复执行也安全:
dsh plugin --profile web add github:JIUYUE-SEP/dsh-plugin-archive-shelf
然后重启 dsh(Ctrl+C 后重新 dsh web)并刷新页面。静态客户端插件随页面的
boot graph 下发,单纯刷新并不保证拿到新插件(浏览器缓存、扫描时机都会搅局),
重启则一定生效。
这一步为什么够了:本包在自己的 package.json 里声明了 dsh.bundle.patch,并自带
cordis.patch.yml 层;dsh plugin add 在 pnpm 装完后会自动把这个层加进 profile 的
dsh.profile.bundles,所以不需要你手动编辑 ~/.dsh/profiles/web/cordis.patch.yml。
# 手动追加到 ~/.dsh/profiles/web/cordis.patch.yml,然后重启
- insert:
- id: archive-shelf
name: dsh-plugin-archive-shelf
git clone https://github.com/JIUYUE-SEP/dsh-plugin-archive-shelf.git
dsh plugin --profile web add ./dsh-plugin-archive-shelf # 同样会自动挂载;重启生效
dsh plugin --profile web remove dsh-plugin-archive-shelf # 同时从 dsh.profile.bundles 里摘掉
若你当初是照上面那个折叠块手动加的 - insert:,再把它一起删掉即可。
这份插件补的是产品明确没有的能力(归档是单向的、没有会话删除入口、没有卸载会话的 API),
所以它必然要碰一些没有被承诺的东西:workspace registry 的私有 setState、会话日志的磁盘布局、
以及宿主补丁提供的 agents.release(id)。升级 DSH 之后,花一分钟走一遍:
cd /path/to/dsh-plugin-archive-shelf && npm test # 两套件:客户端契约/渲染 + 宿主行为(会打印各项数)
setState 还在);git apply + 重建 + 重启即可(此时功能会自动降级为排队删除)。失败应该是响亮的:拿不到私有 API、路径对不上、读不到会话列表快照时,插件一律拒绝并写出原因,
不会猜着删。真报错的话,把归档架里那句话连同 npm test 的输出一起提 issue。
没有构建步骤:两半都是纯 JavaScript,直接随包发布。
lib/index.js(宿主半) —— 一个 Cordis 插件,注入 workspaceRegistry、
sessionPersistence、webServer;注册一条同源 JSON 路由(POST /archive-shelf/api),
实现 list / unarchive / delete / forget / queue / unqueue / release 七个操作。
删除直接用 node:fs —— 宿主插件就是普通 Node 代码,不需要任何沙箱提权。
队列是本插件自己的状态,写在 <harness home>/.archive-shelf/pending.json,
写入走"临时文件 + rename",避免半截文件。lib/client.js(浏览器半) —— 直接写成客户端模块加载器的工厂形态
(window.__ModuleLoader__.load({ id, factory }))。原因是 harness 自己的客户端打包预设
位于其仓库内部、并未发布。它只注册一个 settings.section,
于是这一页出现在设置卡片的左侧 tab 栏里,产品源码一行都不用改。路由是自证的:POST + content-type: application/json + 自定义头
x-archive-shelf-client: 1 组合起来对浏览器是非简单请求,跨站页面会先撞上
CORS 预检(本服务不回答),再叠加一层 Origin/Host 同源校验。
这防的是跨站请求,不防同机其它进程 —— 那些进程本来就能直接读写会话文件。
setState 改写归档集合。将来 harness 改了内部结构,插件会明确报错
而不是静默失败。npm test # 零依赖;两个套件:契约/渲染 + 宿主行为
test/plugin.test.mjs 把宿主半当 ESM 模块导入、按客户端加载器的方式求值浏览器半,
并用一个极简 React 替身渲染出行,断言徽标、按钮可见性与禁用理由、失败文案,
以及每个按钮真正发出的那个请求;test/host.test.mjs 用假 Cordis 上下文 + 真 loopback HTTP 服务驱动真的 apply(),
在临时目录里真删真写:路径逃逸、超大请求体、运行中/常驻拒绝、队列的排队/取消/跨重启兑现、
释放能力的三种结果、以及降级服务组合。每一项都是可运行的回归断言,npm test 打印准确项数。改代码前值得知道的两条不变量:
registration.id === manifest.name)——
loader 按这个名字查工厂;两者漂移时测试会失败。node: 内置模块,其它能力全靠注入的服务。
这正是它能待在 harness 仓库之外却仍能加载的原因。MIT —— 见 LICENSE。