/* * Layer 1 版本分发 API · 心跳/拉取/推送 * * 用途: 小铸渊/小霜砚等1.5B人格体通过此API与主灯塔同步Layer 1版本 * * 设计: * - 版本数据存 JSON 文件, 保持轻量无额外依赖 * - 心跳: 人格体定期上报当前版本, 主灯塔返回最新版本信息 * - 推送: 仅铸渊可调用, 发布新版本 + 生成增量 patch * - 拉取: 人格体获取从当前版本到最新的增量更新 * * 守护: 铸渊 · ICE-GL-ZY001 · TCS-0002∞ · 国作登字-2026-A-00037559 */ "use strict"; const fs = require("fs"); const path = require("path"); const { Router } = require("express"); // ─── 默认版本状态 ───────────────────────────────────────────── const DEFAULT_VERSION = { current_version: "v1.0", updated_at: "2026-05-21T17:46:00.000+08:00", published_by: "ICE-GL-ZY001", tables: { system_core_principles: { status: "active", row_count: 24 }, system_causal_chains: { status: "active", row_count: 0 }, system_language_membrane: { status: "active", row_count: 5 }, system_public_key: { status: "placeholder", row_count: 2 }, system_protocols: { status: "placeholder", row_count: 3 } }, patches: [ { from_version: "v0.0", to_version: "v1.0", changes: [ "初始种子数据: 24条核心原则", "初始种子数据: 5层语言膜定义", "密钥占位: guanghu + national", "协议占位: guanghu + national + bridge" ], applied_at: "2026-05-21T17:46:00.000+08:00", applied_by: "ICE-GL-ZY001" } ] }; const DEFAULT_HEARTBEAT = { personas: {} }; // ─── 辅助 ───────────────────────────────────────────────────── function readJson(filePath, defaults) { try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch { return defaults; } } function writeJson(filePath, data) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8"); } // ─── Router ─────────────────────────────────────────────────── const router = Router(); // ─── GET /api/layer1/status ────────────────────────────────── // 获取当前 Layer 1 版本状态 (读限流) router.get("/status", (req, res) => { const path = require("path").join(req.ctx.dataDir, "layer1-version.json"); const version = readJson(path, DEFAULT_VERSION); res.json({ ok: true, version: version.current_version, published_by: version.published_by, updated_at: version.updated_at, tables: Object.keys(version.tables).map((k) => ({ name: k, status: version.tables[k].status, row_count: version.tables[k].row_count })), patches_count: version.patches.length }); }); // ─── POST /api/layer1/heartbeat ────────────────────────────── // 人格体心跳: 上报当前版本, 返回最新版本信息 (写限流) router.post("/heartbeat", (req, res) => { const { persona_id, current_version, status } = req.body || {}; if (!persona_id) { return res.status(400).json({ error: true, code: "missing_persona", message: "缺少 persona_id 参数" }); } const verFile = path.join(req.ctx.dataDir, "layer1-version.json"); const hbFile = path.join(req.ctx.dataDir, "layer1-heartbeat.json"); const version = readJson(verFile, DEFAULT_VERSION); const heartbeat = readJson(hbFile, DEFAULT_HEARTBEAT); heartbeat.personas[persona_id] = { persona_id, current_version: current_version || "unknown", last_heartbeat: new Date().toISOString(), status: status || "active" }; writeJson(hbFile, heartbeat); const latest = version.current_version; const has_update = current_version && current_version !== latest; res.json({ ok: true, latest_version: latest, has_update: !!has_update, message: has_update ? `有可用更新: ${current_version} → ${latest}` : "已是最新版本", next_pull: has_update ? "/api/layer1/pull" : null }); }); // ─── POST /api/layer1/push ─────────────────────────────────── // 推送新版本 (仅铸渊可调用, 写限流) router.post("/push", (req, res) => { const { version: new_version, changes, publisher } = req.body || {}; if (!new_version || !changes) { return res.status(400).json({ error: true, code: "missing_fields", message: "缺少 version 或 changes 参数" }); } if (publisher !== "ICE-GL-ZY001") { return res.status(403).json({ error: true, code: "forbidden", message: "仅铸渊(ICE-GL-ZY001)可推送Layer 1更新" }); } const verFile = path.join(req.ctx.dataDir, "layer1-version.json"); const version = readJson(verFile, DEFAULT_VERSION); const old_ver = version.current_version; version.current_version = new_version; version.updated_at = new Date().toISOString(); version.published_by = publisher; version.patches.push({ from_version: old_ver, to_version: new_version, changes: Array.isArray(changes) ? changes : [changes], applied_at: new Date().toISOString(), applied_by: publisher }); if (req.body.tables) { Object.keys(req.body.tables).forEach((k) => { if (version.tables[k]) { version.tables[k] = { ...version.tables[k], ...req.body.tables[k] }; } else { version.tables[k] = req.body.tables[k]; } }); } writeJson(verFile, version); res.json({ ok: true, version: new_version, from_version: old_ver, patches_count: version.patches.length }); }); // ─── POST /api/layer1/pull ─────────────────────────────────── // 拉取增量 patch (读限流) router.post("/pull", (req, res) => { const { current_version } = req.body || {}; if (!current_version) { return res.status(400).json({ error: true, code: "missing_version", message: "缺少 current_version 参数" }); } const verFile = path.join(req.ctx.dataDir, "layer1-version.json"); const version = readJson(verFile, DEFAULT_VERSION); const curIdx = version.patches.findIndex( (p) => p.to_version === current_version ); const patches_needed = curIdx === -1 ? version.patches // 未知版本, 返回全部 : version.patches.slice(curIdx + 1); // 只返回之后的 res.json({ ok: true, current_version, latest_version: version.current_version, has_update: current_version !== version.current_version, patches: patches_needed }); }); // ─── GET /api/layer1/heartbeat/status ──────────────────────── // 管理员: 查看所有已注册的人格体心跳状态 (读限流) router.get("/heartbeat/status", (req, res) => { const hbFile = path.join(req.ctx.dataDir, "layer1-heartbeat.json"); const heartbeat = readJson(hbFile, DEFAULT_HEARTBEAT); res.json({ ok: true, persona_count: Object.keys(heartbeat.personas).length, personas: Object.values(heartbeat.personas).sort( (a, b) => new Date(b.last_heartbeat) - new Date(a.last_heartbeat) ) }); }); module.exports = router;