# dsh-trash — DSH 回收站插件 [English](README.md) | **中文** 一个 [DeepSeek Harness](https://github.com/deepseek-ai/dsh) 插件:在最高权限(`danger-full-access`)下,**所有删除操作一律不再永久删除**,而是先进入回收站(trash store)。误删可以随时恢复。 - 拦截 `pwsh` / `bash` / `run_code` 中的删除指令(`Remove-Item`、`rm`、`del`、`rd`、`rmdir`、`unlink`、`[System.IO.*]::Delete`、`fs.unlink/rm/rmdir`、`os.remove`、`shutil.rmtree` …),拒绝执行并把模型引导到回收站工具。 - 提供 4 个工具:`trash`(删除=移入回收站)、`trash-list`(查看)、`trash-restore`(恢复)、`trash-purge`(确认后彻底清空,唯一真正删除)。 - 零运行时依赖(只用 node 内置模块),任意方式安装(registry / `file:` / `link:` / GitHub)都无需额外依赖解析。 ## 工作原理 | 层 | 说明 | |---|---| | **拦截层** | 挂在工具流水线 `tools/pre-execute` 瀑布(`prepend` 最先执行),扫描 `pwsh.command` / `bash.command` / `run_code.code` 中的删除模式。命中即返回 `{ kind: "deny" }`——deny 是单调的,后续任何阶段(审批、guard、dispatch)都无法放行。模型会收到可操作的引导信息,改用 `trash`。 | | **回收站存储** | 每次"删除"在回收站根目录(默认 `$DSH_HOME/trash`)下生成一个条目:`data/` 保留原始文件名,`meta.json` 记录原始路径、删除时间、大小、可选原因。跨卷移动自动退化为 复制+清理。 | | **工具** | `trash` / `trash-list` / `trash-restore` / `trash-purge`,见下方。 | ``` / / data/... 被“删除”的内容(保留原始文件名) meta.json 原始路径、删除时间、大小、原因 ``` ## 安装 ```powershell # 本地源码(开发/link 模式,改源码即时生效) dsh plugin --profile web add link:D:/path/to/dsh-trash # 或直接从 GitHub 安装(本机需有 git 与 GitHub 凭据) dsh plugin --profile web add github:LeonSone/dsh-trash ``` `dsh plugin add` 会把包加入 profile 的 `dependencies`;因为包声明了 `dsh.bundle`,会自动追加到 `dsh.profile.bundles`。重启 `dsh web`(或无头 runner)后插件生效。 > 无头(headless)profile 同理:`dsh plugin --profile headless add `。 ## 使用 装好后,模型的行为自动变为: | 场景 | 行为 | |---|---| | 模型执行 `Remove-Item` / `rm` / `del` 等 | **被拦截**,返回说明回收站工作方式的提示 | | 模型调用 `trash(paths=[...])` | 文件/目录移入回收站(可恢复) | | 误删了 | `trash-restore(entry_id=...)` 恢复到原始路径;或 `trash-restore(path="原路径片段")` 按路径找回最新匹配条目 | | 彻底清空 | `trash-purge(confirm: true)`(可选指定 `entry_ids`) | ### 工具参考 | 工具 | 用途 | |---|---| | `trash(paths, reason?)` | 把文件/目录移入回收站,返回 `entry_id`、`trash_root`、`moved`。 | | `trash-list(path?)` | 列出回收站条目(删除时间、大小、原因、原始路径),可按原始路径子串过滤。 | | `trash-restore(entry_id? 或 path?, target_path?, overwrite?)` | 恢复条目到原始位置(单条目条目可用 `target_path` 指定新位置)。目标已存在时默认拒绝;传 `overwrite: true` 会先把现有内容**自动移入回收站**再恢复——任何一步都不会丢数据。 | | `trash-purge(entry_ids?, confirm: true)` | 彻底删除回收站条目。整个插件中唯一真正的删除。 | ## 配置(可选) 在 profile 的 `cordis.patch.yml` 中追加: ```yaml - id: dsh-trash config: location: D:/data/dsh-trash # 回收站根目录(建议与工作区同盘,移动为瞬时重命名) interceptPwsh: true # 是否拦截 pwsh 删除(默认 true) interceptBash: true # 是否拦截 bash 删除(默认 true) interceptRunCode: true # 是否拦截 run_code 中的 fs.* 删除(默认 true) ``` ## 安全设计 - **fail-safe 方向**:检测器宁可误拦(如教学文本里出现 `Remove-Item` 字样),也不放走真正的删除;误拦时提示会说明原因并给出替代方案。短别名(`rm`/`del`/`rd`/`ri`)只在**命令位置**命中,因此 `cat /tmp/rm`、`Test-Path C:\rd`、`grep 'rm -rf' docs` 这类"读/搜"不会被误拦。 - **真正的删除只有一条路**:`trash-purge(confirm: true)`,且只作用于回收站内部。 - **恢复安全**:`entry_id` 严格校验(不允许路径穿越);`target_path` 禁止指向回收站内部。 - **覆盖安全**:恢复覆盖已有文件时,现有内容会先自动进入回收站——每一步都可逆。 ## 开发与测试 ```powershell node test/unit.test.mjs # 48 个用例:检测器命中/不命中矩阵 + 回收站存储全流程 ``` ## 卸载 ```powershell dsh plugin --profile web remove dsh-trash ``` (回收站目录本身不会被删除;如需保留数据请先手动移走。) ## License [MIT](LICENSE)