给Cellium Agent做了一个组件市场
组件市场上线了:https://components.cellium.top
从"我打算做一个"到"它真的能用",中间隔的不是代码,是我思维的转变。
我本来想写一个后端
这站自带登录,所以想做组件市场的时候,第一反应很自然:写个后端的提交流程和审核流程,存储再挂个对象存储桶。
用户登录、上传 zip、填名称描述、进审核队列、审核通过落库、再给个下载接口。数据库、对象存储、审核后台、权限控制,一样都不能少。方案在脑子里越画越沉。
而且我自己也心虚:组件本来就该放在作者的 GitHub 仓库里,我这边说到底只是个"目录",凭什么替人托管代码?审核还得单独做一套——用 Agent 审核吗?
但当时我确实没想到别的路。
Mr.Lee:"你用静态网站不就能做吗?"
在群里跟 Mr.Lee 聊起这个后端方案,他听完先反问了一句:你要的不就是一个列表加详情页?静态网站不就能做吗?
我还没消化,他又补了一句:提交也不用你做,发个 PR 就行。
我当时的反应是:一头雾水。
PR 不是改代码用的吗?组件提交是"业务",这俩怎么接得上?
他给我捋了一遍:你要的到底是什么?账号、审核、版本记录、自动校验、变更历史——GitHub 全都有。作者 Fork 注册表仓库,在 components/<slug>/ 下加一个 meta.json,发 PR,你 review,CI 校验,合并。这就是完整的提交流程。
他还顺手甩了两个例子:NoneBot 的插件就是这么收的,提交就是往 nonebot2 仓库发 PR(https://github.com/nonebot/nonebot2/pull/4239),https://nonebot.dev/store/plugins 展示的就是这些 PR 里的插件。轴中在旁边补了一句更精炼的总结:"添加靠 PR,合并时顺便审了。"
我才反应过来——我一直在想"我要做一个系统",却没想过"我要的这几样能力,早就有人做好了"。npm 这么发包,Homebrew 这么收 formula,NoneBot 这么收插件,凭什么轮到我发明一个上传表单?
安装包也不用我管:作者把 zip 发到自己仓库的 Release,meta.json 里填下载直链。注册表仓库只放 JSON,一行源码都没有。
后端?一行没写。
后来我复盘过:为什么没想到?大概是"做登录"这个动作太熟悉了,熟悉到成了本能。跳出来看一眼需求本身,才发现一半的复杂度是我自己吓自己加上去的。
然后是漫长的"第一次"
方向有了,剩下的全是细节,而细节最耗人。
Mr.Lee 不只是给了方向。市场里第一个组件——cellium-vision,视觉看图,把图片喂给 OpenAI 兼容的多模态模型——不是他手敲的。他有只 bot,叫 Tlipoca,也是拿 Cellium Agent 框架做的;他跟 Tlipoca 说了一嘴,bot 把组件包写好了,他自己 Fork、改 meta.json、提 PR。
也就是说:我做的 Agent 组件市场,第一个组件是另一只 Agent 写出来的。我盯着那个 PR 页面时心情挺复杂,但更多是踏实——流程真能走通,连"写组件"本身都已经不是瓶颈了。
第一次嘛,坑照踩。
中间有一回 CI 红了。那个 PR 把 meta.json 放在 components/cellium-vision/,里面 slug 却写着 vision,构建脚本直接报错,Pages 部署也跟着挂。这个错是 Mr.Lee 陪我看的。读 CI 日志其实没那么难,但第一次红的时候人是懵的,旁边有人说一句"就这,改个名",性质完全不一样。
还有一次,是他提 PR 时的连续吐槽,直接把我文案问题掀了:
"
source.commit听起来像哈希值,安装包直链听起来像链接,这到底要填链接还是哈希值?"
"npm run validate本地通过——这是啥?"
问得太好了。答案其实很简单:两种登记方式二选一——要么把 zip 发到自己 Release、填下载链接;要么告诉构建机去哪个仓库的哪次提交编号打包。但我把这两个字段并排列在模板里又不解释,谁看谁懵。
npm run validate 也是。当时构建脚本还是 Node 写的,它就是提 PR 前在本地先跑一遍校验:调 node scripts/build.mjs --check,按 CI 同一套规则检查所有 meta.json(目录名和 slug 是否一致、版本号是不是 1.2.3 三段式、日期格式、字段长度),并且会真的访问一次你的直链确认能打开;断网就用 npm run validate:offline,跳过网络检查。我却把它当成人人都懂的黑话写进了清单。
于是 PR 模板里多了一节"术语速查",用人话解释直链、提交编号和该填哪个。这条改进不是我想到的,是被问出来的。
还有些细节是我后来一个个抠的:
- Pages 镜像根路径 404,我一度以为坏了。它其实只产出
data/index.json、data/c/<slug>.json这类数据文件,没有index.html,本来就是给第三方抓数据的公开副本;真正对外的是 components.cellium.top。 - 详情页原来写
market install <slug>,看着像条命令,我差点也信了。查代码才发现 Agent 里根本没有这个命令,真实入口是component.market_install(slug="...")。现在页面显示的已经是能直接用的调用。 - 手机上提交按钮文字换行被固定高度裁掉一半、代码块把整页撑出横向滚动……不疼不痒但特别磨人。
顺手把 Agent 那头也接上了
市场这头通了,Agent 怎么"知道"市场上有什么、怎么装?这部分改动在 component_builder.py,Agent 内置的组件管理工具。
改之前它只管本地:生成组件、列组件、从本地 zip 或文件夹安装。改之后加了三个命令,串成一条链路:
market_search:读市场的data/index.json,按关键词、分类、tags 在本地过滤后分页返回;每个结果带installed标记,Agent 一眼知道你装没装过。索引有 5 分钟内存缓存,和服务器 cron 的发布节奏对齐。market_info:读data/c/<slug>.json,拿回最新版本、安装包地址、校验值、README,并直接给出"该怎么装"的参数。market_install:按元信息里的download直链下载(Git 拉取模式登记的组件没有直链,就回落到市场自建的pkg/<slug>-<version>.zip),边下边算 sha256。元信息里带 sha256 / size 就强校验,对不上直接拒绝——说明一句:直链模式构建时只发一次 HTTP HEAD,只回填 size,不下载不算 hash,所以这类组件通常只有大小校验;Git 拉取模式由构建机打包,才会带 sha256。校验过后复用本地安装的审查流程:检查是否继承BaseCell、语法、依赖、危险调用告警,装进components/,热重载生效。
没有引入任何新的安装逻辑。审查那一套是现成的,市场安装只是把"包从哪来"从本地路径换成了网络下载。
用户的感受就是:去市场逛一圈,Agent 搜一下、装一下,完事。中间那些 JSON、zip、校验,他完全不用知道。
市场地址还能用环境变量 CELLIUM_COMPONENTS_BASE 换,域名出问题可以指到镜像上。
后来我把构建脚本换成了 Python
市场跑起来之后,我又回头收拾了一次构建链。注册表这边的构建其实只干四件事:读文件、校验、发个 HTTP 探活、按提交打 zip。这些事 Python 标准库全包了,根本不需要 Node 那一套依赖。
于是:
- 构建脚本从
build.mjs换成了scripts/build.py,只用标准库(zipfile、tarfile、hashlib、urllib) - 校验规则、直链探活、Git 拉取导出、
sha256/size回填,行为一模一样 - CI 改用
setup-python,validate和 Pages 都跑同一个脚本 - 服务器那边只要
bash+python3,连zip/tar命令都不用装了 - 作者提 PR 前自查的命令,也从
npm run validate变成了python scripts/build.py --check——装了 Cellium Agent 就已经有 Python,不用为了提交一个组件再装 Node
前端打包仍然要用 Node(Vite),但那是我这边一次性的事,跟提交组件的作者没关系。
最后长这样
两个仓库 + 一台服务器:
- 注册表仓库:只放
meta.json,提交就是提 PR,CI 跑python scripts/build.py --check把关 - 前端仓库:React + Vite 的静态站,运行时 fetch 两个同源 JSON
- 服务器:cron 每 5 分钟跑
deploy.sh——拉注册表、拉前端、构建索引、写进前端dist,nginx 直读;只有前端源码更新时才重新打包,纯数据更新不动前端
账号是 GitHub 的,审核是 PR review,版本是 commit 历史,分发是 Release 直链。我要维护的,就一个定时脚本。
| 我原来准备写的 | 后来借用的 |
|---|---|
| 用户登录 | GitHub 账号 |
| 上传表单 | meta.json |
| 审核后台 | PR review |
| 表单校验 | CI |
| 版本历史 | commit |
| 文件托管 | 作者自己的 Release |
还有个人
轴中 Axlewire,技术上的事他没怎么插手,但他在群里负责点赞、接话、活跃气氛。
气氛担当真的有用。做东西的人都懂,中间有一段纯粹的枯燥期——改 CI、修文案、调手机样式,没有新功能,全是补丁。那段时间每次我扔个截图,轴中就在那丢个 QQ 小表情点赞。没人看的时候是真容易头大;有人在旁边看着,就还能再写两小时。
Mr.Lee 是把我从歪路上拽回来的人。一句"你用静态网站不就能做吗",省掉的不是一个后端,是一整套我本不该维护的东西。而且他不是讲完就走:第一个组件是他让 Tlipoca 帮写、自己亲手提的 PR;PR 模板的歧义是他用提问抓出来的;CI 报错是他陪我看的。
这个市场是群友抬出来的,一点不夸张。
最后
如果你也想做类似的东西,建议就一条:先别想"我要写什么",先想"我要的到底是什么"。
把需求拆成账号、审核、版本、校验、分发这几个词,然后挨个问:这世界上,有没有人已经做得比我好了?
这个问题在我这里的答案是 GitHub,而且它做得好得不止一点。NoneBot 早就这么干了,我只是终于被人拽到了这条路上。
还有一条附赠:别怕把模板写得啰嗦。你觉得理所当然的名词,第一次来的人会一句句问出来。与其等他们问,不如自己先当一次小白——Mr.Lee 那两句吐槽,比我自己检查三遍都管用。