dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
归档会话管理 · Archived sessions — DeepSeek Harness (DSH) 设置面板:列出本机全部已归档会话,逐条复原回工作区,或连同会话日志与投影缓存永久删除。 A DSH settings panel that lists archived sessions and either restores one into its workspace or deletes it from disk for good.
DeepSeek Harness(DSH)桌面端的设置面板插件,版本
2.0.0:把本机所有已归档会话集中到一页, 多选后批量复原回工作区,或连同会话日志与投影缓存永久删除;按标题、项目、路径搜索, 需要时再按需搜索会话内容。首屏不再为每一行折叠会话日志。
已归档的会话会从项目列表里消失,而 DSH 本身没有管理它们的界面。本插件补上这一页:一行一个会话, 给出标题、所属项目、路径、体积、最近写入时间,以及「运行中 / 仅归档记录」状态;选中若干行即可批量复原或批量删除。
| 项目 | 值 |
|---|---|
| 包名 | @local/dsh-archive-manager(私有包,未发布到 npm) |
| 版本 | 2.0.0 |
| 实测宿主 | DSH 0.2.1-rc.3 桌面版(Windows,app.asar) |
| 位置 | 设置 → 归档会话(settings.section,order: 100,排在所有内置页面与其它插件之后) |
| 插件行 id | archive-manager(cordis.patch.yml 里的条目,停用或改配置都按它定位) |
| 设置页 section id | archived-sessions(插件卡片标题是「归档会话管理」) |
| 运行时依赖 | 无。浏览器半部只 require('react') |
| 构建步骤 | 无。index.js / client.js / lib/*.js 就是全部产物 |
页面是设置面板滚动容器里的一个流式区块:不自带高度、不抢滚动条,列宽上限 720px,随设置面板一起滚动。 从上到下:
N 个归档会话 · 总体积、可删除 N、运行中 N、仅记录 N;还有标题在补时显示「正在读取标题…」。N 个会话(筛选时是 命中数 / 总数 个会话)、以及「大小与时间来自磁盘上的会话日志」的提示。cwd 时显示项目路径,否则显示会话目录的 $DSH_HOME/… 形式);已选 N 项、合计体积、批量复原、批量删除、取消选择。POST /api/archive/delete 要求请求体带 confirm: true,否则整批拒绝(CONFIRM_REQUIRED)。
页面里的确认对话框只是礼貌,真正拦住误删的是这个字段。即时筛选(默认,不发请求):搜索框输入后 140ms 防抖,在已经载入的快照上过滤,词不区分大小写、按空格切分,
每个关键词都要命中(AND),每个词可以落在不同字段上。参与匹配的字段:标题、项目名、路径(cwd 或会话目录)、会话 id。
上限 500 字符(与 Harness 自身的会话搜索一致)。
内容搜索(可选,较慢):打开「内容搜索」开关后按回车或点「开始搜索」,Host 会分页扫描归档会话的日志:
sessionQuery.filterEvents:只取 user/message 与 assistant/message、surface: current 的记录,再按字面文本匹配;seq/时间,以及命中的片段(页面高亮关键词),其余行按命中集合收窄;skipped),不中断整次扫描;部署没有挂载 sessionQuery 时提示「当前部署不支持内容搜索。」;内容搜索是这一页唯一会打开会话日志的路径,因此它必须由用户显式开启、分页、可中断。
2.0.0 之前,这一页为了显示标题会逐行折叠会话日志;一台有 162 条归档会话的机器上,这等于首屏先打开 162 个日志。 现在分成两层:
sessionQuery.listSessions,把运行中的会话合并到持久化列表之上)、
一次会话根目录的两级扫描(拿到「编码后的 id → 目录」,比按推导路径逐条 stat 更便宜,也能发现目录放错位置的会话)、
每行一次 readdir + 每个日志文件一次 stat(体积与 mtime)。标题先读宿主已经加载在内存里的投影缓存
(sessionProjectionCache.cachedSnapshot / cachedPredecessorTitle),零 I/O。这一层不打开任何会话日志。GET /api/archive/titles(每批 24 个,整轮最多 24 批),
折出来的标题直接补进已经渲染的行;缓存明确回答「这个会话没有标题」的行不会再触发折叠,直接显示「(无标题)」。缓存与失效:
以下默认值来自 lib/snapshot.js 的 DEFAULTS:
| 配置键 | 默认值 | 含义 |
|---|---|---|
headersTtlMs | 15000 | header 列表与根目录扫描的复用时长(毫秒) |
snapshotTtlMs | 4000 | 整份快照的复用时长 |
diskTtlMs | 3000 | 单个会话目录文件事实(体积 / mtime)的复用时长 |
titleTtlMs | 600000 | 折叠出来的标题的复用时长(10 分钟) |
titleBatchSize | 24 | 一次标题深读最多折叠几个 |
titleBudgetMs | 6000 | 一次标题深读的墙钟预算 |
concurrency | 12 | 同时检查多少个会话目录 |
maxBatchSize | 500 | 一次批量操作最多接受多少个 id |
在 profile 自己的 cordis.patch.yml(默认 ~/.dsh/profiles/<profile 名>/cordis.patch.yml)里按行 id 覆盖:
- id: archive-manager
config:
snapshotTtlMs: 8000
concurrency: 6
两点要注意:这个 config 是整体替换而不是逐键合并,没有写出来的键会回落到上表的默认值;
配置改动由 loader 的配置重载生效,如果页面数据没变,重启桌面端。
「永久删除」不是 DSH 已有的 API:会话持久化接缝明确说明「没有任何东西会删除会话文件……这个接缝没有删除 API」,
ctx.workspaceRegistry 也只负责记账、不动文件。所以删除是本插件自己推导路径后执行的文件系统操作,
边界必须自己立起来。
cwd。浏览器无法指定要删哪个文件。
载荷里虽然带绝对路径字段(directoryPath / homePath / sessionsRootPath),但页面只显示 $DSH_HOME/… 形式,
这些字段不参与删除判定。workspaceRegistry.archivedSessionIds 中即拒绝(NOT_ARCHIVED)。SESSION_LIVE),页面也会禁用它的删除按钮。lib/layout.js 的 assertDeletableDirectory,任何一项不成立就拒绝,绝不做「尽力而为」的删除):
$DSH_HOME/sessions 之下,且不是根目录本身(OUTSIDE_ROOT);并且恰好是「项目层/会话层」两级(MISPLACED)。cwd 推导出的 projectKey(cwd)(PROJECT_MISMATCH),
会话目录名必须等于 encodeSegment(id)(ID_MISMATCH)。这两条推导规则是从 DSH 自带 JSONL 后端移植的,
编码是单射的,所以「这个目录是不是这条会话的」是可判定的相等,不是猜。NOT_A_SESSION_DIRECTORY),否则按「看不懂的目录」处理。UNARCHIVE_FAILED:「文件已删除,但归档记录未能移除」),不会假装整批都失败。api-session/removed,让侧栏立刻收起这一行。workspaceRegistry.unarchiveSession(id) 把 id 从归档集合移除,日志与投影缓存一个字节都不碰;
会话原本就在工作区里占着记账槽位,所以它会回到原来的项目与位置。五条路由都注册在 connection 服务的共享 /api 通道上(ctx.connection.fetch.register,requestBody: 'buffered')。
通道自己负责会话鉴权与 Host/Origin 校验,所以本插件一律回 HTTP 200,用一个 ok 判别式区分结果——
这样「业务拒绝」永远不会被误认成「通道鉴权失败」。所有响应都是 cache-control: no-store。
| 方法 | 路径 | 请求要点 | 响应要点 |
|---|---|---|---|
| GET | /api/archive/sessions | refresh=1 跳过缓存重建快照;q 可选(多关键词 AND,服务端同款匹配规则) | ok、generatedAt、totals{archived,onDisk,live,bytes,deletable}、capabilities{batchLimit,titles,contentSearch}、items[];带 q 时 items 只留命中行并附 matched 字段 |
| GET | /api/archive/titles | ids 逗号分隔(上限 200 个);budgetMs(默认 6000,上限 30000);batch(默认 24) | ok、titles{ id: 标题或 null }、pending[](本轮没折叠到的)、failed[](折叠被拒的);ids 为空时 {ok:false, code:'EMPTY_SELECTION'} |
| GET | /api/archive/search | q 必填;cursor(从 0 开始的扫描位置);batch(默认 8,上限 24);budgetMs(默认 2500,上限 8000) | hits[{id,count,seq,time,snippet}]、skipped[]、scanned、cursor、nextCursor、total、done、truncated;q 为空 EMPTY_QUERY,没挂载 sessionQuery 时 UNAVAILABLE |
| POST | /api/archive/restore | { "ids": [...] }(兼容旧式 { "id": "..." }) | ok、results[](逐项 ok/失败码)、summary{requested,succeeded,failed,bytes}、payload(刷新后的快照) |
| POST | /api/archive/delete | { "ids": [...], "confirm": true };dryRun: true 时免确认、只判定不删除 | ok、dryRun、results[]、summary、payload;缺 confirm 整批 CONFIRM_REQUIRED |
失败时是 { ok: false, code, message } 仍是 200。请求级错误码:INVALID_BODY、INVALID_IDS、EMPTY_SELECTION、
TOO_MANY_IDS、CONFIRM_REQUIRED、UNAVAILABLE、INTERNAL。逐项错误码:NOT_ARCHIVED、LIVE_SESSION、NO_HEADER、
NOT_ON_DISK、OUTSIDE_ROOT、MISPLACED、PROJECT_MISMATCH、ID_MISMATCH、NOT_A_SESSION_DIRECTORY、
REMOVE_FAILED、UNARCHIVE_FAILED、RESTORE_FAILED。
实测环境:DSH 0.2.1-rc.3 桌面版(app.asar)。插件是按这一版的内部接缝写的,不是公开 API。
用到的宿主接口:
| 接口 | 用途 |
|---|---|
ctx.connection.fetch.register | 注册五条 /api/archive/* 路由(buffered 请求体,返回 Response) |
ctx.workspaceRegistry | archivedSessionIds / pinnedSessionIds / list()(项目与 sessionIds)/ unarchiveSession(id) |
ctx.sessionQuery | listSessions()(合并运行中会话的 header 列表)、readTitleSnapshots(ids, signal)(折叠标题)、filterEvents(id, filters)(内容搜索) |
ctx.sessionProjectionCache | cachedSnapshot(header) / cachedPredecessorTitle(header):首屏零 I/O 的标题来源 |
ctx.get('sessions').list() | 最近一次「哪些会话在本进程里运行」的权威答案(即使 header 列表命中缓存也要重新问) |
另有 ctx.sessionPersistence.list() 作为 listSessions 缺席时的退路,ctx.emit('api-session/removed', id) 用于驱动侧栏刷新。
依赖:没有第三方运行时依赖,package.json 里只有 @deepseek-ai/cordis 的 peer(^4.0.1)和 engines.node >= 20;
没有构建步骤,没有 bundler,没有需要 require 的官方客户端包(页面样式只用 --dsw-* 主题令牌)。
版本范围:package.json 没有声明 engines.dsh,所以插件页不会替你拦版本不匹配;升级 DSH 后请自己先确认这一版还能用。
先装好 DSH 桌面端(deepseek.com/download)。本插件是标准 DSH 组合包:
package.json 里声明了 dsh.bundle.patch 与 dsh.client,装进任何 profile 都走同一条路。
https://github.com/linanbuan/dsh-archive-manager2.0.0 与简介,
点 「立即启用」。这一步没有第二次「安装」确认;网络受限时:如果插件页报「无法访问 GitHub」或「连接 GitHub 超时」(它会给出「试试其他方式」「改用国内镜像」, 对话框里的「安装源」也可以换一个),也可以先在本机克隆,再把目录绝对路径填进「包名或地址」:
git clone https://github.com/linanbuan/dsh-archive-manager.git
插件页做的事,等价于一次 dsh plugin 调用;具体写法是 dsh plugin add <spec>。
以你所用版本的 dsh CLI 帮助为准(dsh plugin --help),本页不列出未经验证的参数。
在 0.2.1-rc.3 这一版,dsh plugin 是把参数转交给 profile 目录里的 pnpm,因此带 profile 的完整形式是:
dsh plugin --profile <profile 名> add <spec>
<spec> 与插件页里的「包名或地址」相同。注意桌面端 profile 由 Electron 托管,npm 版 CLI 会拒绝管理它的插件——
桌面用户请走第 1 种方式。
手动路径确实存在:把本目录登记到 profile 的 dsh.profile.bundles,再安装依赖。
但这正是官方插件管理器替你做的事,手改配置文件既容易写坏 profile,也可能让 Harness 起不来 ——
请用第 1 种方式(插件页)完成安装,不要手改配置文件。
dsh.profile.bundles 里去掉。0.2.1-rc.3 的内部接缝,宿主改名或改形都会让它失效;
失效时页面会退化成错误提示或空列表,不会影响 Harness 本身启动。$DSH_HOME:删除是本机文件系统操作,不会同步到其它设备,也不会动云端任何东西。$DSH_HOME 在需要提权的目录下,删除会以 EACCES/EPERM 之类的错误如实报出,不会假装成功。C:\… cwd、中文与空格目录名都有对应用例。node test/selfcheck.mjs # 或 pnpm test
自检不需要网络、不需要 Harness、不需要 React:Host 半边在临时 $DSH_HOME 里跑真实的建目录 / 删除 / 复原流程,
五条路由由一个假 harness 供给(含归档集合、会话 header、投影缓存与 filterEvents);
浏览器半边在 stub window.__ModuleLoader__ 里完成注册,再直接调用页面导出的 __test 纯函数
(筛选、排序、区间选择、体积汇总、字典键集合)。
当前状态(2026-10-24 实测):node test/selfcheck.mjs → 50 checks, 0 failed,退出码 0。
没有覆盖的部分,如实说明:真实渲染(没有 React 运行时,因此不驱动 hook 与 DOM,点击流程是照着导出函数断言的)、
真实 Harness 服务、sessionQuery 未挂载时的 sessionPersistence 退路(只断言搜索路由的 UNAVAILABLE)、
以及三个 TTL 的过期行为(只在「TTL 内复用」这一侧断言)。
package.json 组合包清单(dsh.bundle.patch + dsh.client),无运行时依赖
cordis.patch.yml 一行 insert:装载 Host 半边(行 id:archive-manager)
index.js Host 半边:五条 /api/archive/* 路由
lib/snapshot.js 两级快照、缓存与代际失效、配置默认值
lib/ops.js 批量复原 / 批量删除,逐项结果与归档记账
lib/search.js 匹配规则(多关键词 AND、大小写不敏感、片段截取)
lib/concurrency.js 受限并发(mapLimit / settleLimit)
lib/layout.js 会话存储布局移植:projectKey / encodeSegment / 路径推导与删除校验
client.js 浏览器半边:settings.section 页面(手写 lazy CJS,只用 react)
locale/{zh,en}.json 插件卡片文案
docs/项目日志.md 工程记录:问题、调研、改动、性能取舍、验证
test/selfcheck.mjs 自检
icon.svg 插件图标
connection、workspaceRegistry、sessionQuery、sessionProjectionCache、sessions)
与 Node 内置模块,没有复制或改编任何第三方插件的代码。require 官方客户端包。MIT © 2026 linanbuan