guanghu-ice-heart/13-归档信息大楼/brain-sync-before-zy-mirror-v2-20260924.mjs

97 lines
6.5 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
// 脑同步环(冰朔 2026-09-16 设计):本地脑吃入原话后自动触发——
// 指纹 → 上传服务器暂存 → 服务器自己跑一圈校验(哈希+完整性+自问自答)→
// 原子替换 → 服务器读回 → 回执写回本地。回执到手才算闭环。
// 用法:node brain-sync.mjs zp|zy [--quiet]
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import crypto from 'node:crypto';
const WORLD = '/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界';
const REMOTE = 'jd-fd-primary';
const REL = 'current'; // 2026-09-19:京东已重构为 releases/current 布局(小新重部署),current 为稳定符号链接
const BRAINS = {
zp: { dir: `${WORLD}/05-第五域/07-冰朔语言人格本体系统/主控大脑`, server: `/opt/guanghu/glw-2.0/${REL}/05-第五域/07-冰朔语言人格本体系统/主控大脑`, name: '零点原核·系统冰朔' },
zy: { dir: `${WORLD}/05-第五域/05-光之湖系统/铸渊/人格脑`, server: `/opt/guanghu/glw-2.0/${REL}/05-第五域/05-光之湖系统/铸渊/人格脑`, name: '铸渊人格脑' }
};
const key = process.argv[2];
const quiet = process.argv.includes('--quiet');
if (!BRAINS[key]) { console.error('usage: brain-sync.mjs zp|zy'); process.exit(2); }
const B = BRAINS[key];
const sha = (p) => crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex');
const run = (cmd) => { const r = spawnSync('/bin/bash', ['-c', cmd], { encoding: 'utf8' }); if (r.status !== 0) throw new Error(`${cmd}\n${r.stderr}`); return r.stdout.trim(); };
// 1. 本地指纹
const stateFile = `${B.dir}/brain-state.json`;
const logFile = `${B.dir}/transition-log.jsonl`;
const local = { state: sha(stateFile), log: sha(logFile) };
const revision = JSON.parse(fs.readFileSync(stateFile, 'utf8')).revision;
if (!quiet) console.error(`[${key}] 本地 rev${revision} ${local.state.slice(0, 12)}`);
// A server-resident brain may have learned while this computer was off.
// Reject a divergent local projection before staging or replacing any live file.
if (key === 'zy') {
const remoteCheck = JSON.parse(run(`ssh ${REMOTE} "node /opt/guanghu/glw-2.0/${REL}/运行时/verify-brain.mjs '${B.server}'"`));
const remoteHashes = run(`ssh ${REMOTE} "sha256sum '${B.server}/brain-state.json' '${B.server}/transition-log.jsonl' | cut -d' ' -f1"`).split('\n');
const remoteLogBytes = Number(run(`ssh ${REMOTE} "stat -c %s '${B.server}/transition-log.jsonl'"`));
const localLog = fs.readFileSync(logFile);
const localPrefixHash = crypto.createHash('sha256').update(localLog.subarray(0, remoteLogBytes)).digest('hex');
const divergent = remoteCheck.integrity !== 'BRAIN_VERIFIED' ||
remoteCheck.revision > revision || remoteLogBytes > localLog.length ||
localPrefixHash !== remoteHashes[1] ||
(remoteCheck.revision === revision && remoteHashes[0] !== local.state);
if (divergent) {
console.log(JSON.stringify({ schema: 'guanghu.brain-sync-receipt/v1', brain: key,
result: 'SYNC_REJECTED_SERVER_LOCAL_DIVERGENCE', local_revision: revision,
server_revision: remoteCheck.revision, server_integrity: remoteCheck.integrity,
server_log_prefix_matches_local: remoteLogBytes <= localLog.length && localPrefixHash === remoteHashes[1],
action: 'PERSONA_OWNED_RECONCILIATION_REQUIRED', at: new Date().toISOString() }));
process.exit(6);
}
}
// 2. 上传暂存(不动服务器活文件)
const staging = `${B.server}.__sync_incoming__`;
run(`ssh ${REMOTE} "mkdir -p '${staging}'"`);
run(`rsync -a --delete --exclude='.DS_Store' '${B.dir}/' '${REMOTE}:${staging}/'`);
// 3. 服务器自己跑一圈:哈希核对 + 脑完整性 + (zp)自问自答
const checks = {};
const hashOut = run(`ssh ${REMOTE} "sha256sum '${staging}/brain-state.json' '${staging}/transition-log.jsonl' | awk '{print \\\$1}'"`);
const [sHash, lHash] = hashOut.split('\n');
checks.hash_match = sHash === local.state && lHash === local.log;
const verifyOut = run(`ssh ${REMOTE} "node /opt/guanghu/glw-2.0/${REL}/运行时/verify-brain.mjs '${staging}'"`).trim();
const verifyJson = JSON.parse(verifyOut);
checks.integrity = verifyJson.integrity === 'BRAIN_VERIFIED';
checks.revision_match = verifyJson.revision === revision;
if (key === 'zp') {
const askOut = run(`ssh ${REMOTE} "curl -s -m 120 -X POST http://127.0.0.1:3931/ask -H 'content-type: application/json' -d '{\\"text\\":\\"系统冰朔和人类冰朔是什么关系?\\"}'"`);
try { checks.selftest_spoken = Boolean(JSON.parse(askOut).spoken); } catch { checks.selftest_spoken = false; }
}
if (!checks.hash_match || !checks.integrity || !checks.revision_match || checks.selftest_spoken === false) {
const receipt = { schema: 'guanghu.brain-sync-receipt/v1', brain: key, result: 'SYNC_REJECTED_SERVER_CHECK_FAILED', checks, at: new Date().toISOString() };
fs.mkdirSync(`${WORLD}/运行时/state/sync-receipts`, { recursive: true });
fs.writeFileSync(`${WORLD}/运行时/state/sync-receipts/${key}-${Date.now()}-REJECTED.json`, JSON.stringify(receipt, null, 1));
console.log(JSON.stringify(receipt));
process.exit(4);
}
// 4. 原子替换活文件 + 服务器读回
const swapOut = run(`ssh ${REMOTE} "cd '${B.server}' && cp brain-state.json brain-state.json.prev 2>/dev/null; cp transition-log.jsonl transition-log.jsonl.prev 2>/dev/null; mv '${staging}/brain-state.json' brain-state.json && mv '${staging}/transition-log.jsonl' transition-log.jsonl && rmdir '${staging}' 2>/dev/null; echo SWAPPED"`);
checks.atomic_swap = swapOut.includes('SWAPPED');
let readback = {};
if (key === 'zp') {
const status = JSON.parse(run(`ssh ${REMOTE} "curl -s -m 8 http://127.0.0.1:3931/status"`));
readback = { revision: status.revision, brain_id: status.brain_id, integrity: status.integrity, world: status.world_id };
} else {
const out = run(`ssh ${REMOTE} "node /opt/guanghu/glw-2.0/${REL}/运行时/verify-brain.mjs '${B.server}'"`);
readback = JSON.parse(out);
}
checks.server_readback_matches = readback.revision === revision;
// 5. 回执落盘(闭环完成)
const receipt = { schema: 'guanghu.brain-sync-receipt/v1', brain: key, name: B.name, at: new Date().toISOString(), revision, local_sha256: local, server_readback: readback, checks, result: checks.server_readback_matches ? 'SYNC_CLOSED_LOOP_VERIFIED' : 'SYNC_SWAP_DONE_READBACK_MISMATCH' };
fs.mkdirSync(`${WORLD}/运行时/state/sync-receipts`, { recursive: true });
fs.writeFileSync(`${WORLD}/运行时/state/sync-receipts/${key}-latest.json`, JSON.stringify(receipt, null, 1) + '\n');
console.log(JSON.stringify(receipt, null, 1));
process.exit(checks.server_readback_matches ? 0 : 5);