OpenCode Health Guard:打造 18 项自检的启动健康闸门插件

作为第三方开发者,我深度体验了 OpenCode 插件开发流程,分享这个 Boot-Time Self-Check Gateway 的完整开发过程。

目录


背景与动机

在使用 OpenCode 过程中,我多次遇到因配置错误导致的启动失败:

  • 配置不一致opencode.jsonc 只有 1 个 provider,但 fallback.json 引用了 22 个
  • JSON 语法错误:编辑工具将行号标记写入文件导致 JSON 损坏
  • API Key 问题:占位符 API Key 未替换
  • MCP 服务不可达:命令路径配置错误

每次排查都需要手动检查多个文件,费时费力。于是我开发了 Health Guard —— 一个启动前自动检查配置健康的插件。

什么是 Boot-Time Self-Check Gateway

Health Guard 是一个 OpenCode 插件,在以下场景自动执行自检:

场景 触发时机
startup OpenCode 启动时(服务器连接后)
reload 配置更新时
manual CLI 手动运行

核心特性:

  • ✅ 18 项自检清单
  • ✅ 详细报告输出
  • ✅ 修复建议
  • ✅ 严重级别分级(H/M/L)
  • ✅ 可配置跳过特定检查

18 项自检清单详解

ID 检查项 严重性 说明
CFG-001 配置文件存在性 H 检查 opencode.jsonc 是否存在
CFG-002 配置文件语法有效性 H JSON 解析是否成功
CFG-003 配置字段完整性 H 核心字段是否存在
CFG-004 Schema 兼容性 M 检查废弃字段
CFG-005 MCP 服务清单完整性 M MCP 配置是否正确
CFG-006 MCP 连接可达性 H MCP 命令是否可执行
CFG-007 MCP 端口与监听状态 M 端口冲突检测
CFG-008 运行时依赖 M Node/Bun/Git 检查
CFG-009 MCP 权限校验 M 权限配置检查
CFG-010 API Key 有效性 H 占位符检测
CFG-011 环境变量完整性 M 环境变量引用检查
CFG-012 路径合法性 M 必需目录检查
CFG-013 日志系统健康 L 日志目录可写
CFG-014 缓存完整性 L 缓存目录检查
CFG-015 冲突配置检测 M 重复 ID 检查
CFG-016 版本兼容性 L OpenCode 版本
CFG-017 安全与隐私边界 H 明文密钥检测
CFG-018 回归稳定性预演 H 配置加载测试

技术架构设计

opencode-health-guard/
├── src/
│   ├── index.ts      # 插件入口,Hook 注册
│   ├── types.ts      # 类型定义
│   ├── checkers.ts   # 18 项检查器实现
│   └── engine.ts     # 检查引擎,报告生成
├── tests/
│   └── index.test.ts # 单元测试
├── package.json
├── tsconfig.json
└── README.md

核心类型定义

// 检查项结果
export interface CheckResult {
  id: string          // CFG-001
  name: string        // 配置文件存在性
  status: CheckStatus // PASS | FAIL | WARN | SKIP
  evidence: string    // 证据
  fix?: string        // 修复建议
  severity: Severity  // H | M | L
  duration?: number   // 执行时间
}

// 自检报告
export interface SelfCheckReport {
  timestamp: string
  trigger: 'startup' | 'reload' | 'manual'
  total: number
  passed: number
  failed: number
  skipped: number
  blocked: boolean
  checks: CheckResult[]
}

OpenCode 插件 Hook 机制

const healthGuardPlugin: Plugin = async (input) => {
  const hooks: Hooks = {
    // 事件钩子 - 服务器连接时触发
    event: async ({ event }) => {
      if (event.type === 'server.connected') {
        const report = await runSelfCheck(config, 'startup')
        console.log(formatReport(report))
      }
    },
    
    // 配置钩子 - 配置更新时触发
    config: async (cfg: Config) => {
      const report = await runSelfCheck(config, 'reload')
      console.log(formatReport(report))
    }
  }
  return hooks
}

export default healthGuardPlugin

核心代码实现

检查器示例

// CFG-002: 配置文件语法有效性
export const checkConfigSyntax: Checker = async (ctx: CheckContext): Promise<CheckResult> => {
  const configPath = `${ctx.configDir}/opencode.jsonc`
  const content = await ctx.readFile(configPath)
  
  if (!content) {
    return {
      id: 'CFG-002',
      name: '配置文件语法有效性',
      status: 'FAIL',
      evidence: '无法读取配置文件',
      fix: '确保配置文件存在且可读',
      severity: 'H'
    }
  }
  
  // 移除 JSONC 注释
  const jsonContent = content
    .replace(/\/\/.*$/gm, '')
    .replace(/\/\*[\s\S]*?\*\//g, '')
  
  const parsed = ctx.parseJSON(jsonContent)
  
  return {
    id: 'CFG-002',
    name: '配置文件语法有效性',
    status: parsed.success ? 'PASS' : 'FAIL',
    evidence: parsed.success ? 'JSON 解析成功' : `JSON 解析错误: ${parsed.error}`,
    fix: parsed.success ? undefined : `修复 JSON 语法错误: ${parsed.error}`,
    severity: 'H'
  }
}

报告格式化

export function formatReport(report: SelfCheckReport): string {
  const lines: string[] = []
  
  lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
  lines.push('🔒 OpenCode Health Guard - SELF-CHECK REPORT')
  lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
  lines.push(`- 时间: ${report.timestamp}`)
  lines.push(`- 触发场景: ${report.trigger}`)
  lines.push(`- 通过: ${report.passed}`)
  lines.push(`- 失败: ${report.failed}`)
  
  if (report.blocked) {
    lines.push('')
    lines.push('[BLOCKED] 启动被禁止')
    // 输出修复建议...
  }
  
  return lines.join('\n')
}

遇到的坑与解决方案

坑 1:OpenCode 插件 Hook 类型问题

问题@opencode-ai/pluginPluginInput 类型没有 log 属性,但实际运行时存在。

解决:使用可选链操作符,并在文档中说明。

// 错误写法
log("message")

// 正确写法
input.log?.("message")

坑 2:JSONC 注释处理

问题opencode.jsonc 支持 JSONC(带注释的 JSON),直接用 JSON.parse 会失败。

解决:使用正则移除注释后再解析。

const jsonContent = content
  .replace(/\/\/.*$/gm, '')      // 移除单行注释
  .replace(/\/\*[\s\S]*?\*\//g, '') // 移除多行注释

坑 3:命令执行超时

问题:某些检查(如端口扫描、MCP 连接)可能超时。

解决:为所有异步操作添加超时控制。

const result = await Promise.race([
  check.checker(context),
  new Promise<CheckResult>((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), config.timeout)
  )
])

坑 4:覆盖率不足

问题:测试覆盖率 75.05%,异常路径覆盖不足。

解决:补充边界测试用例。

场景 测试
超时 检查器执行超时
异常 文件读取失败
边界 空 JSON、无效字段

测试结果与覆盖率

bun test v1.3.9

 17 pass
 0 fail
 146 expect() calls
Ran 17 tests across 1 file. [95.00ms]

-----------------|---------|---------|-------------------
File             | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|---------|-------------------
All files        |   84.21 |   75.05 |
 src/checkers.ts |   94.74 |   64.22 |
 src/engine.ts   |   73.68 |   85.88 |
-----------------|---------|---------|-------------------

改进方向

  1. 补充异常路径测试:超时、命令失败、JSON 解析异常
  2. 修复 TypeScript 类型错误:确保 typecheck 通过
  3. 增加 E2E 测试:真实 OpenCode 环境测试

安装与使用

方式一:复制到插件目录

cp -r ~/OpenClaw-Workspace/opencode-health-guard ~/.config/opencode/plugins/health-guard

方式二:在 opencode.jsonc 中配置

{
  "plugin": ["health-guard"],
  "healthGuard": {
    "enabled": true,
    "blockOnFail": true,
    "skipChecks": ["CFG-016"],
    "timeout": 30000
  }
}

方式三:CLI 独立运行

cd ~/OpenClaw-Workspace/opencode-health-guard
bun run src/index.ts

输出示例

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔒 OpenCode Health Guard - SELF-CHECK REPORT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 时间: 2026-03-09T15:30:00.000Z
- 触发场景: startup
- 总项数: 18
- 通过: 16
- 失败: 2
- 阻塞: FAIL

[CFG-001] 配置文件存在性 | ✅ PASS
  证据: 文件存在: ~/.config/opencode/opencode.jsonc

[CFG-010] API Key 有效性 | ❌ FAIL
  证据: 发现占位符 API Key
  修复: 替换占位符为实际的 API Key 或使用环境变量
  严重: H

[BLOCKED] 启动被禁止

修复动作:
  1) [CFG-010] 替换占位符为实际的 API Key 或使用环境变量
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

总结与改进方向

已完成

  • ✅ 18 项自检清单实现
  • ✅ OpenCode 插件集成
  • ✅ CLI 独立运行模式
  • ✅ 详细报告输出
  • ✅ 17 个单元测试

待改进

优先级 改进项
修复 TypeScript 类型错误
补充异常路径测试
添加更多检查项
支持自定义检查脚本
发布到 npm

项目地址

https://github.com/Jones-BIGBIG/opencode-health-guard


工具:OpenCode + Bun + TypeScript
测试覆盖率:84.21% 函数 / 75.05% 行

如果你也遇到 OpenCode 配置问题,不妨试试 Health Guard,或者基于它扩展自己的检查规则!

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐