cnb-rs extensions
cnb-rs 的 extension 系统让你用任何语言(Rust / Go / bash / Python ...)写出第三方子命令,安装后就能像内置命令一样调用。
当前状态(Phase 3.1):dispatch 骨架已 ship — 手工放 binary 到 extensions 目录即可工作。 install / upgrade / remove / list 子命令在 Phase 3.2+ 实现, 完整设计文档见仓库
.windsurf/docs/cnb-extension-design.md。
工作原理
$ cnb-rs hello world
│
├─[1] clap 解析顶层 args;"hello" 不在内置命令集
├─[2] fallback:clap external_subcommand 把 ["hello", "world"] 给 cnb-rs main
├─[3] cnb-rs 查找 $EXTENSIONS_DIR/cnb-hello/cnb-hello[.exe]
│ ├─ 找到 → Command::spawn(bin).args(["world"]).status()
│ └─ 找不到 → 报错 + 提示 'cnb-rs ext install cnb-hello'
└─[4] 子进程退出码透传,stdin / stdout / stderr 不缓冲安装位置
| 平台 | extensions 根目录 |
|---|---|
| Linux | $XDG_DATA_HOME/cnb/extensions 或 ~/.local/share/cnb/extensions |
| macOS | ~/Library/Application Support/cnb/extensions |
| Windows | %LOCALAPPDATA%\cnb\extensions |
可通过环境变量 CNB_EXTENSIONS_DIR 覆盖(绝对路径;测试 / dev 场景常用)。
命名约定
仓库名 / extension 命令名必须以
cnb-开头:例如cnb-stats/cnb-prom-export/cnb-todo用户调用时不带
cnb-前缀:cnb-rs stats实际查找cnb-stats/cnb-stats[.exe]单 extension 布局:
extensions/ └── cnb-stats/ ├── cnb-stats[.exe] # Unix 无后缀,Windows 自动 `.exe` ├── manifest.toml # Phase 3.4 起 install 自动写 └── ... # extension 自带资源
extension 子进程的环境
cnb-rs 在 dispatch 时向 extension 子进程注入以下环境变量:
| 变量 | 含义 |
|---|---|
CNB_BIN | 当前 cnb-rs 可执行文件的绝对路径,方便 extension 反向调用 $CNB_BIN api /...(避免 PATH 中找错版本) |
CNB_EXTENSION_NAME | 当前 extension 命令名(不含 cnb- 前缀,如 stats) |
CNB_DOMAIN | 解析后的目标 CNB 域名(默认 cnb.cool) |
CNB_REPO | 解析后的仓库路径(解析失败时不设置,避免误传空值) |
Token 不主动注入:让 extension 通过 $CNB_BIN api ... 间接使用 token,或继承父进程已有的 CNB_TOKEN 环境变量(与 gh extension 行为一致)。
手工安装 extension(Phase 3.1 临时方案)
在 Phase 3.4 的 cnb-rs ext install 子命令 ship 之前,可以手工放 binary:
# 1. 拿到 binary(自己 build 或下载 release)
$ cargo build --release --bin my-extension
$ cp target/release/my-extension cnb-hello
# 2. 放到 extensions 目录
$ mkdir -p ~/.local/share/cnb/extensions/cnb-hello
$ mv cnb-hello ~/.local/share/cnb/extensions/cnb-hello/
# 3. 调用(cnb-rs 会自动 dispatch)
$ cnb-rs hello worldWindows 类似(路径换为 %LOCALAPPDATA%\cnb\extensions\cnb-hello\cnb-hello.exe)。
用 5 行 bash 写个 extension
#!/usr/bin/env bash
# extensions/cnb-whoami/cnb-whoami
set -e
"$CNB_BIN" api /user --jq .username赋可执行权限后 cnb-rs whoami 即输出当前登录用户名。
Phase 3.1–3.4 仅支持 binary extension(直接可执行的文件,含编译产物或 shebang script)。完整的 interpreted script extension(含
kind = "script"标记 + 跨平台 shell 启动)在 Phase 5 实现。
Phase 3.4 之后,install 会在每个 extension 目录里落地 manifest.toml,记录 host / owner / repo / tag / kind / sha256 等元数据,供 list / upgrade 使用。手工放的旧 binary 没有 manifest 也能 list / exec / remove,仅 version / repo 显示为 -(向前兼容)。
state.toml 与 24h 后台 check
Phase 3.6 引入全局 state 文件 $EXTENSIONS_DIR/state.toml(不在某个 extension 目录内),记录:
[updates]:上次全量 check 的时间戳 + 待提示的「有新版本」消息列表[extensions.cnb-<name>]:每个 extension 最近一次 check 拉到的 latest release tag
两者职责严格分离:manifest 是安装凭证(不可变),state 是运行时缓存(可变)。
触发时机
两类调用会在前台进程内同步走 gate(< 5ms)后 spawn detached 子进程 在后台跑实际的 N × API check(Phase 6 Task C 把原 sync in-process 换成 detach):
cnb-rs ext list调用时- 任意 extension dispatch 路径(
cnb-rs <ext-name> ...,如cnb-rs stats)启动时
子进程跑的是 hidden subcommand cnb-rs ext _check-upgrades(不出现在 --help),把新版本消息加进 state.updates.pending_notices 队列。前台 cnb-rs 不再 block。
其它 cnb-rs 命令(如 auth login / repo list)不会触发 check,零额外开销。
详细 detach 机制(Windows CREATE_NO_WINDOW | DETACHED_PROCESS / Unix process_group(0) / parent 抢先 pre-write state 防 retry storm)见 cnb-rs ext upgrade 文档。
提示打印
任何 cnb-rs 命令 main entry 早期调 print_pending_notices(),读 state 把队列输出到 stderr 后清空并写回:
$ cnb-rs auth status
💡 cnb-stats 有新版本可用(v0.1.0 → v0.1.1),运行 'cnb-rs ext upgrade stats' 升级
(设 CNB_NO_EXTENSION_NOTICE=1 禁用此提示)
✓ 已登录 user@cnb.cool禁用
设以下任一环境变量即可永久禁用 check 和提示:
CNB_NO_EXTENSION_NOTICE=1:明确禁用 extension 提示CI=true:CI 平台通用约定(GitHub Actions / GitLab / CNB Pipeline 等默认设)
state IO 失败(磁盘满 / permission / TOML corrupt)在打印路径上被 silently 吞掉,不会令 cnb-rs 主命令失败。
命名冲突
extension 不能覆盖任何内置命令(如 auth / issue / pr 等)。如果你试图安装一个与内置命令同名的 extension:
cnb-rs ext install会拒绝安装并提示用户重命名仓库(Phase 3.4 已 ship)- 用户仍可通过显式
cnb-rs ext exec <name>调用(Phase 3.3 已 ship)
内置命令名清单从 clap
Cli::command().get_subcommands()动态获取,避免维护双份。
Roadmap
| Phase | 内容 | 状态 |
|---|---|---|
| 3.1 | dispatch 骨架 + extensions 目录约定 + env var 注入 + 内置 helper(list_installed / builtin_commands_from) | ✅ 已 ship |
| 3.2 | cnb-rs ext list 子命令(alias ls) | ✅ 已 ship |
| 3.3 | cnb-rs ext exec 显式 dispatch(避命名冲突) | ✅ 已 ship |
| 3.4 | manifest.toml + cnb-rs ext install + SHA256 校验 + 冲突检测 + list 表格升级 | ✅ 已 ship |
| 3.5 | cnb-rs ext remove(alias rm / uninstall) | ✅ 已 ship |
| 3.6 | cnb-rs ext upgrade + 24h 后台 check(Phase 6 Task C 改 detached subprocess)+ state.toml + CNB_NO_EXTENSION_NOTICE | ✅ 已 ship |
| 4 | 3 个官方 extension 仓库孵化(cnb-chat / cnb-stats / cnb-stars) | ✅ 已 ship |
| 5 | registry + cnb-rs ext search + cnb-rs ext create 脚手架 + interpreted script + Local kind + ext registry validate | ✅ 已 ship |
| 6 | Task A --web + min_cnb_rs_version / Task B ext registry refresh + daily CI / Task C detached upgrade check / Task D ext browse TUI | ✅ 已 ship |
完整设计与时间线见仓库 .windsurf/docs/cnb-extension-design.md。
参见
- cnb-rs ext — extension 管理子命令总入口
- cnb-rs api — extension 反向调用 CNB OpenAPI 的「生态基石」命令
- cnb-rs completion — shell 补全脚本生成(extension 命令名暂不在补全列表,Phase 3.4 后改进)