437 lines
18 KiB
JavaScript
437 lines
18 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Bookworm Smart Assistant — 生产活动全量模拟器
|
|||
|
|
* 路径: scripts/production-sim.js
|
|||
|
|
* 版本: v1.0 (2026-02-20)
|
|||
|
|
*
|
|||
|
|
* 用途:
|
|||
|
|
* 模拟一个完整工作日的 67 个对话事件,验证 activity-logger 日志链路完整性。
|
|||
|
|
* 以「SaaS 支付网关重构」为主线,覆盖全部 50 Skills + 10 Agents + 7 MCPs。
|
|||
|
|
*
|
|||
|
|
* 用法:
|
|||
|
|
* node scripts/production-sim.js # 执行模拟并写入活动日志
|
|||
|
|
* node scripts/production-sim.js --dry-run # 仅打印场景,不写入日志
|
|||
|
|
* node scripts/production-sim.js --verify # 执行后校验日志完整性
|
|||
|
|
*
|
|||
|
|
* 日志输出: debug/activity-YYYY-MM-DD.jsonl
|
|||
|
|
*
|
|||
|
|
* 覆盖清单:
|
|||
|
|
* Skills (50): ai-ml-expert → zero-defect-guardian
|
|||
|
|
* Agents (10): orchestrator, self-auditor, self-healer, full-stack-builder,
|
|||
|
|
* research-analyst, quality-gate, canvas-ui-designer,
|
|||
|
|
* code-reviewer, pre-deploy-checker, test-writer
|
|||
|
|
* MCPs (7): deep-research, context7, sequential-thinking, playwright,
|
|||
|
|
* chrome-devtools, selenium, browserbase
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const { execSync } = require('child_process');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
// ─── 配置 ───────────────────────────────────────────────
|
|||
|
|
// 动态检测配置根目录
|
|||
|
|
const detectClaudeRoot = () => require('./paths.config.js').PATHS.root;
|
|||
|
|
|
|||
|
|
const CLAUDE_ROOT = detectClaudeRoot();
|
|||
|
|
const HOOK = path.join(CLAUDE_ROOT, 'hooks', 'activity-logger.js');
|
|||
|
|
const DEBUG_DIR = path.join(CLAUDE_ROOT, 'debug');
|
|||
|
|
|
|||
|
|
// ─── 参数解析 ───────────────────────────────────────────
|
|||
|
|
const args = process.argv.slice(2);
|
|||
|
|
const DRY_RUN = args.includes('--dry-run');
|
|||
|
|
const VERIFY = args.includes('--verify');
|
|||
|
|
|
|||
|
|
// ─── 计数器 ─────────────────────────────────────────────
|
|||
|
|
const stats = { skill: 0, agent: 0, mcp: 0, total: 0, failed: 0 };
|
|||
|
|
|
|||
|
|
// 记录注入前的日志行数,用于 --verify
|
|||
|
|
let logLinesBefore = 0;
|
|||
|
|
if (VERIFY || !DRY_RUN) {
|
|||
|
|
const dateStr = new Date().toISOString().slice(0, 10);
|
|||
|
|
const logFile = path.join(DEBUG_DIR, `activity-${dateStr}.jsonl`);
|
|||
|
|
try {
|
|||
|
|
logLinesBefore = fs.readFileSync(logFile, 'utf8').trim().split('\n').length;
|
|||
|
|
} catch {
|
|||
|
|
logLinesBefore = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─── 触发函数 ───────────────────────────────────────────
|
|||
|
|
function trigger(payload, scenario) {
|
|||
|
|
const evLabel = (
|
|||
|
|
payload.tool_name === 'Skill' ? 'SKILL' :
|
|||
|
|
payload.tool_name === 'TaskCreate' ? 'AGENT' :
|
|||
|
|
payload.tool_name.startsWith('mcp__') ? 'MCP ' :
|
|||
|
|
payload.tool_name === 'Bash' ? 'BASH ' : 'WRITE'
|
|||
|
|
);
|
|||
|
|
const detail =
|
|||
|
|
payload.tool_input.skill ||
|
|||
|
|
payload.tool_input.subject ||
|
|||
|
|
payload.tool_name.replace('mcp__', '').split('__').join('/') ||
|
|||
|
|
payload.tool_input.command?.slice(0, 60) ||
|
|||
|
|
payload.tool_input.file_path || '';
|
|||
|
|
|
|||
|
|
if (DRY_RUN) {
|
|||
|
|
console.log(` [${evLabel}] ${detail}`);
|
|||
|
|
stats[payload.tool_name === 'Skill' ? 'skill' :
|
|||
|
|
payload.tool_name === 'TaskCreate' ? 'agent' : 'mcp']++;
|
|||
|
|
stats.total++;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const json = JSON.stringify(payload).replace(/'/g, "\\'");
|
|||
|
|
execSync(`echo '${json}' | node "${HOOK}"`, { stdio: 'pipe', timeout: 5000 });
|
|||
|
|
stats[payload.tool_name === 'Skill' ? 'skill' :
|
|||
|
|
payload.tool_name === 'TaskCreate' ? 'agent' : 'mcp']++;
|
|||
|
|
stats.total++;
|
|||
|
|
console.log(` [${evLabel}] ${detail}`);
|
|||
|
|
} catch (e) {
|
|||
|
|
stats.failed++;
|
|||
|
|
console.log(` [FAIL] ${detail} — ${e.message.slice(0, 80)}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function skill(name, scenario) {
|
|||
|
|
console.log(`\n 用户: "${scenario}"`);
|
|||
|
|
trigger({ tool_name: 'Skill', tool_input: { skill: name } }, scenario);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function agent(name, subject, scenario) {
|
|||
|
|
console.log(`\n 系统: ${scenario}`);
|
|||
|
|
trigger({ tool_name: 'TaskCreate', tool_input: { subject: `[agent:${name}] ${subject}` } }, scenario);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function mcp(toolName, input, scenario) {
|
|||
|
|
console.log(`\n MCP: ${scenario}`);
|
|||
|
|
trigger({ tool_name: toolName, tool_input: input }, scenario);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 场景剧本: SaaS 支付网关重构 — 完整工作日
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
// 场景仅在直接运行时执行 (被 require 时跳过)
|
|||
|
|
if (require.main !== module) {
|
|||
|
|
// 模块导出 (供测试使用)
|
|||
|
|
if (typeof module !== 'undefined') {
|
|||
|
|
module.exports = { detectClaudeRoot, trigger, skill, agent, mcp, stats, verifyStats, verifyLogIntegrity };
|
|||
|
|
}
|
|||
|
|
return; // 被其他模块 require 时不执行场景
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('');
|
|||
|
|
console.log('================================================================');
|
|||
|
|
console.log(' Bookworm 生产活动全量模拟器');
|
|||
|
|
console.log(` 日期: ${new Date().toISOString().slice(0, 10)}`);
|
|||
|
|
console.log(` 模式: ${DRY_RUN ? '预览 (--dry-run)' : '写入活动日志'}`);
|
|||
|
|
console.log(` 钩子: ${HOOK}`);
|
|||
|
|
console.log('================================================================');
|
|||
|
|
|
|||
|
|
// ── Phase 1: 晨会与规划 (09:00-09:30) ──────────────────
|
|||
|
|
console.log('\n\n--- Phase 1: 晨会与项目规划 (09:00-09:30) ---');
|
|||
|
|
|
|||
|
|
skill('product-manager-expert',
|
|||
|
|
'帮我梳理 Q1 的产品路线图,重点是用户增长和留存功能');
|
|||
|
|
|
|||
|
|
skill('project-coordinator',
|
|||
|
|
'安排本周冲刺任务,包括支付模块重构和移动端适配');
|
|||
|
|
|
|||
|
|
skill('tech-lead-mentor',
|
|||
|
|
'新来的初级开发不太理解 SOLID 原则,帮我准备一份指导材料');
|
|||
|
|
|
|||
|
|
agent('orchestrator', '协调 Q1 支付模块重构项目',
|
|||
|
|
'编排多团队并行任务: 前端支付UI + 后端网关 + 数据库迁移');
|
|||
|
|
|
|||
|
|
// ── Phase 2: 架构与需求分析 (09:30-10:30) ──────────────
|
|||
|
|
console.log('\n\n--- Phase 2: 架构设计与需求分析 (09:30-10:30) ---');
|
|||
|
|
|
|||
|
|
skill('ai-ml-expert',
|
|||
|
|
'用户支付行为数据训练一个欺诈检测模型,要求 recall > 95% 且误报率 < 1%');
|
|||
|
|
|
|||
|
|
skill('architect-expert',
|
|||
|
|
'设计一个支持多租户的 SaaS 支付网关架构,要求 99.99% 可用性');
|
|||
|
|
|
|||
|
|
skill('cloud-native-expert',
|
|||
|
|
'我们要把支付服务迁移到 K8s,帮我设计 Helm Chart 和 HPA 策略');
|
|||
|
|
|
|||
|
|
skill('database-tuning-expert',
|
|||
|
|
'PostgreSQL 的 orders 表已经 5000 万行了,查询越来越慢,怎么优化分区');
|
|||
|
|
|
|||
|
|
skill('edge-computing-expert',
|
|||
|
|
'海外用户延迟太高,考虑在边缘节点部署支付预校验服务');
|
|||
|
|
|
|||
|
|
skill('impact-analyst',
|
|||
|
|
'评估一下把支付从同步改成异步消息队列模式的影响范围');
|
|||
|
|
|
|||
|
|
skill('diagram-as-code-expert',
|
|||
|
|
'用 Mermaid 画一张支付系统的 C4 架构图');
|
|||
|
|
|
|||
|
|
mcp('mcp__sequential-thinking__sequentialthinking',
|
|||
|
|
{ thought: '分析支付架构的关键决策点: 同步vs异步、单体vs微服务、SQL vs NoSQL', thoughtNumber: 1, totalThoughts: 5, nextThoughtNeeded: true },
|
|||
|
|
'顺序推理分析支付架构决策树');
|
|||
|
|
|
|||
|
|
// ── Phase 3: 市场与商业分析 (10:30-11:00) ──────────────
|
|||
|
|
console.log('\n\n--- Phase 3: 市场与商业分析 (10:30-11:00) ---');
|
|||
|
|
|
|||
|
|
skill('business-plan-skill',
|
|||
|
|
'写一份 SaaS 支付平台的商业计划书,目标是种子轮融资');
|
|||
|
|
|
|||
|
|
skill('finance-advisor',
|
|||
|
|
'帮我做一个 3 年财务预测模型,包括 MRR 增长和获客成本');
|
|||
|
|
|
|||
|
|
skill('pricing-strategist',
|
|||
|
|
'我们的 API 调用计费模式合理吗?竞品都是怎么定价的');
|
|||
|
|
|
|||
|
|
skill('investor-review-guide',
|
|||
|
|
'投资人问我们的护城河是什么,帮我准备 pitch deck 的差异化部分');
|
|||
|
|
|
|||
|
|
skill('industry-research-cn',
|
|||
|
|
'调研一下 2025-2026 年中国第三方支付行业的监管趋势和市场规模');
|
|||
|
|
|
|||
|
|
skill('growth-hacker',
|
|||
|
|
'月活增长放缓了,帮我设计一套 PLG 增长飞轮策略');
|
|||
|
|
|
|||
|
|
skill('sales-consultant',
|
|||
|
|
'企业客户谈判卡在年费定价上了,给一些 B2B SaaS 的谈判技巧');
|
|||
|
|
|
|||
|
|
skill('legal-review-skill',
|
|||
|
|
'审查一下我们的用户服务条款,特别是支付退款和数据隐私部分');
|
|||
|
|
|
|||
|
|
agent('research-analyst', '竞品支付网关技术栈深度调研',
|
|||
|
|
'启动调研智能体,分析 Stripe/Adyen/PayPal 的技术架构差异');
|
|||
|
|
|
|||
|
|
mcp('mcp__deep-research__search',
|
|||
|
|
{ query: '2026 payment gateway architecture trends microservices event-driven' },
|
|||
|
|
'深度研究支付网关行业趋势');
|
|||
|
|
|
|||
|
|
// ── Phase 4: 前端开发 (11:00-12:00) ────────────────────
|
|||
|
|
console.log('\n\n--- Phase 4: 前端开发 (11:00-12:00) ---');
|
|||
|
|
|
|||
|
|
skill('frontend-expert',
|
|||
|
|
'用 React 18 + TanStack Query 重写支付收银台组件,要求支持暗色模式');
|
|||
|
|
|
|||
|
|
skill('designer-expert',
|
|||
|
|
'设计支付成功页的 UI,要有品牌感和信任感');
|
|||
|
|
|
|||
|
|
skill('ux-researcher',
|
|||
|
|
'我们的支付转化率才 67%,分析一下结账流程哪一步流失最严重');
|
|||
|
|
|
|||
|
|
skill('miniprogram-expert',
|
|||
|
|
'微信小程序端的支付组件需要适配新版 JSAPI,帮我改一下');
|
|||
|
|
|
|||
|
|
skill('mobile-expert',
|
|||
|
|
'React Native 端的支付页面在 Android 低端机上卡顿,怎么优化渲染');
|
|||
|
|
|
|||
|
|
agent('canvas-ui-designer', '设计支付收银台 UI 原型',
|
|||
|
|
'生成收银台组件的响应式设计方案和交互原型');
|
|||
|
|
|
|||
|
|
skill('copywriter-expert',
|
|||
|
|
'写支付页面的文案,包括按钮文字、错误提示、加载状态的 UX writing');
|
|||
|
|
|
|||
|
|
skill('social-media-manager',
|
|||
|
|
'新版支付体验上线了,帮我写一篇产品更新的推文和公众号文章');
|
|||
|
|
|
|||
|
|
mcp('mcp__playwright__browser_navigate',
|
|||
|
|
{ url: 'http://localhost:3000/checkout' },
|
|||
|
|
'自动化浏览器测试支付收银台页面渲染');
|
|||
|
|
|
|||
|
|
mcp('mcp__chrome-devtools__take_snapshot',
|
|||
|
|
{},
|
|||
|
|
'用 DevTools 抓取收银台页面 DOM 快照排查布局问题');
|
|||
|
|
|
|||
|
|
// ── Phase 5: 后端与数据开发 (13:00-14:30) ──────────────
|
|||
|
|
console.log('\n\n--- Phase 5: 后端与数据开发 (13:00-14:30) ---');
|
|||
|
|
|
|||
|
|
skill('backend-builder',
|
|||
|
|
'用 NestJS + Prisma 实现支付回调的幂等处理逻辑');
|
|||
|
|
|
|||
|
|
skill('developer-expert',
|
|||
|
|
'TypeScript 里怎么优雅地处理支付状态机的类型推导');
|
|||
|
|
|
|||
|
|
skill('api-integration-specialist',
|
|||
|
|
'集成支付宝和微信支付的统一回调接口,需要签名验证');
|
|||
|
|
|
|||
|
|
skill('data-engineer-expert',
|
|||
|
|
'设计支付数据的 ETL 管道,从 PostgreSQL 实时同步到 ClickHouse');
|
|||
|
|
|
|||
|
|
skill('data-analyst-expert',
|
|||
|
|
'分析上个月的支付数据,哪些渠道的失败率最高,原因是什么');
|
|||
|
|
|
|||
|
|
skill('regex-shell-wizard',
|
|||
|
|
'写一个正则提取支付日志里的交易 ID 和金额: "TXN-2026-[A-Z0-9]+ amount=\\d+.\\d+"');
|
|||
|
|
|
|||
|
|
skill('ultimate-code-expert',
|
|||
|
|
'这段支付回调处理代码有竞态条件,帮我重构成无锁设计');
|
|||
|
|
|
|||
|
|
skill('email-communicator',
|
|||
|
|
'帮我写一封邮件给支付渠道的技术对接人,催促沙箱环境的开通');
|
|||
|
|
|
|||
|
|
agent('full-stack-builder', '实现支付回调幂等处理模块',
|
|||
|
|
'全栈构建: 后端幂等键 + Redis 分布式锁 + 前端重试机制');
|
|||
|
|
|
|||
|
|
mcp('mcp__context7__resolve-library-id',
|
|||
|
|
{ libraryName: '@nestjs/core' },
|
|||
|
|
'查询 NestJS 最新文档以实现支付模块');
|
|||
|
|
|
|||
|
|
// ── Phase 6: 安全与合规 (14:30-15:00) ──────────────────
|
|||
|
|
console.log('\n\n--- Phase 6: 安全审计与合规 (14:30-15:00) ---');
|
|||
|
|
|
|||
|
|
skill('security-expert',
|
|||
|
|
'对支付 API 做一次安全审计,检查 OWASP Top 10 和 PCI-DSS 合规');
|
|||
|
|
|
|||
|
|
skill('devsecops-expert',
|
|||
|
|
'在 CI/CD 管道里集成 SAST/DAST 扫描,特别是密钥泄露检测');
|
|||
|
|
|
|||
|
|
skill('customer-success-expert',
|
|||
|
|
'用户投诉支付扣款成功但订单未更新,帮我排查并写一个安抚话术');
|
|||
|
|
|
|||
|
|
// ── Phase 7: 测试与质量 (15:00-16:00) ──────────────────
|
|||
|
|
console.log('\n\n--- Phase 7: 测试与质量保障 (15:00-16:00) ---');
|
|||
|
|
|
|||
|
|
skill('tester-expert',
|
|||
|
|
'帮我设计支付模块的测试方案,包括单元测试、集成测试和 E2E 测试');
|
|||
|
|
|
|||
|
|
skill('browser-automation-expert',
|
|||
|
|
'用 Playwright 写一个支付流程的 E2E 测试,覆盖成功/失败/超时三种场景');
|
|||
|
|
|
|||
|
|
skill('performance-expert',
|
|||
|
|
'支付接口的 P99 延迟从 200ms 涨到 800ms 了,帮我定位瓶颈');
|
|||
|
|
|
|||
|
|
skill('debugger-expert',
|
|||
|
|
'支付回调偶发 500 错误,日志里有 "deadlock detected",帮我排查');
|
|||
|
|
|
|||
|
|
agent('test-writer', '生成支付模块测试套件',
|
|||
|
|
'自动生成: 支付创建/回调/退款/对账 4 个模块的 Vitest 测试用例');
|
|||
|
|
|
|||
|
|
agent('quality-gate', '支付模块质量门控检查',
|
|||
|
|
'执行: 覆盖率 > 80% + 无 Critical 漏洞 + 性能基线达标');
|
|||
|
|
|
|||
|
|
agent('code-reviewer', '审查支付回调 PR #347',
|
|||
|
|
'Review: 幂等处理逻辑 + Redis 锁超时 + 错误重试策略');
|
|||
|
|
|
|||
|
|
mcp('mcp__selenium__navigate',
|
|||
|
|
{ url: 'http://localhost:3000/checkout?test=e2e' },
|
|||
|
|
'Selenium 跨浏览器兼容性测试支付页面');
|
|||
|
|
|
|||
|
|
mcp('mcp__browserbase__browserbase_session_create',
|
|||
|
|
{},
|
|||
|
|
'创建云端浏览器会话进行多地域支付页面测试');
|
|||
|
|
|
|||
|
|
// ── Phase 8: 部署与运维 (16:00-17:00) ──────────────────
|
|||
|
|
console.log('\n\n--- Phase 8: 部署与运维 (16:00-17:00) ---');
|
|||
|
|
|
|||
|
|
skill('devops-expert',
|
|||
|
|
'帮我写 GitHub Actions 的 CI/CD 配置,支付模块要蓝绿部署');
|
|||
|
|
|
|||
|
|
skill('sre-expert',
|
|||
|
|
'设计支付系统的 SLO: 可用性 99.99%,P99 延迟 < 300ms,错误率 < 0.1%');
|
|||
|
|
|
|||
|
|
skill('git-operation-master',
|
|||
|
|
'支付分支 feature/payment-v2 需要 rebase 到 main,有 3 个冲突文件');
|
|||
|
|
|
|||
|
|
skill('technical-seo-expert',
|
|||
|
|
'支付成功页需要正确的 canonical URL 和结构化数据标记');
|
|||
|
|
|
|||
|
|
skill('prompt-optimizer',
|
|||
|
|
'优化支付客服机器人的 system prompt,让它更准确地处理退款咨询');
|
|||
|
|
|
|||
|
|
agent('pre-deploy-checker', '支付模块 v2.0 上线前检查',
|
|||
|
|
'预发布检查: 数据库迁移脚本 + 环境变量 + 回滚方案 + 监控告警');
|
|||
|
|
|
|||
|
|
// ── Phase 9: 系统自进化 (17:00-17:30) ──────────────────
|
|||
|
|
console.log('\n\n--- Phase 9: 系统自进化闭环 (17:00-17:30) ---');
|
|||
|
|
|
|||
|
|
skill('project-audit-expert',
|
|||
|
|
'对今天的支付模块重构做一个项目复盘,识别技术债和改进点');
|
|||
|
|
|
|||
|
|
skill('reviewer-expert',
|
|||
|
|
'帮我 review 今天提交的所有 PR,检查代码风格和架构一致性');
|
|||
|
|
|
|||
|
|
skill('genesis-engine',
|
|||
|
|
'根据今天的开发经验,生成一个"支付系统开发"的技能模板');
|
|||
|
|
|
|||
|
|
skill('zero-defect-guardian',
|
|||
|
|
'全面扫描支付模块的代码质量: 类型安全、错误处理、边界情况');
|
|||
|
|
|
|||
|
|
skill('tech-writer-expert',
|
|||
|
|
'帮支付模块写 API 文档,用 OpenAPI 3.0 格式');
|
|||
|
|
|
|||
|
|
agent('self-auditor', '每日系统一致性审计',
|
|||
|
|
'8 维度扫描: 配置 → 技能 → 智能体 → 钩子 → MCP → 路由 → 安全 → 磁盘');
|
|||
|
|
|
|||
|
|
agent('self-healer', '自动修复审计发现的元数据偏差',
|
|||
|
|
'修复: 版本号同步 + 技能计数校正 + 注册表更新');
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 结果汇总
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
console.log('\n');
|
|||
|
|
console.log('================================================================');
|
|||
|
|
console.log(' 模拟完成');
|
|||
|
|
console.log('----------------------------------------------------------------');
|
|||
|
|
console.log(` Skills: ${stats.skill}/50`);
|
|||
|
|
console.log(` Agents: ${stats.agent}/10`);
|
|||
|
|
console.log(` MCPs: ${stats.mcp}/7`);
|
|||
|
|
console.log(` ----------------`);
|
|||
|
|
console.log(` Total: ${stats.total}/67${stats.failed ? ` (${stats.failed} failed)` : ''}`);
|
|||
|
|
console.log('================================================================');
|
|||
|
|
|
|||
|
|
// 完整性校验函数 (供测试使用)
|
|||
|
|
function verifyStats(s) {
|
|||
|
|
return s.skill === 50 && s.agent === 10 && s.mcp === 7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 日志完整性校验函数
|
|||
|
|
function verifyLogIntegrity(logFile, linesBefore) {
|
|||
|
|
const allLines = fs.readFileSync(logFile, 'utf8').trim().split('\n');
|
|||
|
|
const newLines = allLines.slice(linesBefore);
|
|||
|
|
const skills = new Set(), agents = new Set(), mcps = new Set();
|
|||
|
|
for (const line of newLines) {
|
|||
|
|
const o = JSON.parse(line);
|
|||
|
|
if (o.event === 'skill') skills.add(o.detail);
|
|||
|
|
else if (o.event === 'agent') {
|
|||
|
|
const m = o.detail.match(/\[agent:(\S+)\]/);
|
|||
|
|
agents.add(m ? m[1] : o.detail);
|
|||
|
|
}
|
|||
|
|
else if (o.event === 'mcp') mcps.add(o.detail.split('/')[0]);
|
|||
|
|
}
|
|||
|
|
return { newLines: newLines.length, skills: skills.size, agents: agents.size, mcps: mcps.size };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (require.main === module) {
|
|||
|
|
const passed = verifyStats(stats);
|
|||
|
|
|
|||
|
|
if (!passed) {
|
|||
|
|
console.log('\n [WARN] 覆盖不完整,请检查失败项');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`\n [PASS] 全量覆盖验证通过 — 67/67 事件已${DRY_RUN ? '预览' : '写入活动日志'}`);
|
|||
|
|
|
|||
|
|
// ─── --verify: 日志完整性校验 ───────────────────────────
|
|||
|
|
if (VERIFY && !DRY_RUN) {
|
|||
|
|
console.log('\n--- 日志完整性校验 ---');
|
|||
|
|
const dateStr = new Date().toISOString().slice(0, 10);
|
|||
|
|
const logFile = path.join(DEBUG_DIR, `activity-${dateStr}.jsonl`);
|
|||
|
|
try {
|
|||
|
|
const result = verifyLogIntegrity(logFile, logLinesBefore);
|
|||
|
|
console.log(` 新增日志行: ${result.newLines}`);
|
|||
|
|
console.log(` Unique Skills: ${result.skills}/50`);
|
|||
|
|
console.log(` Unique Agents: ${result.agents}/10`);
|
|||
|
|
console.log(` Unique MCPs: ${result.mcps}/7`);
|
|||
|
|
|
|||
|
|
if (result.skills === 50 && result.agents === 10 && result.mcps === 7) {
|
|||
|
|
console.log('\n [PASS] 日志完整性校验通过');
|
|||
|
|
} else {
|
|||
|
|
console.log('\n [WARN] 日志记录数与预期不符');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log(` [ERROR] 无法读取日志: ${e.message}`);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|