dsh-session-temp-reaper
English | 中文
给 DSH(DeepSeek Harness)聊天界面加一个按钮:一键清理本次会话产生的临时文件。
按钮在输入框左侧。点开后插件扫描本会话写过、跑命令写出、以及落在临时目录里的文件,列成一张可勾选的清单。勾 = 删除,没勾就是保留。删除前要点两次,删完给你一份结果。
为什么不是「按时间猜哪些是临时」
一开始就是那么做的,结果把别的会话的交付物、插件源码统统扫进来,还让模型给出「可以删」的结论 —— 真正会毁东西的那种错误。
现在改成 模型自己登记:
- 插件注册了一个工具
mark_temp_files,模型(我)在写临时脚本、调试产物、一次性中间文件时主动调用它 - 系统提示里也有一节
## Temporary files提醒模型这么做 - 面板分两组:模型标记的(默认勾上)和 未标记的(折叠,默认保留,只作参考)
启发式扫描没有删掉,它现在只负责「喂未标记那一组」,不再决定默认勾选状态。
功能
- 输入框左侧按钮,面板浮在输入框上方;Esc 或点外面关闭
- 两组清单:标记组默认勾选,未标记组折叠且默认不勾
- 每行显示类型、来源徽章(会话写入 / 指令写入 / 未确认归属 / 系统临时 / 临时目录)、大小、时间和说明
- 交付物(
present出来的文件、可交付记录)和「正在被 profile 使用的插件/依赖」即使被标记也不勾 - 来源徽章之外的额外提示:
已交付、正在使用 - 「问模型」按钮:把当前清单塞进输入框草稿并提交,让模型说明每个文件干什么用、哪些能删 —— 只问,不删
- 「全部保留 / 全部删除」批量切换,删除按钮两次点击确认,5 秒后自动复位
- 中英双语,跟随 DSH 语言设置
安装
本地链接(开发用)
# 假设 profile 在 ~/.dsh/profiles/desktop
cd ~/.dsh/profiles/desktop
npm install --save "link:<本项目绝对路径>"
然后在同一个 package.json 的 dsh.profile.bundles 数组里加上 "dsh-session-temp-reaper",重启 DSH。
从 GitHub 装
cd ~/.dsh/profiles/desktop
npm install --save "github:liyolin/dsh-session-temp-reaper"
同样记得把包名加进 dsh.profile.bundles。
改完 Host 代码(index.js)需要重新加载模块:在 GUI 里把插件关掉再打开(plugin_manager 的 set_plugin),或者重启 DSH。只改 client.js 的话刷新页面即可。
使用
- 点输入框左侧的清理按钮 → 扫描
- 看标记组:该删的默认已经勾好了;想留哪个就取消勾选
- 拿不准就点「未标记的文件 N 个」展开看有没有漏网的,要删就手动勾
- 点「删除 N 个」→ 再点一次确认
- 面板显示「已删除 X 个,失败 Y 个」
模型侧的工具
mark_temp_files({
paths: ["scripts/probe.mjs", "out/report.tmp"], // 相对 cwd 或绝对路径
note: "调试用的临时脚本" // 可选,显示在面板里
})
返回 { marked, ignored }:
marked:登记成功的路径ignored:被拒的路径 + 原因 ——git(在.git里)、empty(空字符串)、outside-cwd(不在本会话工作目录内,面板本来就不会列出它)
标记只写进会话日志(一条 tool/call),没有额外状态文件,所以换机器、换 profile 也随会话一起走。同一会话里重复标记同一个文件是幂等的。
不要标的东西:用户要的交付文件、改过的既有源码。标了也会被 present/依赖根保护挡住,但那属于兜底,不是设计意图。
HTTP 接口
两条 exact Fetch 路由,挂在 DSH 已认证的 /api 通道上:
| 路由 | 请求 | 响应 |
|---|---|---|
POST /api/session-temp-reaper/scan |
{ sessionId } |
{ cwd, items[], profileKnown } |
POST /api/session-temp-reaper/delete |
{ sessionId, paths[] } |
{ results[], removed } |
认证由 DSH 的 connection 层负责:请求先过 Host/Origin 栅栏与浏览器(或 API peer)凭据校验,未认证直接 401,非 owner principal 403,只有通过后才进入插件 handler。插件自己不实现认证,也拿不到裸 socket。无凭据的本地 curl 打不动这两条路由。
扫描结果在 Host 内存里缓存 10 分钟、上限 64 条;删除只接受「该会话最近一次扫描里出现过的路径」,跨会话、凭空构造的路径一律拒绝。
请求体上限 1 MB,而且边读边判:超限立刻中断并 400,不会把整包先读成一个字符串。
测试
npm test # 等价于 node test/probe.mjs
test/probe.mjs 是纯 Node、零外部依赖的行为探针:用假 ctx 驱动真实 apply(),在系统临时目录里建沙盒,跑完自动清理。覆盖路由注册(/api 前缀、仅 POST、无裸 webServer 路由)、方法与请求体校验(含超大流式请求体必须中断读取)、标记与默认勾选、Host 侧保护拒删、.git 与越界拒绝、删除后缓存失效、扫描缓存 LRU 淘汰、会话 cwd 缓存的上限与卸载清理、标记工具过滤与渲染、profile 不可定位时的提示、package.json 的 files 必须带上 test、系统提示段注册。
安全边界
- 只删扫描过的:
paths必须在本次会话的扫描结果里,否则逐条报错 - 限定根目录:文件必须位于会话 cwd、本会话的 spill 目录、或命令显式写到 OS 临时根下的文件;spill 目录只有本会话那一个能整目录删
- 交付物保护:
present/ 交付记录里的文件永远默认保留,而且 Host 侧直接拒删(返回protected),不依赖客户端是否勾选 - 依赖保护:profile 依赖包解析出的真实目录(比如插件被
link:进 profile)里的文件默认保留,Host 同样拒删(protected) - 保护态读全量日志:交付与「多次编辑」判定不受 72 小时候选窗口影响,几天前交付的文件不会因为日志有新事件而掉保护
.git永不出现:任何含.git段的路径直接跳过,不列也不删- 两次点击:删除按钮第二次点击前不会发请求
- 不自动删:插件从不主动删除任何东西,只有你点确认才动
- 超时不留悬空:请求 60 秒超时,面板照常能关掉;删除超时的提示明说「结果未知,请重开面板确认」,不会谎称没删
- 缓存有上限:扫描结果按会话缓存 10 分钟、最多 64 条,LRU 淘汰,长期运行不会一直涨;标记工具用的会话 cwd 缓存同样 64 条上限,插件卸载时整体清空
扫描范围(未标记组的来源)
| 来源 | 含义 |
|---|---|
session |
模型用 write / edit 直接写的文件 |
command |
命令里显式写出的路径(重定向、Set-Content 等) |
scan |
文件创建时间落在本次会话某条 shell 命令之后 15 分钟窗口内,或会话开始后出现在工作区根目录 —— 归属未完全确认 |
external |
命令写到 OS 临时根下的文件 |
spill |
本会话的 dsh-spill-* 目录,整目录列一条 |
事件窗口取会话日志最后 72 小时;工作区遍历上限 1500 个文件 / 20000 个目录项 / 12 层深;跳过 node_modules、dist、.git 等目录。
已知限制
- 模型忘了调
mark_temp_files,标记组就是空的 —— 这时候只剩未标记组可看,不会误删 - 并发会话同时写同一工作区时,
scan来源的归属靠时间窗口推定,面板会标「未确认归属」 - 只写在
cordis.patch.yml里、没进 profiledependencies的包,识别不出「正在使用」 - 定位不到 profile 时,面板顶部会警告「无法确认插件依赖」,此时不假装保护生效
- 删除不可恢复,没有回收站
- 路由挂在
/api认证通道上,因此需要 DSH 的 Web 载体;纯 CLI 环境里按钮不出现
目录结构
index.js Host 侧:扫描、删除、mark_temp_files 工具、系统提示段
client.js 浏览器侧:按钮、面板、i18n
cordis.patch.yml 往 profile 里插入这个 bundle 的 patch
locale/ 插件元信息的中英文
icon.svg 图标
test/probe.mjs 行为探针(npm test)
test/harness.mjs 假 ctx / 假会话日志 / 沙盒工具
许可
MIT
No comments yet. Be the first to write one.