# @linxin666/dsh-doctor [English](README.md) | 中文 DeepSeek Harness profile 的事务式救助模式:用户级 Doctor Supervisor 与透明 Doctor Launcher 维持一份隔离救援胶囊,检测启动失败、进程崩溃、心跳丢失、Web 故障与浏览器白屏,并通过快照、确定性修复、隔离健康门禁与原子提升或回滚恢复 profile。插件默认开启:初次安装或 WebUI 版本更新后救援模式自动生效,用户在 Doctor 卡片中显式关闭的选择会被保留;可在「设置 → 插件配置 → Web 插件」的 Doctor 卡片 中切换。本插件不修改 DSH 安装。 ## 能力 - Doctor Host 插件运行在每个受保护 DSH host 内:暴露 loopback 恢复 API,向 Supervisor 上报心跳与启动阶段事实,并收集浏览器故障上报。 - Doctor Web 控制台(「设置 → 插件配置 → Web 插件」内的家族插件卡片)展示系统 阶段、受保护 profile、故障事件与客户端故障探针,并记录已启用但从未启动的 Web UI 插件;在启用开关旁提供诊断、修复、回滚、暂停与恢复动作以及「服务与胶囊」卡片: 一键安装、重启升级与卸载。 - 「发送给 Harness」窗口把最近一次故障的摘要与错误堆栈组合成排障提示词,作为新回合 投递到当前 DSH 会话,让用户的 agent 就地诊断并修复;发送前可编辑或复制提示词。 失败插件行同时提供一键「复制错误」与「禁用并重启」(禁用经插件管理通道写入 profile 的启用行,宿主重启后生效)。 - Doctor Supervisor 作为宿主的受管子进程运行(带父进程存活监视,宿主退出即 退出,不注册任何 OS 级后台服务):把退出归类为用户停止、任务完成与真实故障, 应用崩溃循环熔断,并负责救援调度。 - Doctor Launcher 会在启动 DSH 前检测旧聚合包,并在 `autoMigrate`(默认开启)且目标包可用时自动执行 `@linxin666/dsh-web-ui-all` 到 `@linxin666/dsh-web-all` 的迁移;迁移经官方 `dsh plugin` CLI 执行,带 package.json/pnpm-lock 备份和 `--dump-config` 门禁。 - Doctor Launcher 把 `dsh` 参数原样转发给真实 DSH 可执行文件,转发 stdin、 stdout、stderr 与信号,记录启动意图与退出事实,之后才上报事件。 - 救援胶囊在机器本地目录准备固定版本 DSH 运行时、固定版本 Doctor 包与隔离的 `DSH_HOME`,普通 overlay 或 profile patch 损坏也不会阻断恢复控制台。 profile 的 package.json 与 cordis.patch.yml 只通过官方 `dsh plugin` 命令与 文档化的 profile 层约定修改。 ## 组成 | 部分 | 运行时机 | 职责 | | --- | --- | --- | | Doctor Host 插件 | 每个受保护 host 内 | 设置面、loopback API、心跳与客户端故障上报 | | Doctor Web 控制台 | DSH Web GUI 内 | 启用流程、状态、事件、诊断与修复动作 | | Doctor Supervisor | 宿主受管子进程 | 生命周期监控、分类、熔断、救援调度 | | Doctor Launcher | 每次 `dsh` 调用 | 透明转发参数、信号与退出事实 | | 救援胶囊 | 机器本地隔离目录 | 固定运行时、隔离 home、离线诊断与修复工具 | ## 安装 ### 从 npm 安装(全家桶优先) ```sh dsh plugin --profile web add @linxin666/dsh-web-all@latest ``` ### 独立 bundle 安装 ```sh dsh plugin --profile web add @linxin666/dsh-doctor@latest ``` ### 从仓库安装(开发调试) ```sh git clone https://github.com/zhu1090093659/dsh-web.git cd dsh-web pnpm install pnpm -r build dsh plugin --profile web add link:$(pwd)/packages/dsh-doctor ``` 重启 `dsh web`,打开「设置 → 插件配置 → Web 插件」,展开 Doctor 卡片确认「启用救助模式」已开启(新安装默认开启)。 包内同时提供 `dsh-doctor` CLI:Supervisor、Launcher、胶囊配置与旧服务清理。 ## 启用 在 Doctor 卡片打开「启用救助模式」后,宿主半区挂载 `/api/doctor/*` 端点,写入当前保护策略,并在后台自动核对 Supervisor、包版本、安装路径和救援胶囊;Supervisor 以宿主子进程形态按需拉起,不阻塞 Web 启动,宿主退出即随之退出,不再注册任何 OS 级后台服务(首次核对会顺带清理旧版本遗留的服务注册)。关闭时宿主停止心跳并暂停 Supervisor 自动干预,但保留胶囊。显式卸载会写入抑制标记,后续启动不会偷偷复活 Supervisor;用户点击「一键安装」才清除该标记。控制台按钮保留为手动重试与强制修复入口。 ## 更新 更新到新版本后,先重启 `dsh web` 让宿主半区加载新代码:核对器检测到版本滞后的 Supervisor 时会经 IPC 请其退出,并把当前版本作为自己的子进程重新拉起,版本不一致时 同步刷新救援胶囊。若用户更改了 provider 或密钥,胶囊的凭据指纹会检测到差异,同一次 核对会按新配置重新镜像。核对流程幂等,可安全重复执行;`dsh-doctor service-uninstall` 保留为旧版 OS 服务注册的手动清理入口。 ## CLI `dsh-doctor` 提供运维命令: | 命令 | 含义 | | --- | --- | | `dsh-doctor supervisor [--parent-pid ]` | 运行 Supervisor(带父 pid 时启用存活监视,父进程退出则随之退出) | | `dsh-doctor launch [dsh 参数...]` | 在监督下转发一次 `dsh` 调用 | | `dsh-doctor migrate [profile]` | 直接执行确定性旧聚合包迁移 | | `dsh-doctor status` | 以 JSON 打印 Supervisor 快照 | | `dsh-doctor provision [profile] [--no-credentials]` | 配置或刷新救援胶囊(镜像 provider 配置与凭据、0600;默认固定当前包版本,`DSH_DOCTOR_PACKAGE` / `--no-credentials` / `DSH_DOCTOR_CREDENTIALS=off` 可调整) | | `dsh-doctor snapshot [profile]` | 快照一个 profile | | `dsh-doctor diagnose [profile]` | 只诊断与规划,不写文件 | | `dsh-doctor repair [profile] --allow-live` | 运行暂存修复事务(门禁后提升) | | `dsh-doctor rollback ` | 从隔离区恢复已提升的事务 | | `dsh-doctor service-uninstall` | 清理旧版本 doctor 遗留的 OS 服务注册 | 退出码:0 正常,1 已修复并验证,2 需要关注,3 被阻塞(锁、离线或缺少密钥)。 ## 配置 host 设置命名空间为 `doctor`: | 键 | 默认值 | 含义 | | --- | --- | --- | | `enabled` | `true` | 总开关;开启时挂载路由并自动核对部署,关闭时暂停 Supervisor 且不卸载 | | `fullProtection` | `true` | 托管保护;发送心跳、记录故障事件并执行熔断;关闭后进入观察模式 | | `autoRepair` | `false` | 隔离门禁通过后自动提升;关闭时保留候选并等待明确确认。同时是下方启动自愈的总开关 | | `autoMigrate` | `true` | 启动前自动迁移旧聚合包;只对已知的 `dsh-web-ui-all` -> `dsh-web-all` 映射生效 | | `heartbeatIntervalMs` | `5000` | host 心跳周期 | 环境变量: | 变量 | 含义 | | --- | --- | | `DSH_DOCTOR_HOME` | doctor 根目录(默认 `~/.dsh-doctor`,可覆盖) | | `DSH_DOCTOR_REAL_DSH` | 真实 `dsh` 可执行文件的绝对路径 | | `DSH_DOCTOR_PACKAGE` | 安装救援 Doctor 用的包规格 | | `DSH_DOCTOR_PACKAGE_DIR` | 开发时本地仓库路径 | | `DSH_DOCTOR_CREDENTIALS` | `off` 时禁止把凭据文件镜像进救援胶囊(默认镜像) | | `DSH_DOCTOR_ENDPOINT` | launcher 注入的 Supervisor 端点 | | `DSH_DOCTOR_TOKEN` | launcher 注入的单次 Supervisor token | | `DSH_DOCTOR_RUN_ID` | launcher 注入的单次启动标识 | ## 健康与恢复 | 故障 | 检测 | 默认动作 | | --- | --- | --- | | 启动失败 | launcher 在 ready 阶段前退出、结构化 stderr | 重试一次后进入救援 | | 插件初始化失败 | 配置阶段非零退出 | 重试一次后进入救援 | | 运行期崩溃 | 启动后的信号或非零退出 | 一次重启后熔断 | | 心跳丢失 | 窗口内无心跳 | 进程与 HTTP 探测,然后救援 | | Web 故障 | 多次 loopback HTTP 失败 | host 存活时用备用端口救援 | | 浏览器白屏 | 客户端探针与错误边界 | 先做客户端本地恢复;有证据才记事件 | | 用户 Ctrl+C | launcher 信号 | 正常停止,不记事件 | | headless 业务失败 | 健康启动后非零退出 | 只报告 | 熔断器在窗口内反复失败后暂停自动重试,并把 profile 隔离等待用户明确确认。 ### 启动自愈(插件级隔离) 当 profile 在失败窗口内第二次启动失败且 `autoRepair` 开启时,Supervisor 会从捕获的 stderr 归因(宿主启动报错会点名失败的 loader entry);当能确定唯一涉事行时,向 profile 的 `cordis.patch.yml` 追加一行 `- id: ` + `disabled: true` 覆盖 (与 loader 持久化自毁插件是同一行合并机制)。下一次 `dsh web` 就能在坏插件缺席的 情况下启动,其余插件照常挂载。无法归因到单行的失败(文件错误、patch 不可解析、 宿主级故障)只标注事件;写入器拒绝禁用任何它不能证明损坏的行,从不触碰启动后才 崩溃的场景,也从不编辑解析失败的 patch 文件(那是 D-040 隔离线的职责)。每次 自愈都进 journal 与事件证据,被禁用的插件随时可以手工从 `cordis.patch.yml` 恢复。 ## 修复模型 每次修复都是事务:快照当前 profile,准备候选环境,只应用确定性的规则化操作, 对候选执行隔离的 dump-config 与 Web 健康门禁,提升时把原件移入隔离区,就地验证, 失败则按字节回滚。修复引擎从不猜测:歧义情况生成候选等待确认,不安装未验证的 `latest`,不执行不可信 shell 命令。修复与回滚日志追加写盘,可跨崩溃恢复。 ## 安全模型 - 全部以当前用户权限运行;不使用 root 或管理员提权。 - Supervisor 只监听本地 Unix socket(Windows 命名管道);请求带按实例生成的 bearer token,文件权限 0600。 - Web API 仅限 loopback,绝不把 token 交给浏览器;被拒请求返回 HTTP 403 与 `{ ok: false, error: "forbidden: loopback-only" }`。 - launcher 与 Supervisor 从不运行 shell;DSH 参数原样转发。 - 状态、日志与事件记录不写密钥;快照对凭据脱敏,脱敏层不可能恢复它们。 - 救援胶囊只绑定 loopback,除显式检查外不读取 profile home overlay。 - 救援胶囊镜像用户 profile 的设置与凭据文件(settings.yaml / .credentials.yaml / .env 等,0600,仅规范文件名,备份变体不镜像);manifest 只记录文件名与内容指纹, 绝不写密钥本身;卸载时按清单清除镜像。 - 写入范围限定在 `DSH_DOCTOR_HOME` 与包自有文件;profile 变更只经官方 `dsh plugin` 命令。 - 一键安装、升级与卸载只经本包 CLI 以参数数组发起,从不启用 shell;不注册任何 OS 服务,因此不存在 launchd/systemd/schtasks 攻击面。 ## 已知限制 - 用绝对路径直接调用真实 `dsh` 可执行文件会绕过 launcher;保护覆盖 launcher 启动的运行,被绕过的 host 会报告为部分托管。 - 机器级损坏(Node 二进制无法加载、home 不可写、卷不可用)无法自动修复;控制台 会给出 CLI 恢复指引。 - 快照默认只在本机使用;跨机器恢复需要导出产物与独立凭据 vault。 - Windows 对 junction、PowerShell 5.1 Unicode 与用户级计划任务为尽力支持;部分 内部逻辑假定 POSIX 文件语义。 ## 数据遥测 浏览器半区每个 UTC 日向 dsh-market.com 发送一次匿名安装心跳:仅含一个 localStorage 随机 ID 与本包名,无其他数据。服务端只存储该 ID 的加盐哈希,不存 IP,且只暴露聚合计数。完整契约见 [docs/telemetry.md](../../docs/telemetry.md)。