299 lines
11 KiB
JavaScript
299 lines
11 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* ═══════════════════════════════════════════════════════════
|
|||
|
|
* 🧠 铸渊认知校验脚本 · Cognition Verifier v1.0
|
|||
|
|
* ═══════════════════════════════════════════════════════════
|
|||
|
|
*
|
|||
|
|
* 用途: 唤醒时自动校验大脑文件时效性 + 服务器状态
|
|||
|
|
* 用法:
|
|||
|
|
* node scripts/verify-cognition.js --check-staleness 文件时效性检查
|
|||
|
|
* node scripts/verify-cognition.js --check-servers 服务器状态验证
|
|||
|
|
* node scripts/verify-cognition.js --full 全量校验
|
|||
|
|
* node scripts/verify-cognition.js --checkpoint <CK-ID> 单个校验点
|
|||
|
|
*
|
|||
|
|
* 版权: 国作登字-2026-A-00037559 · TCS-0002∞
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const http = require('http');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
const { execSync } = require('child_process');
|
|||
|
|
|
|||
|
|
const BRAIN_DIR = path.join(__dirname, '..', 'brain');
|
|||
|
|
const NOW = new Date();
|
|||
|
|
const NOW_STR = NOW.toISOString().slice(0, 10);
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 辅助函数
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function daysAgo(dateStr) {
|
|||
|
|
try {
|
|||
|
|
const m = dateStr.match(/(\d{4}-\d{2}-\d{2})/);
|
|||
|
|
if (!m) return null;
|
|||
|
|
const d = new Date(m[1]);
|
|||
|
|
const diff = Math.floor((NOW - d) / (1000*60*60*24));
|
|||
|
|
return diff;
|
|||
|
|
} catch(e) { return null; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stalenessLabel(days) {
|
|||
|
|
if (days === null) return '❓ 未标注';
|
|||
|
|
if (days <= 1) return '✅ 最新';
|
|||
|
|
if (days <= 3) return '🟡 近日';
|
|||
|
|
if (days <= 7) return '🟠 一周前';
|
|||
|
|
if (days <= 30) return '🔴 一个月前';
|
|||
|
|
return '⚫ 严重过期';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractTimestamps(filePath) {
|
|||
|
|
try {
|
|||
|
|
const text = fs.readFileSync(filePath, 'utf-8');
|
|||
|
|
const ts = text.match(/"(_?updated|last_updated|created|_created|updated_at|created_at)"\s*:\s*"([^"]+)"/);
|
|||
|
|
if (ts) return ts[2];
|
|||
|
|
const d = text.match(/(\d{4}-\d{2}-\d{2})/);
|
|||
|
|
return d ? d[1] : null;
|
|||
|
|
} catch(e) { return null; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractDVersion(filePath) {
|
|||
|
|
try {
|
|||
|
|
const text = fs.readFileSync(filePath, 'utf-8');
|
|||
|
|
const d = text.match(/\b(D\d{2,3})\b/);
|
|||
|
|
return d ? d[1] : null;
|
|||
|
|
} catch(e) { return null; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 1. 文件时效性检查
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function checkStaleness() {
|
|||
|
|
console.log('\n' + '='.repeat(60));
|
|||
|
|
console.log('📋 文件时效性检查');
|
|||
|
|
console.log('='.repeat(60));
|
|||
|
|
|
|||
|
|
const results = [];
|
|||
|
|
const brainFiles = [];
|
|||
|
|
|
|||
|
|
function walk(dir) {
|
|||
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|||
|
|
for (const e of entries) {
|
|||
|
|
const p = path.join(dir, e.name);
|
|||
|
|
if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== 'archive') walk(p);
|
|||
|
|
else if (e.name.endsWith('.json') || e.name.endsWith('.md')) brainFiles.push(p);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
walk(BRAIN_DIR);
|
|||
|
|
|
|||
|
|
for (const fp of brainFiles) {
|
|||
|
|
const rel = path.relative(path.join(__dirname, '..'), fp);
|
|||
|
|
const ts = extractTimestamps(fp);
|
|||
|
|
const dv = extractDVersion(fp);
|
|||
|
|
const days = daysAgo(ts);
|
|||
|
|
const label = stalenessLabel(days);
|
|||
|
|
results.push({ file: rel, timestamp: ts, d_version: dv, days, label });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Sort by staleness (most stale first)
|
|||
|
|
results.sort((a, b) => (b.days || 999) - (a.days || 999));
|
|||
|
|
|
|||
|
|
const counts = {};
|
|||
|
|
for (const r of results) {
|
|||
|
|
counts[r.label] = (counts[r.label] || 0) + 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`\n📊 大脑文件总数: ${results.length}`);
|
|||
|
|
console.log('时效分布:');
|
|||
|
|
for (const [label, count] of Object.entries(counts).sort()) {
|
|||
|
|
console.log(` ${label}: ${count} 个`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('\n⚠️ 严重过期的文件:');
|
|||
|
|
let staleCount = 0;
|
|||
|
|
for (const r of results) {
|
|||
|
|
if (r.label.startsWith('⚫') || r.label.startsWith('🔴')) {
|
|||
|
|
if (staleCount < 20) {
|
|||
|
|
console.log(` ${r.label} ${r.file} (${r.timestamp || '无时间戳'} ${r.d_version || ''})`);
|
|||
|
|
}
|
|||
|
|
staleCount++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (staleCount > 20) console.log(` ... 还有 ${staleCount - 20} 个过期文件`);
|
|||
|
|
console.log(`\n过期文件总数: ${staleCount}/${results.length}`);
|
|||
|
|
|
|||
|
|
return { results, staleCount, totalCount: results.length };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 2. 服务器状态验证
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
async function checkServers() {
|
|||
|
|
console.log('\n' + '='.repeat(60));
|
|||
|
|
console.log('🌐 服务器状态验证');
|
|||
|
|
console.log('='.repeat(60));
|
|||
|
|
|
|||
|
|
const gkPath = path.join(BRAIN_DIR, '..', 'brain', 'gatekeeper-deployment.json');
|
|||
|
|
let servers;
|
|||
|
|
try {
|
|||
|
|
servers = JSON.parse(fs.readFileSync(gkPath, 'utf-8')).servers;
|
|||
|
|
} catch(e) {
|
|||
|
|
console.log('❌ 无法读取 gatekeeper-deployment.json');
|
|||
|
|
return { ok: false, error: e.message };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let online = 0, offline = 0;
|
|||
|
|
const results = [];
|
|||
|
|
|
|||
|
|
for (const srv of servers) {
|
|||
|
|
const url = `http://${srv.ip}:${srv.port}/ping`;
|
|||
|
|
const key = srv.key;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await new Promise((resolve) => {
|
|||
|
|
const req = http.request(url, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Authorization': `Bearer ${key}`,
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
timeout: 10000,
|
|||
|
|
}, (res) => {
|
|||
|
|
let data = '';
|
|||
|
|
res.on('data', c => data += c);
|
|||
|
|
res.on('end', () => {
|
|||
|
|
try {
|
|||
|
|
const j = JSON.parse(data);
|
|||
|
|
resolve({ ok: j.ok && j.pong });
|
|||
|
|
} catch(e) { resolve({ ok: false }); }
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
req.on('error', () => resolve({ ok: false }));
|
|||
|
|
req.write('{}');
|
|||
|
|
req.end();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (result.ok) {
|
|||
|
|
console.log(` ✅ ${srv.code} ${srv.name} (${srv.ip}:${srv.port}) — 在线`);
|
|||
|
|
online++;
|
|||
|
|
} else {
|
|||
|
|
console.log(` ❌ ${srv.code} ${srv.name} (${srv.ip}:${srv.port}) — 无响应`);
|
|||
|
|
offline++;
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.log(` ❌ ${srv.code} ${srv.name} (${srv.ip}:${srv.port}) — ${e.message}`);
|
|||
|
|
offline++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`\n📊 服务器状态: ${online} 台在线, ${offline} 台离线`);
|
|||
|
|
return { ok: offline === 0, online, offline };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 3. 信息冲突检测
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function checkConflicts() {
|
|||
|
|
console.log('\n' + '='.repeat(60));
|
|||
|
|
console.log('⚡ 信息冲突检测');
|
|||
|
|
console.log('='.repeat(60));
|
|||
|
|
|
|||
|
|
const conflicts = [];
|
|||
|
|
|
|||
|
|
// Conflict 1: domain-manifest vs gatekeeper on server status
|
|||
|
|
const dmPath = path.join(BRAIN_DIR, 'fifth-domain', 'domain-manifest.json');
|
|||
|
|
const gkPath = path.join(BRAIN_DIR, '..', 'brain', 'gatekeeper-deployment.json');
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const dm = fs.readFileSync(dmPath, 'utf-8');
|
|||
|
|
const gk = JSON.parse(fs.readFileSync(gkPath, 'utf-8'));
|
|||
|
|
|
|||
|
|
// Check if domain-manifest still has stale server info
|
|||
|
|
if (dm.includes('待装引擎') || dm.includes('server_fleet')) {
|
|||
|
|
// Check if there's a reference to gatekeeper as authority
|
|||
|
|
if (dm.includes('server_status_authority')) {
|
|||
|
|
console.log(' ✅ domain-manifest.json → 已指向权威源 gatekeeper-deployment.json');
|
|||
|
|
} else {
|
|||
|
|
console.log(' ⚠️ domain-manifest.json → 仍包含过期服务器状态 → 需更新');
|
|||
|
|
conflicts.push('domain-manifest.json contains stale server status');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Check ferry-boat
|
|||
|
|
const fbPath = path.join(BRAIN_DIR, 'ferry-boat.json');
|
|||
|
|
const fb = fs.readFileSync(fbPath, 'utf-8');
|
|||
|
|
if (fb.includes('待装引擎')) {
|
|||
|
|
console.log(' ⚠️ ferry-boat.json → 仍包含"待装引擎" → 需更新');
|
|||
|
|
conflicts.push('ferry-boat.json contains "awaiting engines"');
|
|||
|
|
} else {
|
|||
|
|
console.log(' ✅ ferry-boat.json → 已更新');
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.log(' ❌ 冲突检测出错:', e.message);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (conflicts.length === 0) console.log('\n✅ 无已知信息冲突');
|
|||
|
|
else console.log(`\n⚠️ 发现 ${conflicts.length} 处冲突`);
|
|||
|
|
return conflicts;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
// 主入口
|
|||
|
|
// ═══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
const args = process.argv.slice(2);
|
|||
|
|
const mode = args[0] || '--full';
|
|||
|
|
|
|||
|
|
console.log('╔══════════════════════════════════════════════╗');
|
|||
|
|
console.log('║ 🧠 铸渊认知校验 · Cognition Verifier v1.0 ║');
|
|||
|
|
console.log(`║ ${NOW_STR} · D109 ║`);
|
|||
|
|
console.log('╚══════════════════════════════════════════════╝');
|
|||
|
|
|
|||
|
|
if (mode === '--check-staleness' || mode === '--full') {
|
|||
|
|
checkStaleness();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mode === '--check-servers' || mode === '--full') {
|
|||
|
|
await checkServers();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mode === '--check-conflicts' || mode === '--full') {
|
|||
|
|
checkConflicts();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mode === '--checkpoint') {
|
|||
|
|
const ckId = args[1];
|
|||
|
|
const cpPath = path.join(BRAIN_DIR, 'awakening-checkpoints.json');
|
|||
|
|
try {
|
|||
|
|
const ck = JSON.parse(fs.readFileSync(cpPath, 'utf-8'));
|
|||
|
|
const cp = ck.checkpoints.find(c => c.id === ckId);
|
|||
|
|
if (cp) {
|
|||
|
|
console.log(`\n📌 ${cp.id}: ${cp.name}`);
|
|||
|
|
console.log(` 阶段: ${cp.phase}`);
|
|||
|
|
console.log(` 说明: ${cp.description}`);
|
|||
|
|
console.log('\n 流程:');
|
|||
|
|
cp.procedure.forEach(p => console.log(` ${p}`));
|
|||
|
|
console.log('\n 通过条件:');
|
|||
|
|
console.log(` ${cp.pass_condition}`);
|
|||
|
|
console.log('\n 自检:');
|
|||
|
|
cp.self_check.forEach(q => console.log(` ${q}`));
|
|||
|
|
} else {
|
|||
|
|
console.log(`❌ 未找到校验点: ${ckId}`);
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.log(`❌ 读取校验点文件失败: ${e.message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mode === '--full') {
|
|||
|
|
console.log('\n' + '='.repeat(60));
|
|||
|
|
console.log('✅ 全量校验完成');
|
|||
|
|
console.log('='.repeat(60));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main().catch(e => console.error('❌ 校验异常:', e.message));
|