一开始做 MoeKoe Music 插件生态的时候,还没有在线插件市场这个功能
后来在社区的建议下便有了这个插件市场 增加官方/社区插件仓库以扩展产品功能
插件可以由社区开发,那插件市场应该怎么管?
最直接的做法当然是把所有插件源码都收进一个大仓库里。但这个方案越想越别扭。每个插件都有自己的作者、自己的发布节奏、自己的构建方式。把它们全塞进官方仓库,不仅维护成本高,也会把责任边界搞得很模糊。
所以这个仓库最后没有被设计成“插件源码仓库”,而是变成了一个“插件登记与索引仓库”。它只做几件事:
- 接收插件上架、更新、下架和举报申请
- 自动做一轮基础校验和静态识别
- 把人工审核通过时的插件快照记录下来
- 维护客户端可以读取的
plugins.json - 顺手把 README 里的插件列表更新出来,方便人看
一个可追溯的登记簿:谁提交的、审核的是哪个版本、下载地址是什么、是否有网络或文件权限,都落在一份清楚的数据里。
市场索引,而不是源码仓库
仓库根目录最重要的文件是 plugins.json。它就是插件市场真正消费的数据源。
{
"id": "custom-app-background",
"name": "自定义背景图",
"description": "为MoeKoe Music提供自定义背景图能力,支持透明度调节。",
"iconUrl": "...",
"version": "1.0.0",
"minversion": "1.6.1",
"...":"....",
"buildRequired": false,
"networkAccess": false,
"fileAccess": false,
"binaryContent": false,
"snapshot": {
"iconUrl": "...",
"repository": "MoeKoeMusic/custom-app-background-plugin",
"commitSha": "dbf72d38c8cf6b1d1cefdf8ce15798d565678995",
"downloadUrl": "hxx.zip",
"release": null
}
}
我不想让市场记录永远指向插件仓库“当前最新代码”。因为作者今天提交的代码,和审核那天看到的代码,可能已经不是同一份东西了。插件市场真正应该承认的是:审核通过时的那个版本。
所以这个项目里面有一个核心原则:
上架的不是一个会漂移的仓库地址,而是一个已经锁定的快照。
对于不需要编译的插件,快照锁到默认分支当时的 commit。对于需要编译的插件,快照锁到对应的 Release tag 和发行附件。这样后面真出了问题,也能回头知道当时到底审核了什么。
Action 先跑,人最后拍板
用户提交 Issue 时,需要选择:
- 是“新上架”还是“更新插件”
- GitHub 仓库地址
- 安装前是否需要编译
Action做自动校验,然后把结果写回 Issue 评论,同时给 Issue 打上 check-passed 或 check-failed 标签。
自动化负责整理事实,人负责做最终判断:
- 用户用 Issue 模板提交插件
- Action 解析 Issue 表单
- 校验仓库、manifest、版本、作者权限
- 锁定插件快照
- 自动评论校验结果
- 维护者人工查看
- 维护者用
Close as completed关闭 Issue - 另一个 Action 读取校验结果,生成更新
plugins.json和README.md的 PR - PR 合并后,插件正式进入市场索引
这里的“关闭方式”也被利用起来了。如果 Issue 是 Close as not planned,脚本不会执行入库。也就是说,GitHub 原生的 Issue 状态就被拿来当成了审核按钮。
快照机制
如果用户说“安装前不需要编译”:
- 读取仓库默认分支
- 拿到当前 commit sha
- 用这个 commit 去读取
manifest.json - 生成固定 commit 的源码 zip 地址
- 下载 zip,解压后做权限扫描
- 生成
repository-tree类型快照
如果用户说“需要编译”:
- 找最新可用 Release
- 取 Release 里的第一个附件
- 下载发行附件
- 解压附件并读取其中的
manifest.json - 生成
release-asset类型快照
这两个路径解决的是同一个问题:不管插件源头怎么发布,最后都要得到一份可审核、可下载、可追溯的快照,不会因为最新仓库的变动而导致用户下载的插件非审核时的.
权限识别
安全审核这件事,不能只靠脚本拍脑袋。但脚本可以先帮维护者把风险点标出来:
- manifest 里声明了什么权限
- 源码或发行包里是否出现了典型 API 或可执行文件
它不是只给出“有风险”这几个字,而是会告诉你为什么判断有这个能力,比如:
- manifest 声明了网络访问
- 源码里用了
fetch - 发现了
.exe、.dll、.node之类可执行内容 - 源码里用了
localStorage或indexedDB
这当然不是完美的安全扫描。它会有漏报,也可能有误报。但它的定位不是取代人工审核,而是把维护者最应该多看一眼的地方提前圈出来。
它不是“审判官”,更像“标注笔”。
隐藏快照数据
校验完成后,会在 Issue 下写一条评论。评论里除了人能读懂的校验结果,还有一个隐藏的 payload。
它会生成类似这样的 HTML 注释:
<!-- plugin-publish-snapshot:base64... -->
这个设计挺巧的。因为 GitHub Actions 的不同 workflow 之间不是一直共享内存的。上架校验发生在 Issue 创建或编辑时,真正入库发生在 Issue 被关闭时。中间可能隔了几小时甚至几天。
那关闭时怎么知道当初校验通过的是哪个快照?
答案就是:从 Issue 评论里读回来。
这让 Issue 评论不只是“提示信息”,还变成了一份轻量的审核记录。它不需要额外数据库,也不用搭服务,直接借用了 GitHub 本身的记录能力。
入库动作
真正写入 plugins.json 的脚本是 scripts/publish-plugin-close.js。
它先做几道门:
- 当前事件必须是 publish 类 Issue
- Issue 必须是
state_reason === 'completed' - 关闭 Issue 的用户必须是仓库维护者
- 校验评论里的 payload 必须是
check-passed - payload 必须有锁定的
downloadUrl
这几道门保证了一个事情:普通用户不能靠关闭 Issue 或伪造流程把插件写进市场。
这里有一个细节:更新插件时,会保留原作者,不会因为别人提交更新 Issue 就改掉作者字段。同时更新必须由原作者本人提交,仓库地址也必须和现有记录一致。
新的或更新后的插件会放到列表最前面。这个选择也挺符合插件市场的直觉:最新通过审核的内容排在前面,用户和维护者都更容易看到最近变化。
最后用当前 plugins.json 生成 Markdown 表格替换 README 中的插件列表。
下架和举报
它的流程和上架很像:
- 用户创建下架或举报 Issue
plugin-moderation-validate.js校验插件是否存在- 如果是作者主动下架,校验提交人是否为作者
- 维护者人工审核
- 维护者用
Close as completed关闭 plugin-moderation-close.js把插件状态改成delisted- 自动生成 PR 更新
plugins.json和 README
AI 审核
项目里还有一个 publish-plugin-ai-audit.js,它会对插件快照做一次 AI 静态审查。
这个脚本的思路是:
- 下载同一个快照
- 选择最多 24 个候选文件
- 优先看
manifest.json、package.json、入口文件、网络/文件/存储相关文件 - 把内容发给 AI 接口
- 要求 AI 返回固定 JSON
- 把审查结果写回 Issue 评论
这里我觉得比较好的地方是:AI 审核没有被设计成“最终裁判”。它只是给维护者多一份参考。
这比“AI 说安全所以自动上架”靠谱得多。
为什么不用数据库或后台服务?
这个项目最有意思的地方,其实不是代码多复杂,而是它尽量不引入额外系统。
它把 GitHub 自带的东西用到了比较完整:
- Issue Form 当申请表
- Issue 评论当审查反馈和快照记录
- Label 当自动校验状态
- Close reason 当人工审核信号
- Pull Request 当数据变更入口
plugins.json当市场索引- README 当公开展示页
- Git 历史当审计日志
所以它不需要一个后台,不需要数据库,也不需要单独做管理面板。对于一个开源音乐播放器的插件市场来说,这个复杂度刚刚好。
ps: GitHub Actions真的好用,包括我之前的 阿珏のBlog 的国际化之路
结尾
回头看这个项目,它并不是那种“架构很重”的插件平台。它更像一个长得很朴素、但每个步骤都能追溯的登记系统。
用户通过 Issue 提交,Action 自动检查,维护者人工确认,PR 合并生效。所有关键动作都留在 GitHub 上,所有市场数据都落在 plugins.json 里。出问题时能查,更新时有记录,下架时也不抹掉历史。
它没有试图一步到位做成一个完整后台,而是先把插件市场最核心的信任链路搭起来。
一个社区插件生态,真正需要的第一件事可能不是华丽的页面,而是一套大家都能看懂、能信任、能复盘、能继续改进的流程。
这个仓库 MoeKoeMusic-Plugins 就是这样来的。
当然以上这些都不是一步到位的,是一步一步更新完善到现在的结果的






人尽皆知,第一个上架的插件是背景插件
那第一个出现,甚至直到现在都没上架的插件是什么?
~~应该不可能是我的插件吧(~~
@ 晚梦 明知故问
@ 阿珏 我还以为在我之前就有人写了插件来着