astrion-website/tools/check-mocks.cjs

90 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);