# 安全升级与卸载 English: [Upgrade and uninstall](upgrading.md). ## 二进制位置与更新清理 本候选随主包携带完整 Niubash 1.5.0;发布前 `@latest` 仍指已发布版本。下列位置适用于市场、`npx dsh-ops install --profile ...`、官方 `plugin ... add`、本地 tarball,以及直接 npm/pnpm 安装和更新: | 归属 | 二进制位置 | 更新与清理 | | --- | --- | --- | | 已安装主包 | `<实际 dsh-ops 包目录>/runtime/-/dsh-ops-worker[.exe]`;Windows Niubash 为 `runtime/win32-x64/niubash/niu.exe` 及完整同级工具树 | 更新同一个 profile/包,不按版本另建目录,不手工复制二进制到包外。由包管理器替换包;pnpm 的实际文件可能位于 store。 | | 显式 provision | `/dsh-ops/shells/niubash/niu.exe` | 固定单目录:新副本先校验,替换失败回滚,成功替换后删除 previous 树;正常成功更新不叠加版本目录。通常随包载荷无需再 provision 一份。 | | 保留的 provision 下载 | `<系统临时目录>/dsh-ops--` 及其在 `/dsh-ops/pending/` 的记录 | 失败时保留下载字节(摘要不符只有在拿到文件时才可诊断),并写入持久记录。下一次 `provision-shells` 按数量/字节/年龄预算回收已登记条目;其余由 `cache status` 报告、`cache reclaim --yes --all-hosts-stopped` 回收,绝不只按名字或时间删除。 | | 每调用执行副本 | `<系统临时目录>/dsh-ops-niu-exec-*`,设置 `DSH_OPS_EXEC_ROOT` 时位于其下,Windows 上载荷与临时目录不同卷时改为 `<载荷所在卷>\dsh-ops-exec` | 不可变载荷的硬链接克隆加 6 个全新的可写状态目录:不复制任何载荷数据字节,命令运行期间封印为只读,完成/取消后先解封再删除。spawn 前校验只做 stat;设置 `NIUBASH_VERIFY=deep` 可恢复全量重哈希。同一宿主进程内的并发调用会延迟到该载荷最后一棵树释放才解封删除;不同宿主进程之间不协调(一个宿主解封期间,另一个宿主正在运行的克隆会短暂失去只读封印)。跨卷执行根、ReFS 或 ACL/权限拒绝导致的复制回退会写入收据(`cloneFallbackCode` / `cloneDiagnostics`)。在 Windows 上,未设置 `DSH_OPS_EXEC_ROOT` 且载荷与临时目录不同卷时,执行根改到 `<载荷所在卷>\dsh-ops-exec`,使默认配置仍走硬链接而非复制;该目录必须是当前账户拥有的真实目录——被其他账户预先创建、是重解析点,或与载荷路径重叠时会被拒绝,调用回退到临时目录并报告原因。显式设置的 `DSH_OPS_EXEC_ROOT` 走同样的校验,但会直接报错,不会被静默替换。POSIX 路径一律归约为同一个卷根,因此 tmpfs 上的 `/tmp` 仍需显式设置 `DSH_OPS_EXEC_ROOT` 才能避免复制回退。清理失败会报告,崩溃可能遗留这份小型克隆。停止全部 OPS 宿主/任务后,只核查这些确属本任务的残留,不自动扫描删除任意临时目录。 `cache reclaim --yes --all-hosts-stopped` 只回收执行 provenance 校验通过的副本;不会按名字模式删除。 | | 旧托管 Git Bash / IPC | `/dsh-ops/shells/bash/`;`/dsh-ops/bash-ipc/` | npm 更新不静默删旧状态。先看 `bash-runtime status`;`bash-runtime remove --dry-run` 预览、`remove --yes` 确认移除不再需要的全部托管副本。该命令也会移除托管 Niubash,并非只删 Git Bash。IPC 用 `bash-runtime clean-cache --yes --all-hosts-stopped`,须先停止全部宿主/任务;该离线命令还会回收最新两份之外被隔离的副本,在线 sweep 则保留它们,`cache status` 会报告。安装成功不会删除未能验证的 staging 兄弟目录。 | | 用户自选 shell / 共享缓存 | 显式 `bashPath`、系统安装、npm/pnpm store、npm `_npx` 缓存 | 不属 OPS 自动清理范围。`npx` 可能缓存额外完整包,不要每次工具调用都执行 npx。包管理器缓存保留/修剪与插件卸载是两回事。 | 直接 npm 安装位于所选项目的 `node_modules/dsh-ops`;全局安装用 `npm root -g` 定位。DSH 通常从 `/profiles//node_modules/dsh-ops` 解析,按实际解析位置查,不猜 store 路径。不同 profile 可有各自的包副本。`bash-runtime status` 可查看当前执行器和托管目录。 **没有 npm 更新/卸载时自动清扫的钩子**:这无法安全判断在用任务、共享 store 与用户自选 shell,清单门禁也禁止安装期钩子。已有固定目录替换与每调用清理避免正常托管更新持续叠加;旧状态仍需按 receipt 和明确确认清理。未来跨版本自动清理必须先有归属及活动进程证据,不能只按目录名猜测。不要为回收一个 shell 直接运行宽范围的旧 `uninstall --yes`,它可能移除其它显式 provision 的 OPS 状态。 OPS 可能创建的每一类资源(归属、类别、字节数、文件数、年龄以及清理被阻塞的原因)可用只读的 `dsh-ops disk status` 查看;它报告的内容不会被删除,见[磁盘与缓存](磁盘与缓存.md)。 ## 替换依赖包前先停止受影响的 profile Windows 上运行中的 Bash、旧版随包 PowerShell 或遗留 FastCtx 进程可能占用 profile 依赖树中的文件。`ERR_PNPM_EPERM` / `importPackage` 是文件占用问题, 即使这次重建依赖树是由另一个插件触发(#175)。包管理操作失败后,清单、 启用的 bundle 列表和磁盘文件可能不一致。 1. 保存工作,通过宿主任务控制停止受影响的任务,并等待进程结束。不要在该 profile 的 OPS shell 内执行升级。 2. 完全退出受影响的 DSH 应用,包括托盘进程。使用外部终端,并保持原来的 `DSH_HOME` 和 profile。 3. 备份 profile 中已有的 `package.json`、`pnpm-lock.yaml`、`pnpm-workspace.yaml` 和 `cordis.patch.yml`。使用该应用自己的官方 CLI;Desktop 要用随附入口, 不能换成无关的全局 `dsh`。 4. 操作成功后再启动 DSH,核对目标 profile 的安装版本和启用组件。 标准 Windows Desktop 安装可按下列方式选择 CLI;自定义安装应修改路径。 从两种操作中选择一种: ```powershell $desktopCli = Join-Path $env:LOCALAPPDATA 'Programs/DeepSeek Harness/resources/runtime/cli/bin/dsh.cmd' # 更新到已审阅的版本: & $desktopCli plugin --profile desktop add 'dsh-ops@' --registry=https://registry.npmjs.org # 或卸载插件: & $desktopCli plugin --profile desktop remove dsh-ops --registry=https://registry.npmjs.org ``` 将 `` 替换为选定版本。`web` 和 `dsh-tui` 使用各自应用的 CLI/profile; 保留原来设置的自定义 `DSH_HOME`。若失败操作留下不一致的依赖树,保留日志, 在宿主停止时用同一个官方 CLI 的 `plugin --profile desktop install` 按已保存 清单修复,并显式传入已核验的 registry。这是恢复预期依赖,不表示宿主已经 完整回滚所有文件。不要删除锁文件,也不要按进程名批量强杀。旧 FastCtx 宿主 可能需要完成正常空闲退出后才会释放文件。 ## 卸载也可能被 registry 策略检查拒绝 `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION` 配合 `version not present in registry manifest` 与文件占用不同(#335)。pnpm 在卸载时也可能验证现有锁文件条目。 镜像缺少固定 Bash 版本时,即使载荷已经安装,也无法提供策略所需的版本元数据; 重启宿主不能修复镜像。 安装、修复和卸载都使用同一个可信且版本元数据完整的 registry。上面的示例将 registry 显式传给官方 CLI 转发的包管理命令;OPS 卸载包装命令没有 registry 参数。不要依赖插件管理器跨重启记住上次安装源。查看该次操作的 pnpm 日志, 确认被拒条目和实际源;单独执行 `npm config get registry` 不能证明宿主 pnpm 使用了哪个源。 对于仍固定旧 Git Bash 可选包的 legacy 0.4.x profile,重试前查询准确版本,不要只查询 `latest`(本候选已不再默认请求这个包): ```powershell npm view @dsh-ops/bash-win32-x64@0.2.1 version dist.integrity --registry=https://registry.npmjs.org ``` 缺失版本、网络失败或真实策略拒绝仍需处理。保持 `minimumReleaseAge`、完整性 等供应链检查启用,不要添加宽泛豁免或删除锁文件条目来强行通过。卸载若只是 停用了 bundle,不代表依赖和载荷已移除;通过官方管理器核对最终清单、bundle 选择和安装状态。事务回滚与 registry 的持久化由 DSH 包管理器负责。 ## pnpm 取包或解包时报出未映射的 Windows 错误 `ERR_PNPM_UNKNOWN` 与 `UNKNOWN: unknown error, open ''` 表示安装失败, 但这段文字本身不能确定根因。应查看操作日志中的包 URL 和准确路径。#524 点名的 归档来自 `yujunzhixue/dsh-purge`,失败成员位于该包的 `skills/redteam/` 目录。 这段日志没有证明 dsh-ops 代码已经执行,也没有证明修改 OPS 能修复该安装。 1. 保留宿主操作日志,记录版本、请求包、归档 URL、失败成员和 store 位置;脱敏凭据和用户路径。 2. 针对该路径检查防护/隔离历史和文件系统诊断。没有记录也不能排除某个原因;不要关闭防护, 或对整个 profile/store 添加宽泛排除来强制安装。 3. 如需对照,使用一次性空目录与另一个 store,例如 `pnpm --store-dir D:/pnpm-store add <同一条 spec> --ignore-scripts`,仅修改诊断夹具。 成功只能缩小排查范围,不能证明究竟是文件系统、缓存、权限还是安全条件不同。 4. 将脱敏证据交给实际失败包或安装器的维护者。若新日志点名 dsh-ops 本身,应附日志另行报告。 保持 `minimumReleaseAge`、完整性校验和 registry 策略启用。不要删除用户锁文件或 修改已安装 profile 来跳过诊断。 ## 安装期间出现 YAML 重复键 `duplicated mapping key (12:3)` 表示 YAML 解析器在同一 mapping 中读到了重复键。 #479 的摘录没有给出输入文件,无法判断来自插件 bundle、宿主 profile、pnpm 配置还是锁文件。 应保留操作日志,取得解析器文件名及附近脱敏 mapping。修改已定位的 mapping 前先备份; 不要删除整个 profile 或锁文件。OPS 清单门禁会解析随包 bundle,但它通过并不代表用户其它 YAML 输入也有效。 ## 不支持通过 Git 通道安装 不支持直接安装 `git+https://github.com/T-Auto/dsh-ops.git` 或未构建的源码检出。 `.gitignore` 排除了 `lib/` 与 `runtime/`;`prepack` 只编译 `lib/`,从不 stage 原生 worker,而 stage 它(`npm run build:ptc`)需要 cargo 和完整工具链。加一个 `prepare` 钩子只会让每次 git 安装都编译一遍 `lib/`, `runtime/` 依旧为空,结果是装完看似成功、文件工具却什么都没发布。因此本包不声明任何 安装期生命周期钩子:凡匹配 `(pre|post)?(prepare|install)` 的名字——`prepare`、 `preprepare`、`postprepare`、`install`、`preinstall`、`postinstall`——都会被 `scripts/validate-manifest.mjs` 判为失败,与构建无关的钩子同样如此,因为门禁读的是清单, 不是作者的意图。 请安装已发布的 registry 目标(`add dsh-ops`),或自行构建检出目录后 `npm pack`; 见[贡献指南](贡献指南.md#构建插件与源码安装)。 ## 从 0.4.x 迁移到 0.5.x | 变化 | 升级后的操作 | | --- | --- | | Bundle 只声明 `dsh-ops` 单行;不再插入旧 `mode-*`、`discovery`、`compat`、`panel`、`tui`、`shell`、`file`、`ptc`、`hide-official` 行。 | 备份并迁移保存值到单行,再删除旧覆盖;OPS 不自动重写 profile。 | | 内置与显式消费者使用一个装配器。 | 用 `presets.<精确-id>` 接入自定义配置;未列出预设不受影响。显式禁用绑定抑制内置回退。 | | 文件替换、Bash 和宿主 Shell 隐藏独立;兼容开关已移除。 | 文件设置 hideOfficialTools/replaceOfficial=false,宿主 Shell 设置 hidePwsh=false;总开关保留保存意图。 | | 常驻与动态加载独立,Native 发现不控制 PTC loader。 | 配置 residentTools、预设动态开关及总开关;PTC 必须有真实类型化宿主展示,不能只改预设名字。 | | Panel 与 TUI 仍为可选项。 | 需要时才启用单行内的 panel.enabled/tui.enabled。 | ## 从 0.3.x 迁移到 0.4.x | 变化 | 升级后的操作 | | --- | --- | | FastCtx/background 工具和随包 PowerShell 依赖已移除;固定 Windows Bash 依赖仍保留。 | 长任务使用宿主 jobs,PowerShell 使用宿主工具。删除过时的 `dsh-ops/background` 行可消除空操作警告。旧 provision 状态单独管理,见[常见问题](常见问题.md)。 | | 宿主投影的 `ops_bash` 必填 `description`。 | 与 `command` 或 `script_path` 一起传入简短说明,例如 `{"description":"检查工作区状态","command":"git status --short","workdir":"D:\\project"}`。`script_path` 使用 Bash;`.ps1` 脚本使用宿主 `pwsh`。 | | 高级 Native 工具按需发现。 | 若 `ops_edit_file` 等工具不在当前表里,先调用 `ops_discover_tools`,参数为 `{"query":"select:ops_edit_file"}`,结束本轮后在下一模型步使用。启动即需要的能力可配置到所属行的 `alwaysLoaded`。见[工具发现](工具发现.md)。 | | PTC 有独立的类型化 SDK 和加载器。 | 在 `run_code` 中调用 `tools.ops_load_sdk({ tools: ['glob'] })`,检查 Outcome 并结束本轮;成功加载后下一步再使用 `tools.ops_glob`。不能在同一个冻结 SDK 快照中加载并调用原先缺失的方法。见[PTC 按需加载](PTC按需加载.md)。 | | 配置采用分层 profile patch。 | 检查目标 profile 的 `cordis.patch.yml`、home patch 与已安装 bundle 声明。空根 `cordis.yml` 可以是合法宿主组合;OPS 不会把这个根文件重写为空。先备份并排查宿主合成配置,再决定是否恢复文件。 | | 包代码加载在宿主进程中。 | 替换包或第三方热重载后工具消失时,应完全重启。单行重挂载不能可靠升级已加载的包代码。先在新会话中检查启用行与实际版本,再考虑重装。 | 发现机制负责发布选中工具,不保证模型自动选择。Native discovery 与 PTC SDK 加载是不同入口。加载器本身缺失、宿主工具更新协议错误或明确权限拒绝需要单独 排查,上表不会绕过这些条件。 ## 用证据报告原生崩溃 NUL stdin 条件的空管道缓解单独见 [PR #392](https://github.com/T-Auto/dsh-ops/pull/392)。 回归验证了管道输入和立即 EOF,未证明所有 Desktop 崩溃的原因。#372 的更新窗口 访问冲突尚无 dump;文件操作的 `.dsh-ops-update-*.lock` 与 npm 包目录替换无关。 记录宿主/插件版本、启动方式、准确操作、可执行文件路径、退出码和 stderr。 对于可重现的 Windows 崩溃,管理员可按微软的 [WER 按应用 LocalDumps 指引](https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps), 在 `HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\.exe` 下设置 DWORD `DumpType=2`、`DumpCount=2`。选择实际崩溃的可执行文件,例如 `DeepSeek Harness.exe` 或 `bash.exe`。默认保存于 `%LOCALAPPDATA%\CrashDumps`; 自带崩溃处理器可能使 WER 无法收集。完成后只移除本次新增的诊断设置。Dump 可能 含会话数据和凭据,审阅后通过私密渠道提供,不要直接附到公开 issue;缺少 dump 不能证明某个具体原生故障。