Skip to content
dsh.fish
Bundle

crash-guard-dsh

崩溃保护:某个插件导致 DeepSeek Harness 启动崩溃时,下次启动自动禁用该插件,避免反复崩溃。

Source
limochaishang
stars
1 stars
License
MIT
Updated
Updated yesterday

Readme

# crash-guard-dsh

> DeepSeek Harness / PawWork 插件崩溃保护插件:当某个插件导致启动崩溃或卡死时,自动隔离该插件,避免反复崩溃(crash loop)。

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-blue)](#)
[![DSH](https://img.shields.io/badge/DeepSeek%20Harness-compatible-green)](#)

## 功能特性

- **崩溃自动隔离**:加载期崩溃后,下次启动自动禁用导致崩溃的插件
- **卡死自动恢复**:独立看门狗进程检测完全卡死(hang),自动杀进程、禁用可疑插件、重启
- **三层自愈架构**:同进程监控 → 独立看门狗 → 手动恢复,层层兜底
- **零配置**:安装即用,无需额外配置
- **安全护栏**:不误伤正常插件、不隔离自身和核心包、防重入误判

## 工作原理

### 三层自愈架构

```
┌─────────────────────────────────────────────────┐
│  第一层:同进程监控(index.mjs)                  │
│  - 每 50ms 跟踪正在加载的插件                      │
│  - 15 秒 hang 超时检测                            │
│  - 状态机:booting → ready → clean               │
│  - 崩溃后下次启动自动隔离                          │
├─────────────────────────────────────────────────┤
│  第二层:独立看门狗(watchdog.mjs)                │
│  - detached 独立进程,不加载任何插件               │
│  - 每 5 秒检查心跳文件                             │
│  - 30 秒无心跳判定完全卡死                         │
│  - 自动:禁用可疑插件 → 杀进程 → 重启             │
├─────────────────────────────────────────────────┤
│  第三层:手动恢复                                  │
│  - 删除 cordis.patch.yml 中的禁用条目             │
│  - 重启即可恢复                                    │
└─────────────────────────────────────────────────┘
```

### 崩溃检测流程

类似 Chrome 扩展的安全启动(safe mode):

| 阶段 | 动作 |
|------|------|
| 每次启动 | crash-guard 作为 profile bundles **第一位**加载,最先执行 `apply` |
| 启动中 | 每 50ms 跟踪正在加载的插件,同步写盘记录 `lastLoading` |
| 加载完成 | 状态标记 `ready`;正常退出标记 `clean` |
| **检测崩溃** | 下次启动发现上次状态停在 `booting`(没走完也没正常退出)→ 判定加载期崩溃 |
| **自动隔离** | 把崩溃前正在加载的那个插件写进用户层 `cordis.patch.yml`(`disabled: true`),不再加载 |

### 独立看门狗机制

同进程监控有一个根本局限:如果插件导致**完全阻塞事件循环**(如无限循环、同步阻塞),crash-guard 自身的 50ms 轮询也会被冻住,无法检测卡死。

独立看门狗解决这个问题:

1. crash-guard 启动时用 `child_process.spawn` 启动 `watchdog.mjs`(`detached: true`)
2. 主进程每 2 秒写心跳文件 `heartbeat.json`
3. 看门狗每 5 秒检查心跳文件的修改时间
4. 超过 30 秒没更新 → 判定完全卡死
5. 自动执行:禁用可疑插件 → 杀掉所有 PawWork 进程(排除自己)→ 重启 PawWork

## 安全护栏(不误伤)

- **只处理加载期崩溃/卡死**:运行期崩溃/强杀只记日志不自动禁用——无法可靠归因
- **永不隔离 guard 自身和核心包**:`id: crash-guard` 和 `@deepseek-ai/*` 永远不会被禁用
- **每次只禁一个**:每次崩溃只禁用最后一个被跟踪的插件,其余保持原样
- **防重入锁**:模块顶层全局锁,live-reload 热重载时清理上一个实例,避免残留积累
- **新鲜度校验**:60 秒状态文件新鲜度窗口 + 进程标记双重保险,防 live-reload 误判
- **看门狗锁文件**:防止多个看门狗实例同时运行

## 文件布局

```
crash-guard-dsh/
├── index.mjs          # 插件主体(崩溃检测 + hang 检测 + 看门狗启动 + 心跳写入)
├── watchdog.mjs       # 独立看门狗进程(detached,监控心跳、自动恢复)
├── cordis.patch.yml   # bundle 补丁:以第一位插入 crash-guard 条目
├── package.json       # 插件声明
├── install.ps1        # Windows 安装脚本
├── uninstall.ps1      # Windows 卸载脚本
├── README.md          # 本文档
├── LICENSE            # MIT 许可证
└── test/
    ├── simulate.mjs        # 崩溃恢复流程模拟测试
    └── verify-install.mjs  # 安装验证测试
```

运行时状态目录(默认):

- 状态:`$DSH_HOME/crash-guard/state.json`
- 心跳:`$DSH_HOME/crash-guard/heartbeat.json`
- 看门狗锁:`$DSH_HOME/crash-guard/watchdog.lock`
- 看门狗日志:`$DSH_HOME/crash-guard/watchdog.log`
- 隔离日志:`$DSH_HOME/crash-guard/quarantine.log`(JSONL,含每次禁用记录)

> `$DSH_HOME` 默认为 `~/.pawwork/dsh`,可用 `DSH_HOME` 环境变量覆盖。

## 安装

### 前置要求

- DeepSeek Harness / PawWork 已安装
- Node.js 18+(DSH 自带运行时即可)

### Windows(PowerShell)

```powershell
# 克隆或下载本仓库
git clone https://github.com/limochaishang/crash-guard-dsh.git
cd crash-guard-dsh

# 运行安装脚本
.\install.ps1
```

脚本会:

1. 复制 `crash-guard-dsh` 到目标 profile 的 `node_modules/`
2. 把 `crash-guard-dsh` 插入目标 profile `package.json` 的 `dsh.profile.bundles` **第一位**
3. 加入 `dependencies`
4. 备份被修改的文件为 `.crash-guard.bak`

安装后**重启 DSH / PawWork** 生效。

### 手动安装

1. 复制本目录到 profile 的 `node_modules/crash-guard-dsh/`
2. 在 profile 的 `package.json` 中,把 `crash-guard-dsh` 加入 `dsh.profile.bundles` **第一位**
3. 加入 `dependencies`
4. 重启 DSH / PawWork

## 卸载

```powershell
.\uninstall.ps1
```

从 bundles / dependencies 移除并删除 node_modules 里的包目录;`state.json`、`heartbeat.json`、`quarantine.log` 会保留(可选删除)。

## 使用方法

安装后无需任何操作,crash-guard 自动工作:

- **正常启动**:crash-guard 跟踪加载过程,记录状态,启动完成后进入 ready 状态
- **插件崩溃**:下次启动自动隔离导致崩溃的插件,PawWork 可以正常启动
- **插件卡死**:看门狗检测到无心跳,自动杀进程、禁用可疑插件、重启
- **查看日志**:检查 `$DSH_HOME/crash-guard/quarantine.log` 查看被禁用的插件记录

## 手动恢复被禁用的插件

崩溃/卡死后 crash-guard 会在用户层 `cordis.patch.yml`(如 `profiles/web/cordis.patch.yml`)追加类似内容:

```yaml
# [crash-guard] 自动禁用:插件 "xxx" (yyy) 在最近一次启动时导致崩溃。
# 如需恢复,删除下面两行即可。
- id: yyy
  disabled: true
```

删除这两行并重启即可重新启用该插件。

## 配置选项

crash-guard 零配置即可使用。如需自定义,可通过 patch 配置以下参数:

| 参数 | 默认值 | 说明 |
|------|--------|------|
| `stateDir` | `$DSH_HOME/crash-guard` | 状态文件目录 |
| `patchFile` | profile 下的 `cordis.patch.yml` | 禁用插件写入的补丁文件 |
| `freshWindowMs` | 60000 | 状态文件新鲜度窗口(毫秒) |
| `readyTimeoutMs` | 30000 | 启动超时时间(毫秒) |
| `hangTimeoutMs` | 15000 | 单插件加载 hang 超时(毫秒) |
| `heartbeatIntervalMs` | 2000 | 心跳写入间隔(毫秒) |
| `watchdogCheckIntervalMs` | 5000 | 看门狗检查间隔(毫秒) |
| `watchdogTimeoutMs` | 30000 | 看门狗心跳超时(毫秒) |

## 测试

### 模拟崩溃测试

```powershell
node test/simulate.mjs
```

测试覆盖:
- 正常启动 → clean 状态
- 崩溃残留 → 下次自动禁用
- 幂等不重复禁用

### 真实环境测试

1. 安装一个会导致崩溃的插件(如已知不兼容的插件)
2. 重启 PawWork,观察是否崩溃
3. 再次重启,观察 crash-guard 是否自动隔离该插件
4. 检查 `quarantine.log` 确认禁用记录

### 看门狗测试

1. 安装一个会导致完全卡死的插件(如无限循环、同步阻塞)
2. 重启 PawWork,观察是否卡死
3. 等待约 30 秒,观察看门狗是否自动杀进程、禁用插件、重启
4. 检查 `watchdog.log` 确认看门狗动作记录

## 常见问题(FAQ)

### Q: crash-guard 会影响正常插件的加载吗?

A: 不会。crash-guard 只在启动时跟踪加载过程,不修改其他插件的代码或配置。正常启动后,crash-guard 进入 ready 状态,不再干预。

### Q: 为什么是"下次启动"才生效?

A: 崩溃发生在加载过程中,guard 自身来不及写禁用指令;只有等下一次启动、由 guard 首先执行检测并落盘禁用,才能阻止坏插件再次加载。这与 Chrome 安全启动的设计一致。

### Q: 看门狗会不会误杀正常进程?

A: 概率极低。看门狗只在心跳超过 30 秒没更新时才触发,而正常运行时主进程每 2 秒写一次心跳。只有完全卡死(事件循环被阻塞)才会导致心跳停止。

### Q: 两个进程(主进程 + 看门狗)会不会都崩溃?

A: 理论上可能但概率极低。看门狗代码极简(约 100 行),不加载任何插件,不依赖 DSH 运行时,作为 detached 独立进程运行。主要风险来自系统级故障(如操作系统崩溃、断电)。

### Q: 归因不准确怎么办?

A: 当前归因是启发式的——记录崩溃前正在加载的插件。在某些情况下(如插件 A 阻塞导致 loader 认为插件 B 还在加载),可能归因到错误的插件。这是已知局限,已列为未来工作方向。如果发现误禁,手动删除 `cordis.patch.yml` 中的禁用条目即可恢复。

### Q: crash-guard 自身崩溃了怎么办?

A: crash-guard 有防重入锁和异常处理。如果 crash-guard 自身崩溃,它不会写入崩溃状态(因为还没完成 booting→ready 的转换),下次启动会重新尝试。crash-guard 代码经过严格测试,自身崩溃概率极低。

## 局限性

- 崩溃发生在 crash-guard 加载**之前**(如 base bundle 自身问题)时无法归因——设计边界,与主流 safe-mode 一致
- 只针对「加载期崩溃/卡死」;运行期崩溃不自动禁用
- 归因是启发式的,极端情况下可能不准确
- 本插件不参与 UI,无配置项(如需自定义可通过 patch 配置)

## 未来工作

- [ ] 精确归因:通过插件加载时序分析更准确地定位崩溃元凶
- [ ] 三层监控架构:同进程 → 独立看门狗 → 操作系统级服务监控
- [ ] 崩溃报告:收集崩溃堆栈,生成更详细的诊断报告
- [ ] 插件兼容性评分:基于历史崩溃数据评估插件稳定性
- [ ] 批量测试工具:自动化测试插件市场中插件的兼容性

## 贡献

欢迎提交 Issue 和 Pull Request!

1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启 Pull Request

## 许可证

本项目采用 [MIT 许可证](LICENSE) 开源。

## 致谢

- DeepSeek Harness 团队提供的插件架构
- Chrome 扩展安全启动机制的设计灵感
- 所有贡献者和用户的反馈

---

**如果你觉得这个插件有用,欢迎给个 Star ⭐**

Install

dsh plugin --profile web add github:limochaishang/crash-guard-dsh

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source