dsh-archive-purge
Verifieddsh-archive-purge · v1.0.2 · MIT · Web UI
无论侧栏「归档显示」如何设置,已归档会话都强制不出现在会话列表;提供统一管理面板,可取消归档,或彻底删除会话日志目录、归档与置顶登记及投影缓存。Force-hides archived DSH sessions and adds a panel to unarchive them or delete them permanently.
Install
dsh plugin add dsh-archive-purge Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-archive-purge
已归档会话的强制隐藏与统一管理:把它们彻底挡在会话列表之外,给它们一个管理面板,并补上 DSH 本身不提供的彻底删除。
npm 上的
dsh-archive-manager是另一个不相干的插件。本插件是dsh-archive-purge。

本文所有截图都由虚构的会话渲染——没有真实的标题、路径或体积。
它补上的两个缺口
DSH 能归档会话,但是:
- 归档只是显示层的事。 只要侧栏「视图选项 → 归档显示」被切成显示已归档,已归档行就会继续渲染,归档搜索结果里也一样。没有一个开关的意思是「永远别显示这些」。
- 没有任何删除会话的路径。
dsh-client-ui-workspace未提供会话删除,dsh-session-persistence也没有删除 seam,于是已归档会话会在磁盘上无限堆积。
本插件两件都补上:把已归档会话钉在列表之外,并接管删除路径。
安装
dsh plugin --profile desktop add dsh-archive-purge
把 desktop 换成你自己的 profile 名(例如 web)。然后在设置 → 插件里打开它,那一行显示为 dsh-archive-purge;关掉即同时停用宿主路由与浏览器面板,会话数据不受影响。
它做什么
| 行为 | |
|---|---|
| 强制隐藏 | 侧栏会话列表中,任何已归档会话行都不再显示——无论「视图选项 → 归档显示」被切成什么。做法是 shell.overlay 上一个常驻组件按注册表里的 archivedSessionIds 动态写入 [data-row-key="session:<id>"]{display:none},另外附带按无障碍描述匹配的兜底规则(覆盖搜索结果行)。 |
| 筛选复位 | 浏览器持久化的 dsh.workspace.view.v5 里,archivedFilter 会被复位回出厂默认 default(即「隐藏已归档」),这样菜单不再显示成「全部对话(显示已归档)」而实际看不到东西。 |
| 管理面板 | 侧栏面板图标 →「已归档管理」:统一列出全部已归档会话,含工作区、路径、磁盘占用、创建时间、子会话数;支持搜索、多选、批量取消归档。 |
| 彻底删除 | 逐条或批量彻底删除,弹窗二次确认(列出会话、体积、将删除的内容)。 |
面板的配色取自宿主的 --dsw-alias-* token,并内置中英两套文案,因此它跟随应用当前的主题与语言,而不是自带一套外观。
彻底删除的确切边界

一次删除会移除四样东西:
- 会话日志目录
<DSH_HOME>/sessions/<项目目录>/<编码后的会话 id>/——该会话自有目录及其全部格式代际与本地产物; - 投影缓存记录
<DSH_HOME>/storages/session_projcache/sessions/<id>.json; - 归档与置顶登记(workspace registry 的
archivedSessionIds/pinnedSessionIds); - 浏览器里那一行——通过会话控制器自己已声明的远程事件
api-session/removed(@mode emit)通知客户端移除,而不是留下一行点开报错的幽灵行。
刻意保留:附件文件。附件按内容寻址、可能被其它会话共享,故不在此范围内(面板底部也写明了这一点)。磁盘上已无日志的归档项只会被清理登记,按钮文案会变成「清理登记」。
拒绝条件(先于任何写入)

批量操作会逐类报告结果:删掉了哪些、释放了多少,哪些已无日志可删,哪些被拒绝——被拒的会具名列出并给出原因。
- 会话不在归档集合里 → 跳过。删除只对已归档会话开放,这是最重要的一道闸。
- 本进程中仍存在该会话的 Agent(
ctx.agents.get(id))→ 拒绝(live)。Agent 可能已打开该日志的写入句柄,删文件会让写入方把日志「复活」。 workspace/session-activity瀑布仍报告进行中的工作 → 拒绝(active)。- 目标目录必须位于配置的会话根目录之内,且目录名正好是该会话 id 的编码形式,否则拒绝(
outside-root)。
注意「仅被加载」不构成拒绝理由:浏览器为每个渲染中的会话行保留一份内存镜像,且从不释放,若据此拒绝,浏览久了之后所有已归档会话都会变成不可删除。判据与 DSH 自身的归档闸门(activity 瀑布)一致,另加一条更便宜的 Agent 检查。被跳过/失败的会话会在面板里逐条列出原因。
界面残留(幽灵行)与两层修复
DSH 的浏览器会话列表是事件驱动的:它只会因 api-session/added/removed 增删条目,自己没有「重新拉取并丢弃缺失项」的路径。而被删除的会话同时会离开归档集合,于是它在侧栏里从「被隐藏」变成「该显示」——缓存里那一行就变成了可见、可点、点开报 session/not-found 的幽灵行。
本插件用两层消除它:
- 宿主层(权威):删除成功后,在写归档集合之前
ctx.emit('api-session/removed', id)。这是会话控制器自己为这个事实定义并转发的远程事件,客户端拿到的是一条{ kind: 'remove', sessionId }变更,于是该行被真正从列表移除。发在改登记之前,是为了避免「先取消归档把它露出来、再消失」的闪烁。 - 客户端层(自愈兜底):清单里多带一份
persistedIds(宿主持久化层真正认识的会话 id)。面板把浏览器缓存的 id 与它做差集,把差集行一并纳入隐藏规则,并在标题下显示数量;差集每次读取都重算(不是并集),因此一次读失败不会永久藏掉一个活着的会话。本次页面内自己删掉的 id 另存一份并集——它不依赖任何读取,立即生效。
如果你在装上这两层之前就删过会话:那些残留行需要一次页面刷新(客户端会重新向宿主拉列表)才会消失;之后的删除不再留下它们。
已知残留
- 工作区成员数组里的悬空 id:
tables.workspaces.<id>.sessionIds是持久化的成员数组,宿主没有提供「移除某个成员」的接口(insertSessionBefore只做插入与排序),因此被删会话的 id 会留在里面。DSH 自身容忍缺失成员(派生行时查不到摘要就跳过),所以它不会渲染成任何一行;这属于登记层面的残留,不是界面问题。 - Agent 生命周期:归档只「停止 + 隐藏 + 加一道禁止运行的持久闸门」,不销毁 Agent;Agent 的回收点是 agent-loop provider 卸载或进程退出。所以在同一个 DSH 进程里刚归档过的会话仍会被本插件拒绝删除,重启后可删。详见上文「拒绝条件」。
维护备注
- 宿主半侧为了让删除精确落点,逐字复制了
dsh-session-persistence-jsonl里的纯函数encodeSegment。DSH 升级若改动会话目录布局,这个函数必须跟着改;在此之前,删除会因「目录名不等于编码后的会话 id / 目录内没有会话日志」而拒绝执行,而不是删错东西。 - 客户端半侧的隐藏规则依赖侧栏会话行的
data-row-key="session:<id>"属性(dsh-client-ui-workspace自己加的稳定标记),以及已归档行/搜索结果行上的aria-description文案。前者变化时,aria-description兜底规则仍然生效。 - 没有构建步骤,也没有运行时依赖:两半都是直接发布的纯 ESM,宿主半侧除了 Node 内置模块不导入任何东西。
- 宿主半侧的代码改动不会热重载(Node 的 ESM 缓存按路径命中),改完
index.js需要重启 DSH 或通过插件页面重新启用该 bundle 来生效;client.js会被客户端 HMR 自动替换。
已知取舍
- 内置「视图选项 → 归档显示」的三个选项仍然存在,但由于已归档行被强制隐藏,选「全部对话(显示已归档)」不会再让它们出现在侧栏——这是需求「已归档必须隐藏 + 统一在管理面板里管理」的直接结果。
- 删除不会级联删除由该会话 fork 出的子会话;面板会在行上标出子会话数量作为提示。
- 非中文/英文语言环境下,相对时间与兜底 CSS 使用英文/中文两套文案中的一套。
开发
没有构建步骤——两半都按写好的样子直接发布。改完这样检查:
npm test
npm test 跑 29 个测试,基于 node --test、零依赖。test 目录不进发布包。
test/smoke.test.mjs用桩上下文驱动两半,盯的是重命名会静默破坏的那几个不变量:客户端注册所用的模块 id、宿主挂载与客户端调用的路由前缀、面板占用的 slot key,以及按 id 生成的那条隐藏规则。test/host-delete.test.mjs拿一个临时DSH_HOME跑真实路由,日志目录与投影缓存记录都是真的建、真的删。它把每一条拒绝都钉在做出该判断的分支上,验证被拒绝时磁盘分毫未动,并固定住宿主所承诺的顺序——先广播移除,再改写归档集合。test/package.test.mjs拿 manifest 与发布文件集对账:DSH 运行时解析的每一样东西——入口、./client导出、bundle patch——都必须列在files里,且不允许存在任何会触发构建授权的安装期脚本。