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

会话删除器 ghgjkbf/dsh-session-deleter

Cordis
ghgjkbf27 天前有提交已提供中文文档扫描:含敏感能力

从网页界面删除会话与消息,可先入回收站再永久清除

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么[目录]—
dsh 原生插件 · market
暂无时间戳·判据 →
装得上吗[实装验证]✓
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
数据截至 2026/11/04·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/04·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 27 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

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

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 39 处,展示前 37 条)
  • 读取本地文件
    lib/index.js:56
    return JSON.parse(readFileSync(manifestPath, 'utf8')).version ?? 'unknown';
  • 读取本地文件
    lib/index.js:169
    bytes += await stat(generation.path).then((s) => s.size).catch(() => 0);
  • 读取本地文件
    lib/index.js:311
    const buffer = await readFile(resolved.artifact);
  • 发起网络请求
    lib/client.js:289
    response = await fetch(url, {
  • 读取本地文件
    lib/trash.js:43
    raw = await readFile(manifestPath(trashRoot), 'utf8');
  • 写入本地文件
    lib/trash.js:65
    await mkdir(trashRoot, { recursive: true });
  • 写入本地文件
    lib/trash.js:68
    await writeFile(staging, `${JSON.stringify({ version: 1, entries }, null, 2)}\n`, 'utf8');
  • 写入本地文件
    lib/trash.js:69
    await rename(staging, path);
  • 写入本地文件
    lib/trash.js:85
    await mkdir(trashRoot, { recursive: true });
  • 写入本地文件
    lib/trash.js:91
    await rename(dir, held);
  • 写入本地文件
    lib/trash.js:123
    await mkdir(join(entry.from, '..'), { recursive: true });
  • 读取本地文件
    lib/trash.js:150
    await stat(path);
  • 删除本地文件递归
    lib/trash.js:167
    await rm(entry.held, { recursive: true, force: true });
  • 读取本地文件
    lib/trash.js:184
    present = await readdir(trashRoot);
  • 读取本地文件
    lib/sessions.js:33
    total += (await stat(generation.path)).size;
  • 读取本地文件
    lib/sessions.js:131
    projectNames = await readdir(root, { withFileTypes: true });
  • 读取本地文件
    lib/sessions.js:144
    sessionNames = await readdir(projectPath, { withFileTypes: true });
  • 读取本地文件
    lib/sessions.js:195
    modifiedAt: await stat(dir).then((s) => s.mtimeMs).catch(() => null),
  • 读取本地文件
    lib/sessions.js:241
    entries = await readdir(trashRoot, { withFileTypes: true });
  • 读取本地文件
    lib/sessions.js:252
    children = await readdir(current, { withFileTypes: true });
  • 读取本地文件
    lib/atomic.js:88
    previous = await readFile(path);
  • 写入本地文件
    lib/atomic.js:110
    await rename(staging, path);
  • 删除本地文件
    lib/atomic.js:112
    await rm(staging, { force: true }).catch(() => {});
  • 写入本地文件
    lib/atomic.js:129
    await rename(target, destination);
  • 读取本地文件
    lib/atomic.js:137
    return (await stat(path)).isDirectory();
  • 读取本地文件
    lib/atomic.js:146
    return (await stat(path)).isFile();
  • 写入本地文件
    lib/ledger.js:27
    await mkdir(dirname(path), { recursive: true });
  • 写入本地文件
    lib/ledger.js:29
    await appendFile(path, line, 'utf8');
  • 读取本地文件
    lib/ledger.js:42
    raw = await readFile(path, 'utf8');
  • 读取本地文件
    lib/paths.js:107
    names = await readdir(dir);
  • 读取本地文件
    lib/title.js:152
    const buffer = await readFile(path);
  • 读取本地文件
    lib/title.js:175
    const buffer = await readFile(path);
  • 读取本地文件
    lib/shadow.js:42
    const buffer = await readFile(path);
  • 读取本地文件
    lib/shadow.js:265
    const raw = await readFile(path, 'utf8');
  • 读取本地文件
    lib/shadow.js:296
    const buffer = await readFile(path);
  • 读取本地文件
    lib/shadow.js:488
    const backup = await readFile(backupPath);
  • 读取本地文件
    lib/shadow.js:489
    const current = await readFile(path);
扫描于 2026/11/4(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

功能亮点

  • 删除会话与单条消息
  • 默认移入回收站
  • 支持影子删除与截断还原

在 Web UI 里删除已存的 DSH 会话和单条消息:默认进回收站;单条消息可影子删除(A 线)或截断(B 线),都支持还原。

README

由 DeepSeek 最新模型翻译生成
dsh-session-deleter 删除 harness 没有提供 API 去删的东西:从 DSH web UI 里删掉整个已存储的会话,以及会话的内容。删除其实是移入私有回收站;只有再单独确认一次永久删除,字节才会真正离开磁盘。 版本历史:CHANGELOG.md。 为什么会有这个插件 @deepseek-ai/dsh-session-persistence-jsonl 只提供 create/open/flush/stat/list,别的什么都没有,所以 UI 能归档会话,却永远删不掉。侧边栏会一直留着那一行,直到下次 Workspace 写入时把 id 清掉;subagent/catalog 是只追加的,所以子项没法靠追加来删除;客户端也没有任何服务暴露删除操作。这个插件补上了缺失的操作,而且不动 harness。 它能做什么 三个操作,都能从 UI 里触达: - 删除整个会话 —— 把会话的整个目录(每一代日志)移进回收站。可逆。 - 恢复 —— 把暂存的目录移回原路径。如果原路径已被占用,它会拒绝覆盖,而不是瞎猜。 - 永久删除 —— 唯一不可逆的一步,也是唯一能回收磁盘空间的一步。需要再明确点一次(或传 confirm: true)。
展开完整文档(README 剩余 1 节)
dsh-session-deleter

删除 harness 没有提供 API 去删的东西:从 DSH web UI 里删掉整个已存储的会话,以及会话的内容。删除其实是移入私有回收站;只有再单独确认一次永久删除,字节才会真正离开磁盘。

版本历史:CHANGELOG.md。

为什么会有这个插件

@deepseek-ai/dsh-session-persistence-jsonl 只提供 create/open/flush/stat/list,别的什么都没有,所以 UI 能归档会话,却永远删不掉。侧边栏会一直留着那一行,直到下次 Workspace 写入时把 id 清掉;subagent/catalog 是只追加的,所以子项没法靠追加来删除;客户端也没有任何服务暴露删除操作。这个插件补上了缺失的操作,而且不动 harness。

它能做什么

三个操作,都能从 UI 里触达:

- 删除整个会话 —— 把会话的整个目录(每一代日志)移进回收站。可逆。
- 恢复 —— 把暂存的目录移回原路径。如果原路径已被占用,它会拒绝覆盖,而不是瞎猜。
- 永久删除 —— 唯一不可逆的一步,也是唯一能回收磁盘空间的一步。需要再明确点一次(或传 confirm: true)。

它在 UI 里出现在哪

| 位置 | 插槽 | 位置 |
| 侧边栏底部,Settings 旁边 | sidebar.footer.action | order: 20 —— 始终可见,不用悬停 |
| 会话选择对话框 | shell.overlay | 由底部入口打开 |
| 会话的「...」菜单 | sidebar.workspaces.session.menu.item | order: 500,排在自带的 pin/rename/fork/archive 之后 |
| 确认对话框 | shell.overlay | 全框架浮动层 |
| 管理页 | settings.section | Settings 里 order: 50 |
| 管理页(同一主体) | shell.overlay | order: 5 —— 插件内的管理浮层,见下文 |

侧边栏底部那个入口是主入口,不依赖悬停:DSH 只在指针悬停在非当前会话行上时才画出「...」触发按钮,所以从不悬停的用户根本看不到这个插件存在。它打开的选择器会自己列出所有可删除的会话,正在运行或当前的会话会显示出来,但不可删除。

管理页由同一个组件渲染在两处。shell 拥有 Settings 面板,却没有暴露任何客户端服务去打开它:layout.selectPanel(id) 只能寻址注册在 sidebar.panellist 插槽里的主面板(自带的是 plugins 和 dsh-market),选中未注册的 key 会抛错。所以插件内那些承诺「管理」的入口——选择器的「管理回收站」和对话框的「打开管理页」——打开的是管理浮层,渲染的是同一个主体。仍会先尽力尝试一次 selectPanel("settings"),纯粹是照顾某些该 key 确实存在的构建。

两个对话框都先做试运行:计划里会显示目录、大小、日志代际,还有什么会受影响,以及会留下什么残留——在动手之前先看清楚。

设计承诺

默认可逆。 首次请求绝不删除字节。把 rename 移入回收站,在同一个文件系统上是原子操作,这就是全部破坏性工作。

后端之后读不到的东西,一律不做。 重写时,每个持久批次重新输出一个带校验和的 Zstandard 帧。.jsonl.zstd 产物是多个可独立解码帧的拼接,不是单一流——所以重新压缩成单一流虽然能解码出正确字节,却会在下次追加时无法通过后端的结构扫描器。

感知多代际。 会话目录合法地可能存放 v3 日志、v4 日志,或两者兼有。这里没有任何地方硬编码文件名。

做不到安全就不做。 运行中的会话日志处于打开写入状态,所以拒绝删除,而不是在写入者手下删掉。发起请求的会话不能删除自己。会话 id 是路径片段,所以形似路径穿越的 id 在到达文件系统之前就被拒绝。

宿主路由

在 harness web 服务器的 /session-deleter 下提供:

| 方法 | 路径 | 用途 |
| GET | /health | 生效的根目录与当前会话 id |
| GET | /inventory | 每个已存储会话,含字节数、代际、标题 |
| GET | /plan?sessionId=… | 试运行:后果、残留、阻塞项 |
| POST | /delete | {sessionId, force?} → 移入回收站 |
| GET | /trash | 保留的条目、孤儿检测、大小 |
| POST | /restore | {id} → 移回 |
| POST | /purge | {id, confirm: true} → 删除字节 |
| GET | /ledger?limit= | 仅追加的操作日志 |
| GET | /messages?sessionId=… | 展示消息及其 live/shadowed 状态 |
| POST | /messages/shadow | {sessionId, seq} → 为一条消息打墓碑(A 线) |
| POST | /messages/unshadow | {sessionId, tombstoneSeq} → 撤销一次遮蔽 |
| POST | /messages/truncate | {sessionId, throughSeq} → 丢弃尾部(B 线) |
| POST | /messages/untruncate | {sessionId, backupPath} → 恢复备份 |

遮蔽与截断:各自到底做什么

- 遮蔽是仅追加的,完全可逆。一条 developer/message 墓碑在模型可见折叠中替换一个 surface 节点——与已发布的压缩流程用的是同一机制。转录仍会显示原始行(依据 surface 自身文档化的约定),而墓碑推导不出任何 LLM 消息。从模型上下文中删除消息,就是它做的事;从屏幕上删除消息,则需要截断。
- 截断通过从存活的帧对齐前缀重建产物,从而移除事件。它会一并带走转录行,并保留 .bak- 备份以便字节级精确恢复。这是唯一会触碰现有字节的消息操作。

配置

可选,作为 bundle 行的 config 提供:

- insert:
- id: session-deleteryaml
name: 'dsh-session-deleter'
config:
sessionRoot: 'D:/somewhere/sessions'
trashRoot: 'D:/somewhere/trash'
protectCurrentSession: true
refuseLiveSessions: true

默认值:sessionRoot = <DSH_HOME|~/.dsh>/sessions,trashRoot = <home>/session-trash/sessions。

磁盘上的状态

- <home>/session-trash/sessions/ —— 暂存的目录,每次删除一个,外加 manifest.json。
- <home>/session-trash/ledger.jsonl —— 每个操作阶段追加一行。末尾被截断的行(追加过程中崩溃)在读取时直接丢弃,不当成损坏处理。

验证

每项检查都针对真实会话树的沙箱副本跑真实的文件系统和 HTTP 操作——不 mock 插件自身的逻辑。

node tools/verify-host.mjs "$DSH_HOME/sessions"
node tools/verify-http.mjs
node tools/verify-client.mjs
node tools/check-locale.mjs # static: t() keys vs both dictionaries
node tools/verify-frames.mjs <log> [<log> …]
node tools/verify-manage.mjs # needs a browser + the gate cookie, see below
node tools/verify-ui.mjs # same
node tools/verify-footer.mjs # same

verify-host 覆盖清单与标题解析、完整的暂存 → 恢复 → 清除流程、拒绝路径(路径被占用、条目已清除、条目未知)、原子替换备份、账本持久性、逐字节精确的帧往返,以及遍历形状的 id 拒绝。verify-http 通过一个 webServer 桩挂载插件,该桩与随附的契约一致,并用真实 HTTP 驱动全部八条路由。verify-client 在 VM 中针对桩模块加载器执行浏览器 bundle,断言每个 slot 注册。check-locale 证明每个
t("…") 调用在两本字典中都能解析(缺失的键会渲染成字面的键文本),并且没有键
被重复项遮蔽。verify-manage、verify-ui 和 verify-footer 在 Chrome 中驱动真实 GUI,证明这些界面确实渲染出来,而不只是注册了:verify-manage 从侧边栏底部打开选择器,点击「管理回收站」,并断言管理视图渲染出的计数与路由返回的一致——空面板即使挂载了也无法通过。

不提交任何与机器相关的东西。tools/harness.mjs 在运行时发现浏览器和 puppeteer-core,所以这些测试套件在另一台机器上也能跑;每个值都有覆盖项:

| 变量 | 用途 |
| DSH_URL | 被测 GUI 源(默认 http://127.0.0.1:3080) |
| DSH_COOKIE_NAME / DSH_COOKIE_VALUE | GUI 门禁 cookie;覆盖浏览器套件中的内存铸造 |
| DSH_HOME | Harness home,cookie 从其中的凭据文件铸造 |
| DSH_CHROME | Chrome/Chromium 二进制文件 |
| DSH_PUPPETEER_CORE | puppeteer-core 入口模块 |
| DSH_SHOTS_DIR | 截图存放位置 |
| DSH_SESSIONS_DIR | verify-http 复制其 fixture 会话的源树 |
| DSH_LAUNCHER / DSH_PLUGIN_DIR | 供 tools/restart-and-verify.ps1 使用 |
网关 cookie 是持有者凭证,绝不会写入这个仓库。
当 DSH_COOKIE_NAME/DSH_COOKIE_VALUE 缺失时,tools/harness.mjs 里的 cookieForBaseUrl() 会从这个 Harness home 自己的 .credentials.yaml(client-connection/browser-session)在内存中生成一个,签名方式与 dsh-client-connection 完全一致——载荷 {version, authority, issuedAt, expiresAt} 以毫秒为单位,用解码后的 32 字节密钥对 base64url 主体做 HMAC-SHA256,名字为 dsh-auth-<base64url sha256(authority)>。不会打印任何内容,也不会持久化。环境变量只要设置了就仍然优先,所以对远程主机运行时不需要本地凭证文件。

卸载

删掉 bundle 行和包。保留的目录留在回收站里,可以手动恢复——manifest 记录了每个条目的原始路径。
预览分享卡片长什么样 ↓ghgjkbf/dsh-session-deleter
分享 · DeepSeek Harness Hub
ghgjkbf/dsh-session-deleter

从网页界面删除会话与消息,可先入回收站再永久清除

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