Skip to content

dsh-archive-purge

Verified

dsh-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,并内置中英两套文案,因此它跟随应用当前的主题与语言,而不是自带一套外观。

彻底删除的确切边界

二次确认弹窗

一次删除会移除四样东西:

  1. 会话日志目录 <DSH_HOME>/sessions/<项目目录>/<编码后的会话 id>/——该会话自有目录及其全部格式代际与本地产物;
  2. 投影缓存记录 <DSH_HOME>/storages/session_projcache/sessions/<id>.json;
  3. 归档与置顶登记(workspace registry 的 archivedSessionIds / pinnedSessionIds);
  4. 浏览器里那一行——通过会话控制器自己已声明的远程事件 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 里,且不允许存在任何会触发构建授权的安装期脚本。

许可证

MIT