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