dsh-archive-favorites-forge
Verifieddsh-archive-favorites-forge · v0.24.3 · MIT · Web UI
Favorites, copy id+path, per-session delete, idle batch archive and a favourites filter for DSH conversations — additive slot extensions over the shipped archive feature, editing no official file. 为 DSH 对话增加收藏、复制 ID+路径、单条删除、超时批量归档与收藏筛选。
Install
dsh plugin add dsh-archive-favorites-forge Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-archive-favorites-forge
给 DSH 的对话加上收藏、复制 ID+路径、单条删除、超时批量归档和收藏筛选 —— 全部挂在官方插槽上,不改官方一行代码。
Favorites, copy id+path, per-session delete, idle batch archive and a favourites filter for DeepSeek Harness — additive slot extensions over the shipped archive feature, editing no official file.
English | 简体中文
效果图
① 对话菜单 —— 在官方「…」菜单里新增三行:收藏对话、复制 ID + 路径、带二次确认的永久删除:

② 收藏筛选 —— 在官方「筛选会话」分组里新增两行,把侧栏收窄到「已归档的收藏」与「未归档的收藏」:

③ 设置页 —— 超时批量归档、带归档状态的收藏清单、以及批量删除归档中未收藏的对话:

一句话说明
DSH 自带的归档功能只有「归档 / 取消归档 / 筛选已归档」,没有收藏、没有单条删除、 没有复制 ID 或路径、也没有按时间批量归档。这个插件把这些补上,而且一个字都不改官方文件。
功能总览
| 中文 | 位置 | |
|---|---|---|
| ① | 收藏对话:归档或未归档的都能收藏,星标即时生效 | 侧栏行「…」菜单 + 行内 ☆ |
| ② | 复制 ID + 路径:一键复制会话 ID 与转录文件绝对路径(两行) | 侧栏行「…」菜单 |
| ③ | 单条永久删除:任意非收藏对话可删,二次确认;删完侧栏立即移除该行 | 侧栏行「…」菜单 |
| ④ | 超时批量归档:把闲置超过 N 小时的未归档对话一次性归档,可一键撤回 | 设置 → 归档收藏 |
| ⑤ | 导出清单:制表符分隔的 txt,含对话名称 / 对话ID / 所属工作区 / 闲置时长 / 最后活动时间;可复制路径或直接打开所在文件夹 | 设置 → 归档收藏 |
| ⑥ | 收藏筛选:「仅显示收藏(已归档)」与「仅显示收藏(未归档)」 | 视图选项 → 筛选会话 |
| ⑦ | 批量删除:一键永久删除「已归档且未收藏」的对话 | 设置 → 归档收藏 |
| ⑧ | 删除护栏:收藏项拒删、越出会话根目录拒删、符号链接拒删、逐条报告、批量上限 | 自动生效 |
安装
dsh plugin --profile <profile> add dsh-archive-favorites-forge
装完必须完全退出 DSH 再打开。宿主半边(lib/index.js)在本代 harness 里没有热重载,
客户端 bundle 也在 DSH 启动时就快照了 —— 所以刷新页面拿不到新版,必须重启。
工作原理
lib/index.js 宿主半边 —— 收藏存储、转录路径解析、JSON 路由、带护栏的删除、归档/取消归档
lib/client.js 客户端半边 —— 官方插槽注册 + 一条 DOM 规则
用到的官方插槽(全部来自官方客户端契约):
| 插槽 | 贡献内容 |
|---|---|
sidebar.workspaces.session.menu.item |
3 行:收藏、复制 ID+路径、删除 |
sidebar.workspaces.session.row.action |
1 个行内 ☆ 星标 |
settings.section |
「归档收藏」设置页 |
所有贡献都通过 ctx.slots.inject 注册 —— 也就是「等宿主条目挂载后再注册」的官方契约。
所以插件从不假设侧栏已经起来,也从不持有宿主节点的引用。
唯一的例外是收藏筛选:官方那个「视图选项」菜单是写死的数组,既没有插槽也没有触发器 id,
所以这一个控件只能走 DOM —— 做法是找到官方那条筛选行并克隆它。
定位只依赖 role / aria / data-row-key 这些稳定属性,绝不依赖 CSS 模块哈希
(那些类名每次构建都会重新哈希)。
为什么收藏存在文件里,不用 localStorage
渲染进程的 localStorage 是按 origin 隔离的:桌面端、手机端、远程浏览器是三个不同 origin,
各自只会看到自己的星标。所以宿主半边把它们持久化到:
~/.dsh/data/dsh-archive-favorites-forge/favorites.json
这样换 profile、重装、两端之间都共享同一份收藏。
为什么删除放在宿主端并带护栏
本代 harness 没有官方的会话删除 RPC —— 官方 UI 从来不删会话。
所以「删除这条对话」在这里的物理含义是:删掉该会话自己那个产物目录(~/.dsh/sessions/ 下)。
这个操作不可逆,因此宿主端在动手前逐项拦:
- 收藏的 id 直接拒删(在宿主端判定,不只是 UI 不显示);
- 解析出来的路径越出 sessions 根目录的拒删;
- 符号链接拒删;
- sessions 根目录本身拒删;
- 单次批量受
maxDeletePerCall上限约束。
每个 id 单独回报结果,所以一条失败不会中断整批。
删除成功后宿主会发出 harness 自己的 api-session/removed 事件,
所以侧栏那一行会立刻消失,而不是挂到下次刷新。
配置
| 键 | 默认 | 含义 |
|---|---|---|
enabled |
true |
宿主路由总开关 |
maxDeletePerCall |
500 |
单次删除请求的硬上限 |
- id: dsh-archive-favorites-forge
config:
maxDeletePerCall: 200
注意事项与边界
- 会话 id 有两种拼写:
session-<uuid>,但磁盘上的产物目录两种都有 (老机器升级后<uuid>与session-<uuid>并存)。插件所有比较都经过归一化, 路径解析也两种都试,所以新老对话都能正确复制出路径。 - 已归档的对话打不开 —— 这是 DSH 官方行为,不是本插件造成的。想读就先取消归档。
- 删除是永久的,会连同转录一起删掉。不会碰你的项目工作目录。
- 空白会话永不作为候选。 「空白」= 从没发过消息的「新会话」占位槽。
它没有内容可归档,而且官方侧栏只显示当前那一个空白会话、其余全部隐藏 ——
所以候选清单里出现它的话,你根本无法在界面上核对。
插件已把空白会话从超时归档候选和批量删除集合里都排除,
导出时其余对话的命名也与侧栏用词一致(
新会话/未命名)。 - 候选清单会与「侧栏真正列出的会话」求交集:已归档但侧栏不渲染(没有
cwd)的会话, 既不会被统计,也不会被操作。 - 插件会读 Workspace 注册表来判断会话归属哪个文件夹,但归档/取消归档只走注册表自己的
archiveSession/unarchiveSession方法,绝不手写状态文件,也绝不改其他插件的内部实现。
开发
lib/index.js 宿主半边
lib/client.js 客户端半边(__ModuleLoader__ bundle)
test/probe-client.mjs DOM stub + 宿主探针测试
跑测试:
node test/probe-client.mjs
这套探针不是走过场 —— 它反复抓出了「语法检查通过、人工读码也通过」的真 bug,包括:
- 项目目录名编码器把分隔符折叠成
C--Users(正确是C-Users), 会让每一次「复制路径」都指向一个不存在的文件; - 路径解析只试了裸 uuid 目录形式,对目录带
session-前缀的那些会话静默失败; - 隐藏规则没有等收藏集合加载完成就生效,可以把整个侧栏清空;
- 归档集合读取失败会连带整个收藏筛选静默失效,因为两个本该独立的闸门共用了同一个标志位。
它对着机器上真实的目录和真实的注册表断言,同时 DOM fixture 逐项照抄官方结构
(包括「选中标记是选中行上那个裸 <svg>」这一条)—— 上面每个 bug 都是这么暴露出来的。
许可
MIT。