跳转到内容

插件系统与发布流水线: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 shim

plugin.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_UPDATE0禁用启动时检查
OMX_AUTO_UPDATEdefer调度延迟更新,不提示

问题与规避

问题后果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(发布脚本)