插件与 marketplace · 打包分发扩展

插件把 skills、MCP 服务器、应用连接器和 hooks 装进一个目录,靠 marketplace 分发。本篇拆开 codex-core-plugins:清单的四个位置与两种格式、marketplace 的来源与策略、安装时复制进版本化缓存的流程,以及每个会话如何把启用的插件拆回各个扩展子系统。

作者 David更新于 第 38 篇(共 57 篇)

插件与 marketplace · 打包分发扩展

前面三篇讲的配置、技能、hooks,加上下一篇的 MCP,都能单独使用。插件是它们之上的分发单元:一个目录,一份清单,通过 marketplace 安装、启用、升级。实现集中在 codex-core-plugins(codex-rs/core-plugins/,去掉测试约 2.3 万行),共享的数据模型在 codex-plugin(codex-rs/plugin/)。

怎么用

插件目录里放 .codex-plugin/plugin.json 清单和 skills/、.mcp.json、.app.json、hooks/hooks.json 等组件;marketplace 是一份 marketplace.json,列出插件及其来源。命令行用 codex plugin add 名字@市场、codex plugin marketplace add 来源,TUI 用 /plugins。字段与操作见手册插件。

全景:安装一次,每个会话拆开用

图表加载中…

两条入口共用同一个 PluginsManager:codex plugin 命令行直接在本进程里构造它(codex-rs/cli/src/plugin_cmd.rs:731),TUI 的 /plugins 则经 app-server 的 plugin/list、plugin/install、marketplace/add 等方法调用。

清单:四个位置,两种格式

find_plugin_manifest_path 先看插件根目录的 plugin.json,只有它的 $schema 以 https://agent-plugins.org/schemas/ 开头才认作可移植的 Agent Plugins 格式;否则依次找:

pub const DISCOVERABLE_PLUGIN_MANIFEST_PATHS: &[&str] = &[
    ".codex-plugin/plugin.json",
    ".claude-plugin/plugin.json",
    ".cursor-plugin/plugin.json",
];

(codex-rs/exec-server-protocol/src/protocol.rs:49)

是符号链接的根目录清单直接不认。后三种统称 Legacy 格式,Claude Code 与 Cursor 的插件可以不改清单直接装。解析在 codex-rs/core-plugins/src/manifest.rs:skills、mcpServers、apps、hooks 里的路径必须以 ./ 开头、不能含 ..,否则这个字段被忽略并记警告;interface.defaultPrompt 最多 3 条、每条最长 128 字符。清单没写的组件走默认位置:skills/、.mcp.json、.app.json、hooks/hooks.json(codex-rs/core-plugins/src/loader.rs:67)。

两种格式的待遇不同,load_plugin 里分得很清楚:

    loaded_plugin.skill_discovery_mode = match loaded_manifest.format {
        PluginManifestFormat::Legacy => SkillDiscoveryMode::Recursive,
        PluginManifestFormat::AgentPlugin => SkillDiscoveryMode::DirectChildren,
    };
    // ...
    let (hook_sources, hook_load_warnings) =
        if loaded_manifest.format == PluginManifestFormat::AgentPlugin {
            (Vec::new(), Vec::new())
        } else {
            load_plugin_hooks(

(codex-rs/core-plugins/src/loader.rs:896)

Agent Plugins 格式的技能只认 skills/ 下一层目录,不加载 hooks,也不加载应用连接器;Legacy 格式递归扫描技能目录。Legacy 插件里 Claude Code 风格的 commands/ 斜杠命令,会在安装时被转写成技能,放进 .codex-plugin/migrated-command-skills/;frontmatter 没写描述、或转写后超过 4000 字节的命令直接跳过(codex-rs/core-plugins/src/command_migration/plugin.rs:25)。

marketplace:插件从哪来

marketplace 清单认四个相对位置:.agents/plugins/marketplace.json、.agents/plugins/api_marketplace.json、.claude-plugin/marketplace.json、.cursor-plugin/marketplace.json(codex-rs/core-plugins/src/marketplace.rs:20)。个人 marketplace 从家目录自动发现;用 codex plugin marketplace add 加的会写进用户配置的 [marketplaces.名字],Git 来源克隆到 $CODEX_HOME/.tmp/marketplaces/名字,本地来源原地引用。每个插件条目的来源是三选一:

pub enum MarketplacePluginSource {
    Local {
        path: AbsolutePathBuf,
    },
    Git {
        url: String,
        path: Option<String>,
        ref_name: Option<String>,
        sha: Option<String>,
    },
    Npm {
        package: String,
        version: Option<String>,
        registry: Option<String>,
    },
}

(codex-rs/core-plugins/src/marketplace.rs:128)

条目还带 policy.installation(AVAILABLE、INSTALLED_BY_DEFAULT、NOT_AVAILABLE)与 policy.authentication(ON_INSTALL、ON_USE)。管理员可以在 requirements 里设 [marketplaces] restrict_to_allowed_sources = true,再用 allowed_sources 列出允许的 Git 地址与 ref、主机名正则或本地路径,安装前由 MarketplacePolicy::validate_install 把关,已配置的插件也按同一策略过滤(codex-rs/core-plugins/src/marketplace_policy.rs:43)。

OpenAI 自己的精选目录有两种形态。用 ChatGPT 账号这类走 Codex 后端的方式登录、且功能开关 remote_plugin(默认开)打开时,远程目录生效,由后端提供 openai-curated-remote 等目录;远程目录不可用时,才在后台把 GitHub 上的 openai/plugins 仓库同步到 $CODEX_HOME/.tmp/plugins,作为本地的 openai-curated(codex-rs/core-plugins/src/manager.rs:723)。同步先用 git 拉,失败改走 GitHub HTTP API,再失败才考虑 ChatGPT 后端的导出包,而且导出包只用于首次引导:

                        // The export archive is a lagging backup path. Only use it to bootstrap a
                        // missing local curated snapshot, never to refresh an existing one.

(codex-rs/core-plugins/src/startup_sync.rs:142)

同步过程用文件锁防止多个 Codex 进程同时写。远程目录里的插件以压缩包下载,下载上限 100 MiB、解压后上限 512 MiB(codex-rs/core-plugins/src/remote_bundle.rs:32)。

安装:复制进版本化缓存

install_resolved_plugin(codex-rs/core-plugins/src/manager.rs:2223)的步骤:先把来源物化成本地目录(Git 克隆、npm 拉包或直接用本地路径),再交给 PluginStore 复制进 $CODEX_HOME/plugins/cache/市场/插件/版本/——先复制到同级临时目录并核对清单在复制过程中没被改动,再整体改名启用;新版本启用后删掉旧版本目录,同版本重装则先把旧目录挪去备份,替换失败可以回滚(codex-rs/core-plugins/src/store.rs:550)。清单的 name 必须与 marketplace 里的插件名一致。版本号取清单的 version,缺省为 local(Agent Plugins 格式缺省 1.0.0),精选目录的插件用仓库 commit 的前 8 位。最后调用 set_user_plugin_enabled,在用户配置写入 [plugins."插件@市场"] enabled = true。

加载时若同一插件目录下仍有多个版本,local 优先,否则取语义化版本最高的一个;插件自己的可写数据目录是 $CODEX_HOME/plugins/data/插件-市场。缓存是一份拷贝,改了插件源码必须重新安装才生效。

加载:每个会话把插件拆回去

PluginsManager::plugins_for_config(codex-rs/core-plugins/src/manager.rs:792)从生效配置里读出 [plugins] 表,经 marketplace 策略过滤,再并上远程安装的插件,逐个 load_plugin。结果按配置与登录身份缓存,最多留 8 份;加载期间登录状态变了,就放弃这次结果,同一账号的令牌刷新则重来一遍。

拆出来的四类组件分别交给已有的子系统,插件本身不另开一套执行路径:

  • 技能:插件的技能根目录进入技能扫描,名字加上 插件名: 前缀。
  • MCP 服务器:清单决定怎么启动,用户配置只能在 [plugins."插件@市场".mcp_servers.服务器] 下覆盖开关、认证与工具策略——PluginMcpServerConfig 的注释写明它“有意不含传输设置”(codex-rs/config/src/types.rs:1015)。
  • Hooks:作为 PluginHookSource 交给 hooks 引擎,命令能用 PLUGIN_ROOT、PLUGIN_DATA,但照样要逐条信任。
  • 应用连接器:.app.json 声明的连接器 ID,并入会话的应用列表。

用户在输入里用 @ 链接到某个插件(结构化提及的路径形如 plugin://插件@市场)时,build_plugin_injections 会生成一段开发者提示,列出该插件在本会话可用的 MCP 服务器、应用和技能前缀,上限 4 KiB(codex-rs/core/src/plugins/render.rs:8)。

和《从 LLM 到 Coding Agent》对照

那本书没有插件这一层,它的 MCP 一章讲的是用标准协议把外部工具接进来。插件是再往上的一层:不新增能力,只负责把技能、hooks、MCP 配置打包、分发、按版本缓存。与 Grok Build 相比,Grok Build 按作用域判定整个插件是否受信、未受信的插件只露出元数据;Codex 把“安装”与“运行代码”分开——装进缓存不等于信任,插件带来的每条 hook 仍要单独审阅,MCP 服务器的开关与工具审批也留在用户配置里。


上一篇:Hooks · 在关键节点插入你的脚本 · 下一篇:MCP 客户端 · 传输与 OAuth 登录

本页目录