插件提交与审核(开放生态)
English: plugin-submission.en.md
Minibar 的插件是 npm 包:基座从 npm 源或就近的镜像下载,并按签名索引里锁定的 包名 + 版本 + sha256 校验(10-backend.md §2b)。索引(registry.json)托管在 Vercel(https://minibar.084817.xyz,服务端代码在 server/)。内置插件来自 Sources/BundledMiniApps/Catalog/,由 CI 发布为 @minibar-app/*;社区插件由作者发布到 npm,再在本仓库 registry/plugins/<插件 id>/ 留一份源码副本供审核与索引构建。App 的市场会给社区插件打上"社区"标签,并显示 npm 的下载量。
1. 提交流程
- 用
minibar-cli init或照着docs/examples/写好插件(含package.json:version等于 manifest 版本、keywords含minibar-plugin、没有scripts),本地跑通:node tools/jsharness/run.js <插件目录> # 模拟宿主渲染 node tools/jsharness/scan.js --strict <插件目录> # 静态扫描(registry 规则,含 package.json)在 Mac 上用"设置 › 插件 › Install from Folder…"或开发者模式真机验证。
- 发布到 npm:
npm publish --access public(任何名字都行,推荐minibar-plugin-<名字>)。索引锁定的是精确版本和整包哈希,发布后同一版本的内容不可再改,更新就发新版本。 - 把插件源码放到
registry/plugins/<插件 id>/(目录名必须等于 manifest 的id),提交 PR(仓库私有期间通过官网支持页说明的渠道投递)。PR 描述必须回答:- 插件做什么、数据来自哪里;
- 每一项权限的用途(尤其
system、apps、files、clipboard); allowedHosts里每个域名的用途;"*"需要单独说明为什么不能列举域名;- 是否包含第三方代码(库、压缩产物),来源与许可证。
- CI(
.github/workflows/plugin-submission.yml)自动跑:静态扫描(--strict)、mock 宿主渲染、索引打包预演。全绿是人工审核的前提。 - 维护者按 §3 清单审核,并核对 npm 上发布的 tarball 与 PR 里的源码一致(
scripts/build_registry.py --resolve-from https://registry.npmjs.org会下载已发布的包、校验 manifest 并计算哈希)。通过即合并;合并到默认分支后 Registry 工作流部署索引,App 在下一次刷新时看到它(缓存 6 小时,市场窗口打开时立即刷新)。 - 更新:发布新版本到 npm,改
version(semver)再提 PR;App 会提示更新,用户可开启自动更新。
2. 静态扫描查什么(tools/jsharness/scan.js)
| 类别 | 规则 | 结果 |
|---|---|---|
| manifest | id 反向域名、目录名 = id、version semver、name ≤ 32、description ≤ 200、未知权限、network 无 allowedHosts、allowedSchemes 含禁止 scheme | 错误 |
| manifest | 无 author / homepage(--strict)、allowedHosts: ["*"]、申请 system / apps / files / clipboard | 错误 / 警告(审核重点) |
| 文件 | 扩展名白名单(与 PathSafety.allowedExtensions 一致)、符号链接、> 8 层目录、单文件 > 5 MB、总计 > 20 MB、> 200 个文件 | 错误 |
| 脚本 | 直接触碰 __host / __runtime / __meta、eval、Function 构造、require / import()、XMLHttpRequest / WebSocket、Node process、浏览器存储 | 错误(无正当用途) |
| 脚本 | 入口未调用 defineMiniApp、脚本 > 1 MB | 错误 |
| 脚本 | 超长行(压缩/混淆)、String.fromCharCode 长链、\x 转义长串、fetch / atob / setTimeout(运行时不存在)、读剪贴板、源码里的 http 地址或不在 allowedHosts 的域名 | 警告 |
| Web 卡片 | <script src="http…"> | 错误;其它网络资源引用为警告 |
扫描器只是第一道门:它把"不可能合法"的东西挡在外面,把"需要人看"的地方标出来。
3. 人工审核清单
审核者逐项打勾后再合并:
- [ ] 只做它说的事:读完
main.js(通常 < 300 行),每个ctx.http请求的域名和用途都能对上 PR 描述;没有把用户数据(剪贴板、日历、文件内容、位置)发往任何域名,除非插件的核心功能就是如此并且在描述里写明。 - [ ] 权限最小:申请的每项权限在代码里都真的用到;
system/apps的调用都在 action 里并且对应一个用户能看懂的按钮;files只读用户选的内容;clipboard读取有明确理由。 - [ ]
allowedHosts精确:没有"*";没有与功能无关的域名;没有短链接或跳转服务。 - [ ] 没有混淆:代码可读;第三方库(若有)是未经修改的已知版本并注明来源与许可证;压缩产物须附未压缩源码或可复现的构建方式。
- [ ] UI 诚实:头条和卡片不冒充系统提示;按钮文字说明会发生什么;不使用 Apple 或他人商标作为图标。
- [ ] 稳定:mock 宿主下离线渲染不报错;网络失败有降级文案;
refreshInterval≥ 60 除非有充分理由(≥ 5 秒是硬限制)。 - [ ] 描述与本地化:
name/description清楚、没有错别字;如果提供了localizations,译文与原文一致。 - [ ] 来源可查:
homepage指向可访问的仓库或页面,作者能被联系到。
拒绝的常见原因:allowedHosts: ["*"] 且无法说明理由;向第三方发送个人数据;混淆代码;与内置插件完全重复且无改进。
4. 下架(revoke)与演练
registry/revoked.json:
{ "revoked": [ { "id": "com.example.bad", "reason": "Sends clipboard contents to a third party" } ] }
合并后 Registry 工作流重新发布 registry.json;每台安装了该插件的 Mac 在下一次刷新 registry 时(≤ 24 小时,打开市场立即)会:
- 在市场里把它标为「已从市场移除:<原因>」并禁止安装;
- 自动暂停已安装的副本(卡片显示原因,用户可以查看源码、卸载,或明知风险后重新启用);
- 写一条日志。
从 revoked.json 删掉该条目后,下一次刷新 registry 时被暂停的副本会自动恢复(只恢复因下架而暂停的;崩溃、超时等原因的暂停不受影响,用户自己的启用/停用选择保持不变)。
演练(每个版本发布前做一次,确认链路没坏):
- 在分支上把
app.minibar.community.worldclock加进revoked.json,node server/dev.mjs --build构建并在本机服务(curl http://127.0.0.1:8000/registry.json确认revoked含它;只有本机回环地址允许明文 http,本机 registry 不签名,市场显示"未验证来源"); - 在 Mac 上把设置 › General 的 registry 地址改为
http://127.0.0.1:8000/registry.json,打开市场:World Clock 显示已移除,已安装的副本被暂停; - 把地址改回正式 registry,确认恢复;
- 不要把演练分支合并。
4a. 不想进官方源?自己开一个外部源
官方源只收审核过的插件。团队内部、私有或实验性插件可以放在你自己的源里:按 03-plugin-api.md §11 生成并托管 registry.json,使用者在「设置 › 市场 › 外部源」添加地址即可。外部源的插件在市场里带来源标签、显示为未签名,但安装校验、沙箱与权限流程与官方插件完全相同。
5. 维护者侧的 registry 设置
- 托管在 Vercel 项目
minibar-registry;registry.yml用VERCEL_TOKEN/VERCEL_ORG_ID/VERCEL_PROJECT_ID三个 secret 部署(默认分支 → 生产,其它分支 → 预览)。详见10-backend.md§5。 MINIBAR_REGISTRY_KEYsecret 存 Ed25519 私钥(scripts/sign-registry.sh keygen),公钥嵌在 App 的RegistryTrust里;官方域名的 registry 未签名时 App 会拒绝,其它来源未签名显示「未验证来源」。--featured控制市场首页推荐位。