OpenCode-Health-Guard-文章
·
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/plugin 的 PluginInput 类型没有 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 |
-----------------|---------|---------|-------------------
改进方向
- 补充异常路径测试:超时、命令失败、JSON 解析异常
- 修复 TypeScript 类型错误:确保
typecheck通过 - 增加 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,或者基于它扩展自己的检查规则!
更多推荐



所有评论(0)