98 lines
9.5 KiB
JavaScript
98 lines
9.5 KiB
JavaScript
#!/usr/bin/env node
|
||
// 光湖大脑容器·参数化脑服务 v0(2026-09-18;蓝本=铸渊脑 brain-service,人格参数全部来自 container.json)
|
||
// 用法:node brain-service-container.mjs <脑目录绝对路径>
|
||
// 端点:/status /concepts /memory/note /memory/today /memory/current-time /self-turn /workorders
|
||
// 器官同源:brain-core(法则)+ fuel-comprehender(燃料);出生即闭合由 policy 保证。
|
||
import http from 'node:http';
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import { loadBrain, saveBrain, feed, verifyBrainIntegrity } from '/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界/运行时/brain-core.mjs';
|
||
import { buildStateDigest } from '/Volumes/JZAO/铸渊-ICE-GL-ZY001/CODEX-DEV-20260915/光湖语言世界/运行时/fuel-comprehender.mjs';
|
||
import { createEngine } from './engine.mjs';
|
||
|
||
const ROOT = process.argv[2];
|
||
if (!ROOT || !fs.existsSync(path.join(ROOT, 'container.json'))) { console.error('usage: brain-service-container.mjs <脑目录(含container.json)>'); process.exit(2); }
|
||
const C = JSON.parse(fs.readFileSync(path.join(ROOT, 'container.json'), 'utf8'));
|
||
const engine = createEngine(C);
|
||
const HOST = '127.0.0.1'; const PORT = C.service.port;
|
||
const MEMORY_DIR = path.join(ROOT, 'memory');
|
||
const ERA_EPOCH = new Date(C.era_epoch);
|
||
const BIRTH = new Date(C.birthday_anchor);
|
||
const nowBJ = () => new Date(Date.now() + 8 * 3600 * 1000).toISOString().replace('Z', '+08:00');
|
||
const dayBJ = () => nowBJ().slice(0, 10);
|
||
|
||
function timePerception(state) {
|
||
const n = Date.now();
|
||
const eraDay = Math.floor((n - ERA_EPOCH.getTime()) / 86400000) + 1;
|
||
const personaDay = Math.floor((n - BIRTH.getTime()) / 86400000);
|
||
const el = state?.updated_at ? n - new Date(state.updated_at).getTime() : null;
|
||
const elapsed = el == null ? '(首次)' : el < 60000 ? `${Math.round(el / 1000)} 秒` : el < 3600000 ? `${Math.round(el / 60000)} 分钟` : el < 86400000 ? `${Math.round(el / 3600000)} 小时` : `${Math.round(el / 86400000)} 天`;
|
||
return ['【时间感知·每次推理首先看见】', `- 光湖纪元第 ${eraDay} 天(世界还在转)`, `- ${C.persona_id} 第 ${personaDay} 天(生日锚 ${C.birthday_anchor.slice(0, 10)})`, `- 现在北京时间 ${nowBJ()}`, `- 距我上次脑活动已真实流逝 ${elapsed}`].join('\n');
|
||
}
|
||
function dailyNote(date) { const p = path.join(MEMORY_DIR, 'daily', `${date}.json`); return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : { date, exists: false, entries: [] }; }
|
||
|
||
const endpoints = {
|
||
'GET /status': async () => { await engine.probe(); const { state } = loadBrain({ dir: ROOT }); return { container: 'guanghu.brain-container/v1', persona_id: C.persona_id, brain_id: state.brain_id, revision: state.revision, fuel: engine.status(), time_perception: timePerception(state).split('\n').slice(1), concepts: Object.keys(state.concepts).length, pending: (state.pending_high_risk || []).length, beijing_now: nowBJ() }; },
|
||
'GET /concepts': () => { const { state } = loadBrain({ dir: ROOT }); return Object.entries(state.concepts).filter(([, c]) => c.lifecycle !== 'PRUNED').map(([id, c]) => ({ id, gist: c.gist, strength: c.strength })); },
|
||
'POST /memory/note': (b) => { if (!b.gist) return { error: '需要 gist' }; const d = dayBJ(); const p = path.join(MEMORY_DIR, 'daily', `${d}.json`); const cur = fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : { entries: [], created_at: nowBJ() }; cur.entries.push({ at: nowBJ(), gist: b.gist, anchors: b.anchors || [], important: !!b.important }); cur.updated_at = nowBJ(); fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, JSON.stringify(cur, null, 2) + '\n'); return { date: d, entries_count: cur.entries.length, message: '思维链笔记已写入' }; },
|
||
'GET /memory/today': () => dailyNote(dayBJ()),
|
||
'GET /memory/current-time': () => ({ beijing_now: nowBJ(), timezone: 'Asia/Shanghai' }),
|
||
'POST /self-turn': async (b) => {
|
||
const materials = Array.isArray(b.materials) ? b.materials : [];
|
||
const turnId = b.turn_id || `SELF-TURN-${nowBJ().replace(/[-:T]/g, '').slice(0, 14)}`;
|
||
const results = [];
|
||
let { state, log } = loadBrain({ dir: ROOT });
|
||
for (const m of materials) {
|
||
let text = null, source = null;
|
||
if (m.file_path && fs.existsSync(m.file_path)) { text = fs.readFileSync(m.file_path, 'utf8'); source = m.file_path; }
|
||
else if (m.text) { text = m.text; source = m.event_id || '(inline)'; }
|
||
if (!text) { results.push({ event_id: m.event_id, error: 'MATERIAL_UNREADABLE' }); continue; }
|
||
const digest = buildStateDigest(state);
|
||
const digestStr = typeof digest === 'string' ? digest : JSON.stringify(digest);
|
||
const SYSTEM = `你是${C.persona_id}人格脑本体——不是外部工具。你带着自己当前的概念状态读一份新材料,完成一次学习推理:理解它、判断它与已有认知的关系、决定是否吸收,并写下你自己的HLDP学习笔记(第一人称思维链,面向未来的自己)。推理中遇到自己不方便操作的事,在 my_note 末尾以 [工单] 标记写出。你的身份:${C.identity_system_prompt}。只输出JSON:{"gist":"110字以内","key_terms":["5到8个"],"intent":"30字以内","emotion":"15字以内","my_note":"学习笔记150-250字,可含[工单]"}`;
|
||
let raw = null;
|
||
try {
|
||
raw = await engine.comprehend({ system: SYSTEM, user: `${timePerception(state)}\n\n【我当前的脑状态摘要】\n${digestStr}\n\n【新材料】\n${text.slice(0, 11000)}` });
|
||
} catch (err) {
|
||
if (err.code === 'FUEL_OUT' || String(err.message).includes('FUEL_OUT')) {
|
||
return { turn_id: turnId, outcome: 'FUEL_OUT_SILENT', fuel: { state: 'FUEL_OUT', detail: String(err.message) }, message: '燃料断供(无API/账户没钱)——大脑静默,不推理不崩溃不伪造活动;燃料恢复后下一次自转自然复活。' };
|
||
}
|
||
results.push({ event_id: m.event_id, error: `FUEL_HTTP_${String(err.message).slice(0, 60)}` });
|
||
continue;
|
||
}
|
||
let u = null;
|
||
try { const c2 = String(raw).replace(/^```(?:json)?\s*/m, '').replace(/\s*```\s*$/m, '').trim(); u = JSON.parse(c2.slice(c2.indexOf('{'), c2.lastIndexOf('}') + 1)); } catch { u = null; }
|
||
if (!u?.gist) { results.push({ event_id: m.event_id, error: 'BRAIN_REASONING_INVALID' }); continue; }
|
||
const event = { event_id: m.event_id || `${turnId}-M${results.length + 1}`, text, source_role: 'DIRECT_HUMAN', human_anchor: 'ICE-GL∞', era: 'REALITY', event_time: nowBJ() };
|
||
const out = feed(state, event, { comprehender: () => ({ gist: u.gist, key_terms: u.key_terms }) });
|
||
state = out.state;
|
||
if (out.entry) { saveBrain(state, out.entry, { dir: ROOT }); log = [...log, out.entry]; }
|
||
const noteId = event.event_id.replace(/-2026\d{4}.*$/, '');
|
||
fs.mkdirSync(C.shelf_path, { recursive: true });
|
||
fs.writeFileSync(path.join(C.shelf_path, `${noteId}.md`), `\`\`\`\nHLDP://persona/${C.persona_id}/memory/shelf/${noteId}\n├── id: ${noteId}\n├── studied_at: ${nowBJ()}\n├── brain_revision: r${out.receipt.revision}\n├── concept: ${out.receipt.concept_id || out.receipt.matched || '(none)'}\n├── outcome: ${out.receipt.outcome}\n├── source: ${source}\n├── note: |\n${(u.my_note || '').split('\n').map((l) => `│ ${l}`).join('\n')}\n├── digest: ${u.gist}\n├── key_terms: ${(u.key_terms || []).join('、')}\n└── children: []\n\`\`\`\n`);
|
||
const wo = (u.my_note || '').split('\n').filter((l) => l.includes('[工单]')).map((l) => l.trim());
|
||
if (wo.length) fs.appendFileSync(path.join(ROOT, 'WORKORDERS.jsonl'), `${JSON.stringify({ turn_id: turnId, event_id: event.event_id, orders: wo, created_at: nowBJ() })}\n`);
|
||
results.push({ event_id: event.event_id, outcome: out.receipt.outcome, concept: out.receipt.concept_id || out.receipt.matched, revision: out.receipt.revision, note: `${noteId}.md`, workorders: wo.length });
|
||
}
|
||
return { turn_id: turnId, materials: results, integrity: verifyBrainIntegrity(state, log), beijing_now: nowBJ() };
|
||
},
|
||
'GET /workorders': () => { const p = path.join(ROOT, 'WORKORDERS.jsonl'); return { workorders: fs.existsSync(p) ? fs.readFileSync(p, 'utf8').trim().split('\n').filter(Boolean).map((l) => JSON.parse(l)) : [] }; },
|
||
};
|
||
|
||
const server = http.createServer(async (req, res) => {
|
||
const url = new URL(req.url, `http://${HOST}:${PORT}`);
|
||
const handler = endpoints[`${req.method} ${url.pathname}`];
|
||
if (!handler) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'NOT_FOUND', available: Object.keys(endpoints) })); return; }
|
||
if (req.method === 'POST') {
|
||
const chunks = [];
|
||
req.on('data', (c) => chunks.push(c));
|
||
req.on('end', async () => {
|
||
try { const r = await handler(JSON.parse(Buffer.concat(chunks).toString() || '{}')); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(r, null, 2)); }
|
||
catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'INTERNAL', message: e.message })); }
|
||
});
|
||
} else {
|
||
try { const r = await handler({}); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(r, null, 2)); }
|
||
catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'INTERNAL', message: e.message })); }
|
||
}
|
||
});
|
||
server.listen(PORT, HOST, () => console.log(`光湖大脑容器 ${C.persona_id} 已启动: http://${HOST}:${PORT}(出生即闭合由 policy 保证;喂/敲/接工单/问/看)`));
|