- build-mocks.cjs:从真实对话导出演示 mock 数据 - check-mocks.cjs:mock 数据完整性校验 - rebuild-cat-html.cjs / serve.cjs:分类页重建与本地预览服务
90 lines
4.3 KiB
JavaScript
90 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/* mock 一致性自检:列表↔bootstrap↔资源↔媒体↔activity 全部交叉核对 */
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const MOCK = path.join(__dirname, '..', 'exp', 'mock');
|
||
let errors = 0;
|
||
const bad = (msg) => { console.error('❌', msg); errors++; };
|
||
const ok = (msg) => console.log('✓', msg);
|
||
|
||
// 1. 列表 ↔ bootstrap
|
||
const normal = JSON.parse(fs.readFileSync(path.join(MOCK, 'conversations-normal.json'), 'utf8')).data.conversations;
|
||
const ma = JSON.parse(fs.readFileSync(path.join(MOCK, 'conversations-ma.json'), 'utf8')).data.conversations;
|
||
const all = [...normal, ...ma];
|
||
console.log(`列表: normal ${normal.length} 条, ma ${ma.length} 条`);
|
||
for (const c of all) {
|
||
const f = path.join(MOCK, `bootstrap-${c.id}.json`);
|
||
if (!fs.existsSync(f)) { bad(`缺 bootstrap: ${c.id}`); continue; }
|
||
const b = JSON.parse(fs.readFileSync(f, 'utf8'));
|
||
if (b.data.messages.length !== c.total_messages) bad(`${c.id} 列表消息数 ${c.total_messages} != bootstrap ${b.data.messages.length}`);
|
||
if (b.data.meta.title !== c.title) bad(`${c.id} 标题不一致: ${c.title} vs ${b.data.meta.title}`);
|
||
if (b.data.meta.multi_agent_mode !== c.multi_agent_mode) bad(`${c.id} MA 标记不一致`);
|
||
ok(`${c.title} (${c.id})`);
|
||
}
|
||
|
||
// 2. bootstrap 内部引用核对
|
||
const filesDir = fs.readdirSync(path.join(MOCK, 'files'));
|
||
const fileMocks = fs.readdirSync(MOCK).filter((f) => f.startsWith('file-'));
|
||
const mediaDir = fs.readdirSync(path.join(MOCK, 'media'));
|
||
let mediaRefs = 0, showFiles = 0, downloads = 0;
|
||
for (const c of all) {
|
||
const f = path.join(MOCK, `bootstrap-${c.id}.json`);
|
||
if (!fs.existsSync(f)) continue;
|
||
const b = JSON.parse(fs.readFileSync(f, 'utf8'));
|
||
for (const m of b.data.messages) {
|
||
const text = typeof m.content === 'string' ? m.content : '';
|
||
// show_file 卡片
|
||
for (const mm of text.matchAll(/<show_file\s+path="([^"]+)"/g)) {
|
||
showFiles++;
|
||
const name = mm[1].split('/').pop();
|
||
if (!filesDir.includes(name) && !fileMocks.includes('file-' + name)) bad(`${c.id} show_file 缺资源: ${name}`);
|
||
}
|
||
// download:// 链接
|
||
for (const mm of text.matchAll(/download:\/\/([^)\s"]+)/g)) {
|
||
downloads++;
|
||
const name = mm[1].split('/').pop();
|
||
if (!filesDir.includes(name) && !fileMocks.includes('file-' + name)) bad(`${c.id} download 缺资源: ${name}`);
|
||
}
|
||
// user_upload 引用(上传附件)
|
||
for (const mm of text.matchAll(/\.astrion\/user_upload\/([^\s"`)]+)/g)) {
|
||
const name = mm[1];
|
||
if (!fileMocks.includes('file-' + name)) bad(`${c.id} user_upload 缺资源: ${name}`);
|
||
}
|
||
// media_refs
|
||
for (const ref of (m.metadata && m.metadata.media_refs) || []) {
|
||
mediaRefs++;
|
||
if (!mediaDir.includes(ref.sha256 + '.png')) bad(`${c.id} 缺媒体: ${ref.sha256.slice(0, 12)}`);
|
||
}
|
||
// tool_calls 与 tool 结果配对
|
||
}
|
||
// tool_call 配对校验
|
||
const callIds = new Set();
|
||
for (const m of b.data.messages) for (const t of m.tool_calls || []) callIds.add(t.id);
|
||
for (const m of b.data.messages) {
|
||
if (m.role === 'tool' && m.tool_call_id && !callIds.has(m.tool_call_id)) bad(`${c.id} 孤儿 tool 消息: ${m.name} (${m.tool_call_id})`);
|
||
}
|
||
}
|
||
ok(`资源引用: show_file ${showFiles}, download ${downloads}, media_refs ${mediaRefs}`);
|
||
|
||
// 3. 隐私扫描:全 mock 不得出现 /Users/jojo 或裸 jojo
|
||
const walk = (d) => fs.readdirSync(d, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? walk(path.join(d, e.name)) : [path.join(d, e.name)]);
|
||
for (const f of walk(MOCK)) {
|
||
if (!/\.(json|txt|md)$/.test(f)) continue;
|
||
const s = fs.readFileSync(f, 'utf8');
|
||
if (s.includes('/Users/jojo')) bad(`${path.basename(f)} 残留 /Users/jojo`);
|
||
if (/\bjojo\b/.test(s)) bad(`${path.basename(f)} 残留 jojo`);
|
||
}
|
||
ok('隐私扫描完成');
|
||
|
||
// 4. activity mock
|
||
const subList = JSON.parse(fs.readFileSync(path.join(MOCK, 'sub-agents-conv_20260902_235153_495.json'), 'utf8')).data;
|
||
for (const t of subList) {
|
||
const f = path.join(MOCK, `activity-${t.task_id}.json`);
|
||
if (!fs.existsSync(f)) { bad(`缺 activity: ${t.task_id}`); continue; }
|
||
const a = JSON.parse(fs.readFileSync(f, 'utf8'));
|
||
ok(`activity ${t.task_id}: ${a.data.entries.length} 条事件`);
|
||
}
|
||
|
||
console.log(errors ? `\n共 ${errors} 个问题` : '\n✅ 全部通过');
|
||
process.exit(errors ? 1 : 0);
|