插件系统与发布流水线:Codex 插件布局、技能同步与验证
插件系统与发布流水线:Codex 插件布局、技能同步与验证
学习目标
- 理解 OMX 的 Codex 插件目录布局与 marketplace 元数据
- 掌握技能安装同步机制(syncManagedContent 保留用户编辑)
- 了解过期技能清理与 Native Agent 验证
- 理解 npm 发布流程的 prepack 验证链
前置知识
本章聚焦 Oh-My-Codex 的具体工程实践,无需前置阅读。
项目实践
插件目录布局
plugins/oh-my-codex/├── .codex-plugin/│ └── plugin.json # 插件元数据└── hooks/ ├── hooks.json # Hook 注册表 └── codex-native-hook.mjs # Launcher shimplugin.json:
{ "name": "oh-my-codex", "version": "0.18.9", "description": "Multi-agent orchestration layer for OpenAI Codex CLI", "marketplace": { "name": "oh-my-codex", "display_name": "Oh-My-Codex" }}技能安装同步
伪代码(src/cli/setup.ts — installSkills):function installSkills(options): // 读取源技能目录 sourceSkills = readSkillManifest("skills/")
// 确定目标目录(用户级或项目级) destDir = options.projectScoped ? ".codex/skills/" : "~/.codex/skills/"
// 读取目录技能 destSkills = readInstalledSkills(destDir)
// 安装/更新活跃技能 for skill in sourceSkills: if skill.status == "deprecated": continue // 跳过已废弃技能
if options.teamModeDisabled and skill.requiresTeam: continue // 跳过团队技能
srcPath = `skills/${skill.name}/` destPath = `${destDir}/${skill.name}/`
// 同步,保留用户编辑 syncManagedContent(srcPath, destPath)
// 清理过期技能 if options.force: for skill in destSkills: if not sourceSkills.has(skill.name) or sourceSkills[skill.name].status == "deprecated": removeSkill(destPath)
// 刷新插件缓存 refreshPluginCache()syncManagedContent 保留用户编辑
伪代码(syncManagedContent):function syncManagedContent(sourceDir, destDir): // 只同步托管文件(SKILL.md、references/、examples/) managedFiles = findManagedFiles(sourceDir)
for file in managedFiles: srcFile = `${sourceDir}/${file}` destFile = `${destDir}/${file}`
if not fileExists(destFile): // 新文件,直接复制 copy(srcFile, destFile) else: // 已有文件,检查是否为用户编辑 if isManagedByOmx(destFile): // OMX 管理的文件,同步最新版本 copy(srcFile, destFile) else: // 用户编辑的文件,保留不变 log(`Preserving user edits: ${destFile}`)OMX 管理标记:OMX 通过文件内容中的特定标记(如 frontmatter 中的 managed: true 或特定注释)判断文件是否为 OMX 管理。
过期技能清理
Catalog manifest(src/catalog/manifest.json)跟踪每个技能的状态:
伪代码(manifest 条目):{ "name": "swarm", "category": "core", "status": "deprecated", "reason": "Replaced by team runtime"},{ "name": "ecomode", "category": "core", "status": "deprecated", "reason": "Replaced by ultragoal"},{ "name": "help", "category": "utility", "status": "deprecated", "reason": "Replaced by /skills command"}安装时,status: "deprecated" 的技能被跳过。--force 模式下,已安装的过期技能被移除。
Plugin Bundle 验证
伪代码(scripts/sync-plugin-mirror.js — verify:plugin-bundle):function verifyPluginBundle(): // 检查插件目录的完整性 requiredFiles = [ "plugins/oh-my-codex/.codex-plugin/plugin.json", "plugins/oh-my-codex/hooks/hooks.json", "plugins/oh-my-codex/hooks/codex-native-hook.mjs", ".agents/plugins/marketplace.json" ]
for file in requiredFiles: if not fileExists(file): throw Error(`Missing plugin file: ${file}`)
// 验证 marketplace.json 与 skills/ 目录一致性 marketplace = readJson(".agents/plugins/marketplace.json") skills = readSkillManifest("skills/")
for skill in skills: if not marketplace.skills.has(skill.name): throw Error(`Skill ${skill.name} not in marketplace.json`)
// 验证 plugin.json 版本与 package.json 一致 plugin = readJson("plugins/oh-my-codex/.codex-plugin/plugin.json") pkg = readJson("package.json")
if plugin.version != pkg.version: throw Error(`Plugin version ${plugin.version} != package version ${pkg.version}`)Native Agent 验证
伪代码(scripts/verify-native-agents.js):function verifyNativeAgents(): definitions = readAgentDefinitions() catalog = readCatalogManifest()
// 验证别名/合并目标有效 for agent in catalog.agents: if agent.status in ["alias", "merged"]: target = agent.canonicalTarget if not definitions.has(target): throw Error(`Agent ${agent.name} points to invalid target: ${target}`)
// 验证所有 active/internal agent 有提示词文件 for agent in catalog.agents: if agent.status in ["active", "internal"]: promptFile = `prompts/${agent.name}.md` if not fileExists(promptFile): throw Error(`Missing prompt file for agent: ${agent.name}`)
// 验证叶子代理守卫 for agent in definitions: if not agent.nativeSubagentDelegation?.allowed: prompt = readPromptFile(agent.name) if not contains(prompt, "NATIVE_SUBAGENT_LEAF_GUARD"): throw Error(`Missing leaf guard for agent: ${agent.name}`)npm 发布流程
package.json scripts:{ "prepack": "npm run build && npm run verify:native-agents && npm run sync:plugin && npm run verify:plugin-bundle && npm run clean:native-package-assets", "postpack": "npm run clean:native-package-assets"}prepack 验证链:
发布包包含的文件
package.json files:{ "files": [ "Cargo.toml", "Cargo.lock", "dist/", "crates/", "!crates/**/.omx/**", "skills/", "prompts/", "templates/", "src/scripts/", "plugins/", ".agents/plugins/marketplace.json" ]}安装后脚本
package.json postinstall:{ "postinstall": "node -e \"const fs=require('fs');const p='./dist/scripts/postinstall.js';if(fs.existsSync(p))import(p).then(m=>m.main?.()).catch(e=>console.warn('[omx] Postinstall skipped: '+e?.message))\""}Postinstall 脚本在 npm install -g oh-my-codex 时运行,进行初始化和环境检查。非致命错误被捕获并警告,不阻止安装。
版本更新提醒
OMX 在启动时按节流策略检查 npm 更新:
伪代码(maybeCheckAndPromptUpdate):function maybeCheckAndPromptUpdate(): lastCheck = readLastUpdateCheckTime() if now() - lastCheck < THROTTLE_INTERVAL (24h): return
latestVersion = fetchNpmVersion("oh-my-codex") if latestVersion > currentVersion: prompt(`New version ${latestVersion} available. Run 'omx update' to upgrade.`)
writeLastUpdateCheckTime(now())用户也可以通过环境变量控制更新检查行为:
| 变量 | 值 | 效果 |
|---|---|---|
OMX_AUTO_UPDATE | 0 | 禁用启动时检查 |
OMX_AUTO_UPDATE | defer | 调度延迟更新,不提示 |
问题与规避
| 问题 | 后果 | OMX 的规避策略 |
|---|---|---|
| 技能同步覆盖用户编辑 | 用户自定义工作流丢失 | syncManagedContent 检测并保留用户编辑 |
| 插件版本与包版本不一致 | Codex 发现不兼容的技能 | verify:plugin-bundle 验证版本一致性 |
| 别名指向无效目标 | Agent 路由失败 | verify:native-agents 验证所有别名目标 |
| 叶子代理缺少守卫 | 无限递归子代理调用 | 验证每个叶子代理的守卫注入 |
| Postinstall 失败阻止安装 | 用户无法安装 | 非致命错误捕获,仅警告 |
| npm 发布遗漏关键文件 | 安装包功能不完整 | package.json files 字段显式声明 |
设计取舍
为什么选择插件布局 + npm 包双重分发?
OMX 同时通过 npm 包和 Codex 插件系统分发。
优势:
- npm 包:标准的 Node.js 分发,
npm install -g安装 - 插件布局:Codex 原生发现,自动注册 Hook 和技能
- 互补:npm 包提供 CLI,插件提供生命周期集成
代价:
- 双重维护:需要同步两个分发渠道的内容
- 版本一致性:需要验证 plugin.json 版本与 package.json 一致
- 安装复杂度:用户需要理解两种分发的关系
参考来源
- Oh-My-Codex v0.18.9 源码 —
src/cli/setup.ts(技能安装逻辑) - Oh-My-Codex v0.18.9 源码 —
plugins/oh-my-codex/(插件目录) - Oh-My-Codex v0.18.9 源码 —
src/catalog/manifest.json(技能 catalog) - Oh-My-Codex v0.18.9 源码 —
scripts/sync-plugin-mirror.js(插件镜像同步) - Oh-My-Codex v0.18.9 源码 —
scripts/verify-native-agents.js(Native Agent 验证) - Oh-My-Codex v0.18.9 源码 —
package.json(发布脚本)