根因:启动器的运行时解析第三顺位是开发检出 ~/Dev/ant/ant-copilot,同事机器没有该目录 → 插件装上、MCP 起不来;而私有 registry 匿名读返回 401,走 npm 安装等于要求每台机器都配 Gitea token。 改法:① 打包产物随插件分发(plugins/<产品>/runtime/,3.96MB 自包含单文件 + manifest 记录来源版本/提交/大小/sha256);② 启动器每次拉起比对 sha256,缺失或被改动即从插件包安装到 ~/.deta/runtime(首次启动零操作、升级自动同步);③ 新增 deta install-runtime(显式安装 + 补装可选依赖 @resvg/resvg-js + 装完立刻做一次真实 MCP 握手自证),status/doctor 显示运行时来源与预览图依赖状态;④ 契约校验加 E6:运行时包必须自洽(sha256/字节数/manifest.pluginVersion 与插件版本一致),拦住“升版本忘重跑 sync-runtime”。 实测:全新 HOME(模拟同事机器、无开发检出、无凭据)下供给探针 10/10 通过 —— 首次启动自动安装、启动行指向沙箱运行时、install-runtime 握手 39 个工具、重复安装幂等、运行时被改动自动修复、插件包缺运行时两处都给打包缺陷指引;已装副本 0.3.14 独立验证(含 npm 自动补装 @resvg 2 包)。studio 0.3.14 / writer 0.1.5。
This commit is contained in:
@@ -18,10 +18,15 @@
|
||||
* —— 那是 ant-copilot 的实现细节,文档越界即漂移源。
|
||||
* W1 不得出现开发检出路径(~/Dev/ant/ant-copilot)
|
||||
* —— 本机引擎包落地后升级为错误(见章程 M2)。
|
||||
* E6 运行时包必须自洽:runtime/dist/deta-mcp.mjs 存在,其 sha256/字节数与 runtime/manifest.json 一致,
|
||||
* 且 manifest.pluginVersion 等于插件清单版本
|
||||
* —— 运行时随插件分发(私有 registry 匿名读 401,走 npm 会让每台机器都要 token),
|
||||
* 因此「升了版本忘了重跑 sync-runtime」会静默发出发错版本的运行时;这条规则把它变成红灯。
|
||||
*
|
||||
* 用法:node scripts/check-plugin-contract.mjs [--json]
|
||||
* 退出码:有 error 时 1,否则 0(warning 不失败)。
|
||||
*/
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
|
||||
import { join, relative, basename, resolve } from 'node:path';
|
||||
|
||||
@@ -75,6 +80,11 @@ const RULES = {
|
||||
hint: '插件清单用 logo / logoDark 指向插件根内相对路径(如 ./assets/logo.svg,深色主题给 logoDark);'
|
||||
+ '注意 iconSmall/iconLarge 是**技能**的字段,插件里写了不生效',
|
||||
},
|
||||
E6: {
|
||||
level: 'error',
|
||||
what: '运行时包不自洽(缺 runtime/、sha256 或字节数与 manifest 不符、manifest.pluginVersion 与插件版本不符)',
|
||||
hint: '运行时随插件分发:改动 ant-copilot 后跑 node scripts/sync-runtime.mjs,再升插件版本重装',
|
||||
},
|
||||
};
|
||||
|
||||
const HEADING_BAN = /工具一览|工具清单|工具列表|工具汇总|可用工具|能力一览|能力清单/;
|
||||
@@ -178,6 +188,40 @@ for (const pluginDir of ['plugins/studio', 'plugins/writer']) {
|
||||
}
|
||||
}
|
||||
|
||||
// E6:运行时包自洽(插件自带打包产物 → 装插件即可用;防「升版本忘重跑 sync-runtime」多发错版本)
|
||||
for (const pluginDir of ['plugins/studio', 'plugins/writer']) {
|
||||
const pluginRoot = join(ROOT, pluginDir);
|
||||
const entry = join(pluginRoot, 'runtime', 'dist', 'deta-mcp.mjs');
|
||||
const mPath = join(pluginRoot, 'runtime', 'manifest.json');
|
||||
if (!existsSync(entry) || !existsSync(mPath)) {
|
||||
add('E6', pluginDir, 1, '缺少运行时包(runtime/dist/deta-mcp.mjs + runtime/manifest.json)——跑 node scripts/sync-runtime.mjs');
|
||||
continue;
|
||||
}
|
||||
let m;
|
||||
try {
|
||||
m = JSON.parse(readFileSync(mPath, 'utf8'));
|
||||
} catch (e) {
|
||||
add('E6', mPath, 1, `manifest.json 解析失败:${e.message}`);
|
||||
continue;
|
||||
}
|
||||
const buf = readFileSync(entry);
|
||||
const sha = createHash('sha256').update(buf).digest('hex');
|
||||
if (sha !== m.sha256) {
|
||||
add('E6', mPath, 1, `运行时 sha256 与 manifest 不一致(文件 ${sha.slice(0, 12)}… ≠ manifest ${String(m.sha256).slice(0, 12)}…)——重跑 sync-runtime`);
|
||||
}
|
||||
if (buf.length !== m.bytes) {
|
||||
add('E6', mPath, 1, `运行时字节数与 manifest 不一致(${buf.length} ≠ ${m.bytes})`);
|
||||
}
|
||||
try {
|
||||
const pv = JSON.parse(readFileSync(join(pluginRoot, '.codex-plugin', 'plugin.json'), 'utf8')).version;
|
||||
if (m.pluginVersion !== pv) {
|
||||
add('E6', mPath, 1, `manifest.pluginVersion=${m.pluginVersion} 与插件版本 ${pv} 不一致——升版本后必须重跑 sync-runtime`);
|
||||
}
|
||||
} catch {
|
||||
// 清单本身不合法由官方校验器报
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv.includes('--json')) {
|
||||
console.log(JSON.stringify({ findings, rules: RULES }, null, 2));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user