🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

会话硬删除 qwe4888789/dsh-hard-delete

DeepSeek Harness
qwe488878924 天前有提交已提供中文文档扫描:含敏感能力

在会话菜单一键彻底删除会话,不可恢复

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add qwe4888789/dsh-hard-delete
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么[目录]—
dsh 原生插件 · other
暂无时间戳·判据 →
装得上吗[实装验证]!
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
数据截至 2026/11/04·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 24 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

⚠︎ 实装验证未通过(unknown · 2026/11/4) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 22 处,展示前 12 条)
  • 读取本地文件
    lib/index.js:95
    if (!existsSync(file)) continue
  • 读取本地文件
    lib/index.js:97
    const parsed = JSON.parse(readFileSync(file, 'utf8'))
  • 读取本地文件
    lib/index.js:168
    try { return statSync(p).isDirectory() } catch { return false }
  • 读取本地文件
    lib/index.js:172
    if (!existsSync(file)) return undefined
  • 读取本地文件
    lib/index.js:173
    try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return undefined }
  • 写入本地文件
    lib/index.js:179
    writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf8')
  • 写入本地文件
    lib/index.js:180
    renameSync(tmp, file)
  • 读取本地文件
    lib/index.js:207
    try { return readdirSync(dir) } catch { return [] }
  • 删除本地文件递归
    lib/index.js:474
    rmSync(found.dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 60 })
  • 删除本地文件递归
    lib/index.js:483
    if (isDir(projectDir) && safeReaddir(projectDir).length === 0) rmSync(projectDir, { recursive: true, force: true })
  • 删除本地文件
    lib/index.js:516
    rmSync(entryFile, { force: true })
  • 发起网络请求
    lib/client.js:286
    const res = await fetch(apiPrefix + path, Object.assign({
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 一键彻底删除会话
  • 删除不可恢复

README

由 DeepSeek 最新模型翻译生成
dsh-hard-delete 仓库:<https://github.com/qwe4888789/dsh-hard-delete> 许可:AGPL-3.0-only(+ 商业授权,见第九节) | 版本:v0.1.0 | 类型:cordis 插件(host + client 双半,零运行时依赖) 在 DSH 的侧边栏会话「…」菜单里加一行 删除 —— 点一下就把这个会话彻底删掉: - 不可恢复:直接物理删除,不进回收站、没有回滚。 - 不设门槛:不看「疑似还在活动」,不要求二次输入,只弹一次确认框(因为不可逆)。 - 当前会话也能删:确认框会明确提示「这正是你当前打开的会话,删完请切换或刷新」。 - 删完整:会话目录之外,还清掉指向它的 projcache 条目与 workspace.json 引用,避免留下悬空元数据。 与同机另一个插件 dsh-session-recycle 的分工:那个是设置页里的「软删 → 回收站 → 恢复 / 回滚」,保守、可救回;这个是菜单里的一键硬删。两者互不干扰(本插件不碰 _recycle-bin/)。
展开完整文档(README 剩余 1 节)
dsh-hard-delete

仓库:<https://github.com/qwe4888789/dsh-hard-delete> 许可:AGPL-3.0-only(+ 商业授权,见第九节) | 版本:v0.1.0 | 类型:cordis 插件(host + client 双半,零运行时依赖)

在 DSH 的侧边栏会话「…」菜单里加一行 删除 —— 点一下就把这个会话彻底删掉:

- 不可恢复:直接物理删除,不进回收站、没有回滚。
- 不设门槛:不看「疑似还在活动」,不要求二次输入,只弹一次确认框(因为不可逆)。
- 当前会话也能删:确认框会明确提示「这正是你当前打开的会话,删完请切换或刷新」。
- 删完整:会话目录之外,还清掉指向它的 projcache 条目与 workspace.json 引用,避免留下悬空元数据。

与同机另一个插件 dsh-session-recycle 的分工:那个是设置页里的「软删 → 回收站 → 恢复 / 回滚」,保守、可救回;这个是菜单里的一键硬删。两者互不干扰(本插件不碰 _recycle-bin/)。

一、形态与注册

本插件同时声明 dsh.bundle 与 dsh.client,因此走 bundle 层注册:

"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": { "inject": [], "platform": "web" }
}

dsh.client.inject 刻意留空:本插件浏览器半只 require("react")(平台种子词)。
为什么不去 require 官方 UI 组件库,见第六节第 7 条。

⚠️ 只有 dsh.client 而没有 dsh.bundle 的插件不能进 dsh.profile.bundles ——宿主会以 declares no dsh.bundle 拒绝并启动即崩。本插件两个都声明了,所以不受此限。

装到某个 profile:

方式 A(推荐):让 DSH 自己装 —— 桌面端「插件 → 添加插件」里粘贴本仓地址,或命令行(profile 名按你的实际环境改):
dsh plugin --profile web add qwe4888789/dsh-hard-delete

方式 B:声明式手工安装在 <DSH_HOME>/profiles/<name>/package.json 里
dependencies: { "dsh-hard-delete": "link:<本仓路径>" }
dsh.profile.bundles: 追加 "dsh-hard-delete"
cd <DSH_HOME>/profiles/<name> && pnpm install
重启 DSH

桌面版(DeepSeek Harness 桌面端)的 desktop profile 由 Electron 应用独占,别用 dsh plugin --profile desktop …(CLI 会直接拒绝);桌面端请走「插件 → 添加插件」。

二、两个槽位

| 槽位 | 挂什么 | 为什么 |
| sidebar.workspaces.session.menu.item | 菜单里的「删除」行(id hard-delete,order 500,排在 archive 的 400 之后) | owner 注入 { sessionId, displayTitle } 与 useMenuOpenState 钩子 |
| shell.overlay | 确认框 + 结果提示 | 必须挂在常驻浮层上:确认框若挂在菜单项里,菜单一关它就跟着卸载了 |

菜单项只做一件事:把「待确认请求」写进模块级小 store,然后关掉菜单。
浮层里的对话框读同一个 store,所以在菜单卸载后依然活着。

界面自绘(官方 MenuItemButton / Modal / Button / Toast 拿不到,见第六节第 7 条),但完全照官方 Menu.module.css 的规格来:min-height:34px / padding:6px 8px / gap:6px / border-radius: var(--dsw-radius-md) / hover 用 --dsw-alias-interactive-bg-hover,危险行用 --dsw-alias-state-error-primary + --dsw-alias-interactive-bg-hover-danger。DOM 也与官方同构: div > [role=separator] + button[role=menuitem] —— 菜单的键盘遍历与焦点回收都是读 DOM 的,自绘按钮同样能参与。每个 var() 都写了兜底值,主题变量缺失时不会变成透明。

三、host 半的路由

前缀可配(默认 /api/dsh-hard-delete),全部 loopback-only + same-origin 校验。

| 方法 | 路径 | 请求体 | 说明 |
| GET | /config | — | 公开配置(apiPrefix / 文案覆盖 / 开关 / configErrors),不含绝对路径 |
| POST | /inspect | {sessionId} | 一个会话的概况:标题 / 工作目录 / 大小 / 文件数 / 有无线索日志 |
| POST | /delete | {sessionId, confirm:true} | 彻底删除(不可逆) |

错误统一 { error, code }。值得注意的 code:session-not-found(404)、
confirm-required(400)、invalid-sessionId(400)、path-escape(400)、
loopback-only(403)、body-too-large(413)。

返回列表里不含任何绝对路径——界面只显示标题 / 工作目录 / 大小 / 文件数。

四、删一个会话要动几处

<DSH_HOME>/
├─ sessions/<projectKey>/<sessionId>/
│ └─ session[.v<N>].jsonl[.zstd] ← ① 整个目录物理删除(多版本日志一起) ├─ storages/ │ ├─ session_projcache/sessions/<id>.json ← ② 新版:每会话一个文件 │ ├─ session_projcache.json ← ③ 旧版:tables.sessions[<id>] │ └─ workspace.json ← ④ archivedSessionIds / pinnedSessionIds │ / tables.workspaces[*].sessionIds └─ (浏览器那一边) └─ ctx.emit('api-session/removed', id) ← ⑤ 让侧边栏那一行立刻消失

安全性质:

- 路径纵深防御:sessionId 必须是单层路径段(挡 . / .. / 分隔符 / NUL),定位后再用 relative() 确认最终目录没跑出 sessions/。
- 只认精确路径:只在 sessions/<任意项目目录>/<精确的 sessionId> 下动手,绝不复刻宿主的目录名编码规则(也就不会被规则变化带偏)。
- 不覆盖、不猜:定位不到就是 404,什么都不做。
- 元数据清理失败不冒充删除失败:降级成 warnings 如实回传,界面照实提示。
- 原子写 JSON:先写 .tmp-<pid>-<ts> 再 rename,网络盘上失败也不留半个文件。
- 只读探测:/inspect 全程不写盘。

五、七个实测踩出来的坑(本节是这份代码最有价值的部分)

1. 多帧 zstd:Node 的同步接口只解第一帧。 DSH 的会话日志是多帧拼接的。
zstdDecompressSync(整个文件) 实测在 107 KB 的文件上只吐 187 字节(就第一帧),
zlib.createZstdDecompress() 流也一样停在第一帧。必须按 magic 28 B5 2F FD
切帧、逐帧解。第一版没做这件事,标题/工作目录大面积读不出来。

2. 头部解码要「帧数 + 字节」双限。 帧大小不固定(实测 1.5 KB ~ 35 KB 都有),只限帧数会漏后文,只限字节会白解一堆帧。两个都设,取先到的那个;并且 magic 也可能出现在压缩数据内部,解不开就跳过,靠 JSON 解析兜住误判。

3. projcache 有两种布局,都得清。 实测 <home>/storages/session_projcache/sessions/<id>.json 与 <home>/storages/session_projcache.json 的 tables.sessions[<id>] 同时存在。
只清一种就会留下悬空条目。

4. 存在「空壳」会话目录。 真实 ~/.dsh 里有目录建了但没写成日志的会话(实测 15 个会话目录里有 1 个)。如果「必须扫到日志才算会话」,这类目录就永远删不掉。改成:目录名精确等于 sessionId 就认,日志有无只作为 hasLog 事实回传。

5. 侧边栏那一行不会自己消失 —— 但有一条现成的事件。 客户端 ctx.sessions(@deepseek-ai/dsh-api-session-controller 的 client 半)通过 ctx.remote.$on('api-session/added' | 'api-session/removed', …) 维护会话列表;宿主侧 @deepseek-ai/dsh-api-remotes 把这批宿主 Cordis 事件原样转发给浏览器。
所以删完文件只要 ctx.emit('api-session/removed', sessionId),那一行立刻消失,不必等下一次 list 刷新,也不必自己去动客户端的列表状态。

6. 删除当前会话受 Windows 文件语义影响。 宿主对日志文件是持有句柄持续追加写的。
Node/libuv 打开文件时带 FILE_SHARE_DELETE,所以删除通常能成功(名字立刻消失),但宿主后台仍可能在写那个已经 unlink 的文件。因此确认框里明确写了「若它正在运行,建议删完重启一次 DSH」。

7. @deepseek-ai/dsh-client-ui-primitives 在浏览器里 require 不到。 浏览器端模块表里只有两类东西:平台种子词(react 一族)和声明了 dsh.client 的 Loader 条目。primitives 两个都不是 —— 它没有 dsh.client,在插件树里也没有对应条目(实测 201 条 Loader 条目里没有它);官方客户端插件是在构建期把它内联进自己 bundle 的。所以第三方插件要么自己上构建,要么自己画。本插件选择自己画,但严格照官方 Menu.module.css 的规格与主题变量来(见第二节)。
推论:dsh.client.inject 里写一个不存在的包名不会报错(只是解析不到),真正会炸的是运行时的 require —— 所以本插件只 require("react")。

六、配置

<DSH_HOME>/hard-delete.config.json(可选;env 优先)。载入一次常驻,改完重启 DSH 生效。

{
"apiPrefix": "/api/dsh-hard-delete",
"cleanMetadata": true,
"emitRemovedEvent": true,
"maxFiles": 20000,
"headerFrames": 32,
"headerScanBytes": 262144,
"text": {}
}

env 覆盖:DSH_HOME、HARD_DELETE_CONFIG、HARD_DELETE_API_PREFIX。

text 段可按 key 覆盖界面文案(前端内置 zh 字典兜底;默认下发空表 = 不覆盖)。
cleanMetadata:false = 只删会话目录,留元数据;emitRemovedEvent:false = 不发那一行消失事件。

七、测试

node _test/test-host.mjs # 39 项夹具断言,全在临时目录里跑,不碰真实会话
node _test/test-client.mjs # 11 项客户端离线断言(最小 hooks 运行时跑一遍渲染)
node _test/smoke-real-readonly.mjs # 对真实 ~/.dsh 只读冒烟(不做任何写操作)
node _test/verify-e2e-copy.mjs # 把真实 home 复制一份,走完整 HTTP 栈删一个真会话

夹具单测覆盖:多项目定位、版本号文件名、多帧 zstd 头部解码、两种 projcache 布局、空壳目录、路径穿越、dryRun、workspace.json 引用清理、删除后 404、 loopback / Host / 跨站 / 跨源 / 405 / 非法 JSON / 413 / confirm 校验 / api-session/removed 的发出与关闭开关。

客户端离线单测覆盖:factory 形态、只 require react、两个槽位的 id/order/locale、文案 key 全部有兜底、菜单项 DOM 与官方同构且点击后关菜单并写入请求、确认框三条警告齐全且默认焦点在「取消」、缺 slots API 时明确报错、 sessions 服务取不到时降级不炸。

verify-e2e-copy.mjs 是最后一道闸:真实会话目录里的多版本日志、真实 projcache 与 workspace.json,走完整链路删掉一个真会话后逐项核对,真实库全程只读。

八、明确不做的事(以及为什么)

- 不做回收站 / 回滚:本插件就是「删掉」这一个动作。要可恢复请用 dsh-session-recycle。
- 不做活动守卫:用户要的是「自由删除」,加守卫就回到了软删插件的语义。
取而代之的是确认框里把风险讲清楚(不可恢复 / 当前会话 / 可能仍在写盘)。
- 不改主包的会话列表状态:只发一个宿主事件,列表由客户端自己维护。
- 不碰宿主服务:只 inject: ['webServer'],不 import 会话/工作区服务。
- 不返回绝对路径:前端显示不了也不需要。

九、许可

AGPL-3.0-only(GNU Affero General Public License v3.0),全文见 LICENSE。

本项目管理在 AGPL-3.0 下开放:个人、学习、开源项目可自由使用与修改。
若需在 闭源 或 商业场景 中使用(不愿承接 AGPL 的传染条款),请联系获取商业授权 ——本仓暂无公开的联系邮箱,请通过本仓 Issue 联系。

本仓自 v0.1.0 起即为 AGPL-3.0,尚无许可变更历史。

本仓代码为独立实现:实现时观察了宿主公开的运行时约定(HTTP 路由注册方式 ctx.webServer.register、客户端打包形态 window.__ModuleLoader__.load、槽位注册 ctx.slots.inject/register、磁盘布局与日志文件名、转发事件 allowlist)与官方 Menu.module.css 的公开样式规格; 未复制任何第三方插件代码。命名上刻意避开 npm 上已被占用的 dsh-session-delete / dsh-session-recycle。

十、发布与收录

- 发布流程、版本号策略与推送步骤见 docs/RELEASING.md;推送脚本 scripts/push-publish.mjs(token 只从环境变量读,不落盘、不进 git 配置)。
- 远端只有 GitHub:插件市场按 GitHub 仓库索引,Gitee 镜像对本仓没有收益,故不做。
- 插件市场收录:dshbase 与 DSH 插件市场 按 GitHub 仓库的 dsh-plugin topic 自动索引(每 2 小时扫一次)。本仓的 topic: dsh-plugin · dsh · deepseek-harness · cordis-plugin。
- 市场按 package.json 的 version 判断更新;发版必须 bump version(见 CHANGELOG)。
预览分享卡片长什么样 ↓qwe4888789/dsh-hard-delete
分享 · DeepSeek Harness Hub
qwe4888789/dsh-hard-delete

在会话菜单一键彻底删除会话,不可恢复

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部