docs: rewrite the README around the plugin itself

Only the features, the behaviour boundaries, the three ways to install it, and a precise compatibility statement remain; the release pipeline, the file layout and the local-directory install method are gone. The install section now mirrors how a published DSH plugin is described: npm package plus custom registry, a pinned git URL, or a downloaded tgz.
This commit is contained in:
pyh
2026-09-30 11:46:17 +08:00
parent ec8aadedb7
commit 105e86edd5
+45 -75
View File
@@ -1,127 +1,97 @@
# dsh-session-delete # 会话彻底删除
DeepSeek Harness 插件:**彻底删除会话**,外加在 设置 → 通用设置 里**清理孤儿临时文件**。 DeepSeek Harness(DSH)插件:给会话加上**真正不可逆的删除**,并清理已删除会话遗留的临时文件。
DeepSeek Harness 原本只能归档会话,归档只是从列表里移开、日志仍然留在磁盘上;这个插件补齐了真正不可逆的删除。 DSH 原本只能归档会话——归档只是把会话从列表里移开,日志仍然留在磁盘上。这个插件提供两处删除入口,以及设置页里的一次性孤儿文件清理。
## 功能 ## 功能
### 1. 删除会话 ### 1. 删除会话
入口: 两个入口,行为一致,都会先弹出确认弹窗(复刻系统 Modal 的样式与键盘行为:Esc 关闭、Tab 焦点圈定、打开时焦点进入、关闭后焦点归位):
- 会话行 "..." 菜单 → **删除会话**(同一行的悬停快捷按钮也接管为删除,原"归档会话"按钮的位置) - 会话行右侧 **⋯** 菜单 → **删除会话**
- 两处入口都会弹出确认弹窗(复刻系统 Modal 的样式与键盘行为:Esc 关闭、Tab 焦点圈定、打开时焦点进入、关闭后焦点归位) - 会话行悬停时的 🗑 快捷按钮(占用原「归档会话」的位置,归档改从菜单进入)
删除时一并处理: 确认后一次性处理:
| 对象 | 处理方式 | | 对象 | 处理方式 |
| --- | --- | | --- | --- |
| 会话日志目录 | 整个目录删除,只保留 `session.lock`(POSIX 语义下该锁文件随会话存活,删除它没有意义且可能影响锁语义) | | 会话日志目录 | 整个删除,仅保留 `session.lock` |
| 子智能体会话 | 按 `parentSession` 递归收集,连同各自的日志与溢出文件一起删除 | | 子智能体会话 | 按 `parentSession` 递归收集,连同日志与溢出文件一并删除 |
| 工具输出溢出文件(spill) | 删除该会话(含子会话)对应的 spill 目录 | | 工具输出溢出文件(spill) | 删除该会话(含子会话)对应的 spill 目录 |
| 工作区登记项 | 解除与该会话的绑定(detach)、取消置顶、取消归档 | | 工作区登记项 | 解除绑定、取消置顶、取消归档 |
| 客户端列表 | 广播 `api-session/removed`,列表行立即消失 | | 客户端列表 | 广播 `api-session/removed`,列表行立即消失 |
有意**不**处理的部分: 有意**不**处理:
- **附件对象**(`$DSH_HOME/attachments/v1/objects`):按内容 sha256 寻址,同一份内容可能被多个会话共享,而 harness 里没有任何引用计数;跟着一个会话删除会破坏别的会话。要做只能实现"引用检查式清扫"(先扫剩余会话日志确认无人引用才删)。 - **附件对象**(`$DSH_HOME/attachments/v1/objects`):按内容 sha256 寻址,同一份内容可能被多个会话共享,而 DSH 没有引用计数,跟着一个会话删除会破坏别的会话;
- **投影缓存**(`$DSH_HOME/storages/session_projcache`):由日志派生的缓存,读取时带生命周期身份校验,永远不会串到别的会话;投影缓存服务本身也没有提供删除接口。 - **投影缓存**(`$DSH_HOME/storages/session_projcache`):由日志派生的缓存,读取时带生命周期身份校验,永远不会串到别的会话。
### 2. 清理临时文件(设置 → 通用设置) ### 2. 清理临时文件
只清理**孤儿**,判定规则偏保守: 设置 → 通用设置 → **清理临时文件**,清理 spill 后端留下的**孤儿**目录。判定偏保守:
1. 目录名必须精确等于 `session-<sha256(会话 id) 前 12 位十六进制>`——这是 spill 后端唯一会生成的形状; 1. 目录名必须精确等于 `session-<sha256(会话 id) 前 12 位>`——这是 spill 后端唯一会生成的形状;
2. 该名字不能属于任何已知会话(存量会话与内存中正在运行的会话都算); 2. 该名字不属于任何已知会话(存量会话与内存中正在运行的会话都算);
3. 只在 spill 的 root 内操作(后端当前 root + `%TEMP%\dsh-spill*`); 3. 只在 spill 的 root 内操作(后端当前 root + 系统临时目录下的 `dsh-spill*`);
4. 符号链接一律跳过不跟随,单项失败只记警告。 4. 符号链接一律跳过、不跟随;单项失败只记警告。
结果是幂等的,可以反复点击。 结果直接显示在该行(清理了几个目录、几个文件、释放多少空间),可反复点击。
## 行为与边界 ## 行为与边界
- **仍在运行的会话无法被插件从内存里移除**:它会先被归档并停止,日志与溢出文件在**下次启动 DeepSeek Harness 时**删除(界面会明确提示"已先停止并归档…");在此之前取消归档就会取消这次删除。 - **仍在运行的会话**无法被插件从内存里移除:它会先被归档并停止,日志与溢出文件在**下次启动 DSH 时**删除(界面会明确提示);在此之前取消归档就会取消这次删除。
- 删除**不可恢复**,但**不是安全擦除**:按普通文件删除处理,底层介质上可能有残留。 - 删除**不可恢复**,但**不是安全擦除**:按普通文件删除处理,底层介质上可能有残留。
- 工作区里的代码文件不受影响。 - 工作区里的代码文件不受影响。
- 溢出文件被删除后,如果某个 fork 的日志里仍保留着指向它的路径文本,那些路径会失效——这与"父会话数据被删除"是同一件事。 - 溢出文件被删除后,fork 出来的会话日志里若仍保留指向它的路径文本,那些路径会失效。
## 安装 ## 安装
这是一个 DeepSeek Harness **bundle** 包(普通 Node 包 + `cordis.patch.yml`),由 Harness 的插件管理机制安装,而不是单独运行的程序: 在 DSH 的 **插件 → 添加插件** 里三选一。
- `package.json` 声明 `dsh.bundle.patch`(bundle 补丁)与 `dsh.client`(web 平台客户端半) ### 1. npm 包(推荐)
- `cordis.patch.yml` 把 `@dsh-plugin/session-delete` 插进 profile
- 安装后 profile 的依赖是 `link:<本目录绝对路径>`(本地目录安装)或注册表/git 解析出的版本,并在 `node_modules` 下建立链接
> 包名在三个地方必须完全一致:`package.json` 的 `name`、`cordis.patch.yml` 里 `name:`、以及 `client.js` 顶部 `__ModuleLoader__.load({ id })`。Cordis 是按 `name` 在 profile 的 `node_modules` 里解析模块的。 安装源选 **自定义地址**,填本插件的注册表:
### 安装方式 ```text
https://gitea.iwake.top/api/packages/dsh-plugin/npm/
```
在 DeepSeek Harness 的 **插件 → 添加插件** 里,以下三种都可用: 包名填:
| 方式 | 输入 | ```text
| --- | --- | @dsh-plugin/session-delete
| 本地目录 | 本仓库在本机的绝对路径(`D:\DeepSeek Harness Plugins\dsh-session-delete`),适合边改边用 | ```
| Git 仓库地址 | `https://gitea.iwake.top/dsh-plugin/session-delete.git#v1.0.0`(`#` 后可跟标签或提交) |
| npm 源 | 安装源选「自定义地址」填 `https://gitea.iwake.top/api/packages/dsh-plugin/npm/`,包名填 `@dsh-plugin/session-delete` |
用 npm 源或私有源时,凭据放在本机 `~/.npmrc`(客户端提示里也是这么说的): 包是公开的,不需要凭据。若你的环境需要认证,把令牌写进本机 `~/.npmrc`:
```bash ```bash
npm config set @dsh-plugin:registry=https://gitea.iwake.top/api/packages/dsh-plugin/npm/ npm config set @dsh-plugin:registry=https://gitea.iwake.top/api/packages/dsh-plugin/npm/
npm config set //gitea.iwake.top/api/packages/dsh-plugin/npm/:_authToken=<带 package 权限的 token> npm config set //gitea.iwake.top/api/packages/dsh-plugin/npm/:_authToken=<token>
``` ```
公开的 git 地址不需要凭据。安装或升级后:**宿主半需要重启客户端**,只改浏览器半时刷新页面即可。 ### 2. Git 仓库地址
### 发布到 Gitea 的 npm 仓库 ```text
https://gitea.iwake.top/dsh-plugin/session-delete.git#v1.0.0
仓库自带工作流 [`.gitea/workflows/publish.yml`](.gitea/workflows/publish.yml):推送 `v*` 标签(或在 Actions 页面手动 Run workflow)就会把该版本发布到 `https://gitea.iwake.top/api/packages/dsh-plugin/npm/`。工作流会校验标签与 `package.json` 的版本一致,不一致直接失败。
```bash
git tag -a v1.0.1 -m "v1.0.1" && git push origin v1.0.1
``` ```
发布凭据按顺序取:仓库 secret `NPM_TOKEN`(带 `package` 写权限的个人访问令牌)→ 没有则退回 Gitea 内置任务令牌 `${{ secrets.GITEA_TOKEN }}`(工作流已声明 `permissions: packages: write`)。 `#` 后面可以跟标签或提交,用来锁定版本。仓库是公开的,不需要凭据。
前提与排查: ### 3. 本地 tgz
- 实例启用了 Actions 并注册了 runner(`runs-on: ubuntu-latest` 要与 runner 的标签一致); 把 [session-delete-1.0.0.tgz](https://gitea.iwake.top/api/packages/dsh-plugin/npm/@dsh-plugin%2Fsession-delete/-/1.0.0/session-delete-1.0.0.tgz) 下载到运行 DSH 的机器上,在安装界面里填它的绝对路径。
- 工作流**不使用任何 `uses:` 外部 action**:检出用 `git fetch`,Node/npm 用 runner 镜像自带的那份。这样 runner 不必访问 github.com(国内自建 runner 拉 `actions/checkout` 常超时,症状是所有步骤 `cancelled`,日志里是 `dial tcp …:443: i/o timeout`)。若你的镜像里没有 node,请换成带 node 的 runner 镜像;
- 若组织把 Actions 令牌上限设成只读,或任务令牌对包命名空间没有写权限,发布步骤会很快失败:配置 `NPM_TOKEN` 即可,工作流会自动改用它;
- 已发布的版本不会让工作流失败:注册表对同版本返回 409(`package version already exists`),npm 客户端也会用 `cannot publish over the previously published versions` 拦下,两种都记为“已存在,本次跳过”,所以手动重跑同一个版本是安全的;
- 真失败时看 `Publish` 步骤日志:`registry =` 那行确认注册表地址(必须是 `…/api/packages/dsh-plugin/npm/`,否则 npm 会拿 registry.npmjs.org 的判断结果说“版本已存在”),`npm error code` 给出原因(`E401/E403` = 令牌无效或权限不足)。注意 Gitea 的 npm 注册表**不实现** `/-/whoami`,`npm whoami` 返回 404 属正常现象;`npm config get ...:_authToken` 也必然报 protected,因为 npm 禁止读回令牌。
手工发布等价于: 安装或升级后:**宿主半变更需要重启 DSH**(完全退出再打开,仅关闭窗口不一定退出进程);只改浏览器半时刷新页面即可。
```bash
npm config set @dsh-plugin:registry=https://gitea.iwake.top/api/packages/dsh-plugin/npm/
npm config set //gitea.iwake.top/api/packages/dsh-plugin/npm/:_authToken=<TOKEN>
npm publish # registry 已写在 package.json 的 publishConfig 里,无需再传 --registry
```
同一版本不能重复发布:改内容要同时提升 `package.json` 的 `version`。
## 结构
```
index.js 宿主半:删除流程、溢出文件清理、两条已认证路由、启动补删
client.js 浏览器半:菜单行、行悬停按钮 + 气泡、确认弹窗、设置行
cordis.patch.yml bundle 补丁
locale/*.json 插件管理页显示的标题与描述
icon.svg 插件图标
```
## 兼容性 ## 兼容性
为 DeepSeek Harness **0.2.0-rc.2** 编写。 基于 **DeepSeek Harness 0.2.0-rc.2** 编写。DSH 仍在演进,其它版本与平台组合未逐一验证。
浏览器半**不 import 任何 Harness 客户端包**(上游规范要求如此:这些包随时可能变化,且渲染抛错会让整个插槽条目变空白),而是复刻 `dsh-client-ui-primitives` 的 markup、CSS 声明、图标 path 数据与键盘行为,只保留 `--dsw-*` 主题 token 引用。如果上游改动这些内部结构,界面细节可能失配(例如不再弹出气泡),但不会导致插件加载失败或崩溃。 - 浏览器半不 import 任何 Harness 客户端包(上游规范如此要求:这些包随时可能变化,渲染抛错会让整个插槽条目变空白),而是复刻 `dsh-client-ui-primitives` 的 markup、CSS 声明、图标 path 数据与键盘行为,只保留 `--dsw-*` 主题 token 引用。上游若改动这些内部结构,界面细节可能失配(例如悬停气泡不再出现),但不会导致插件加载失败。
- 宿主半只依赖稳定的服务契约:`sessionPersistence`、`sessionQuery`、`workspaceRegistry`、`sessions`、`agents`、`connection.fetch`,以及 spill 后端的目录规则。
删除逻辑只依赖稳定的服务契约:`sessionPersistence`、`sessionQuery`、`workspaceRegistry`、`sessions`、`agents`、`connection.fetch`,以及 spill 后端的目录规则(`<root>/session-<sha256(id)[:12]>/…`)。
## License ## License
MIT [MIT](LICENSE)