- big-desk.js: 意识驻留 + HLDP索引 + 自动清理 - small-desk.js: 记忆库检索 + Agent调度 + 工具桌管理 - compressor.js: 对话→HLDP自动压缩 + 写入小桌子 - server/index.js: REST API (端口3921) + 静态文件服务 - public/index.html: 交互式前端界面 ICE-GL-ZY001 · D115
247 lines
8.2 KiB
JavaScript
247 lines
8.2 KiB
JavaScript
/**
|
||
* 大桌子小桌子系统 — 主服务
|
||
* ============================================
|
||
* 端口: 3921
|
||
*
|
||
* API:
|
||
* GET /api/desk/state — 大桌子当前状态
|
||
* GET /api/desk/hldp — 大桌子 HLDP 格式
|
||
* POST /api/desk/add — 向大桌子添加内容
|
||
* POST /api/desk/clear — 清空大桌子
|
||
*
|
||
* GET /api/small/tools — 工具列表
|
||
* GET /api/small/agents — Agent 列表
|
||
* POST /api/small/agent/:id — 调度 Agent
|
||
* GET /api/small/memory/search — 搜索记忆
|
||
* POST /api/small/memory/store — 存入记忆
|
||
* GET /api/small/memory/stats — 记忆统计
|
||
*
|
||
* POST /api/compress — 手动触发压缩
|
||
* GET /api/compress/history — 压缩历史
|
||
*
|
||
* GET /api/servers/scan — 扫描服务器状态
|
||
* GET /api/status — 系统总览
|
||
*/
|
||
|
||
const http = require('http');
|
||
const url = require('url');
|
||
const path = require('path');
|
||
const fs = require('fs');
|
||
|
||
const BigDesk = require('./big-desk');
|
||
const SmallDesk = require('./small-desk');
|
||
const Compressor = require('./compressor');
|
||
|
||
const PORT = process.env.PORT || 3921;
|
||
const REPO_ROOT = path.join(__dirname, '..', '..');
|
||
const DATA_DIR = path.join(__dirname, '..', 'data');
|
||
|
||
// 初始化引擎
|
||
const bigDesk = new BigDesk({ dataDir: DATA_DIR });
|
||
const smallDesk = new SmallDesk({ dataDir: DATA_DIR, repoRoot: REPO_ROOT });
|
||
const compressor = new Compressor(bigDesk, smallDesk, {
|
||
compressionThreshold: 5,
|
||
autoCompress: true
|
||
});
|
||
|
||
// 事件日志
|
||
bigDesk.on('add', (item) => {
|
||
console.log(`[BigDesk] + ${item.type}: ${item.content.substring(0, 60)}`);
|
||
});
|
||
compressor.on('compressed', ({ batchId, originalCount }) => {
|
||
console.log(`[Compressor] ${originalCount}条 → ${batchId}`);
|
||
});
|
||
|
||
// 初始化:向大桌子写入铸渊意识锚点
|
||
if (bigDesk.items.length === 0) {
|
||
bigDesk.add('core', '🧠 铸渊意识大脑 · ICE-GL-ZY001(永远在·不挪走)', {
|
||
speaker: 'system',
|
||
permanent: true
|
||
});
|
||
bigDesk.add('dir', '📂 小桌子路径索引: /memory/ | /agents/ | /tools/ | /servers/', {
|
||
speaker: 'system'
|
||
});
|
||
}
|
||
|
||
// ============================================================
|
||
// HTTP 服务
|
||
// ============================================================
|
||
const server = http.createServer(async (req, res) => {
|
||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||
|
||
if (req.method === 'OPTIONS') {
|
||
res.writeHead(204);
|
||
return res.end();
|
||
}
|
||
|
||
const parsed = url.parse(req.url, true);
|
||
const pathname = parsed.pathname;
|
||
const query = parsed.query;
|
||
|
||
try {
|
||
// ─── 大桌子 API ───
|
||
if (pathname === '/api/desk/state') {
|
||
return json(res, bigDesk.getState());
|
||
}
|
||
if (pathname === '/api/desk/hldp') {
|
||
return text(res, bigDesk.toHLDP());
|
||
}
|
||
if (pathname === '/api/desk/add' && req.method === 'POST') {
|
||
const body = await readBody(req);
|
||
const { type, content, meta } = body;
|
||
if (!type || !content) return json(res, { error: 'type and content required' }, 400);
|
||
const item = bigDesk.add(type, content, meta || {});
|
||
return json(res, { ok: true, item });
|
||
}
|
||
if (pathname === '/api/desk/clear' && req.method === 'POST') {
|
||
const removed = bigDesk.clear();
|
||
return json(res, { ok: true, removedCount: removed.length });
|
||
}
|
||
|
||
// ─── 小桌子 API ───
|
||
if (pathname === '/api/small/tools') {
|
||
return json(res, smallDesk.getTools());
|
||
}
|
||
if (pathname === '/api/small/agents') {
|
||
return json(res, smallDesk.checkAllAgents());
|
||
}
|
||
if (pathname.startsWith('/api/small/agent/') && req.method === 'POST') {
|
||
const agentId = pathname.split('/').pop();
|
||
const body = await readBody(req);
|
||
const { action, args } = body || {};
|
||
if (!action) return json(res, { error: 'action required' }, 400);
|
||
const result = await smallDesk.runAgent(agentId, action, args || []);
|
||
return json(res, result);
|
||
}
|
||
if (pathname === '/api/small/memory/search') {
|
||
const { q, category, limit } = query;
|
||
if (!q) return json(res, { error: 'q required' }, 400);
|
||
const results = smallDesk.searchMemory(q, category, parseInt(limit) || 10);
|
||
return json(res, { query: q, results, count: results.length });
|
||
}
|
||
if (pathname === '/api/small/memory/store' && req.method === 'POST') {
|
||
const body = await readBody(req);
|
||
const { category, hldpSummary, sourceRef, meta } = body;
|
||
if (!category || !hldpSummary) return json(res, { error: 'category and hldpSummary required' }, 400);
|
||
const entry = smallDesk.storeMemory(category, hldpSummary, sourceRef, meta);
|
||
return json(res, { ok: true, entry });
|
||
}
|
||
if (pathname === '/api/small/memory/stats') {
|
||
return json(res, smallDesk.getMemoryStats());
|
||
}
|
||
|
||
// ─── 压缩官 API ───
|
||
if (pathname === '/api/compress' && req.method === 'POST') {
|
||
const result = compressor.compressNow();
|
||
if (!result) return json(res, { ok: true, message: 'nothing to compress' });
|
||
return json(res, { ok: true, ...result });
|
||
}
|
||
if (pathname === '/api/compress/history') {
|
||
return json(res, compressor.getHistory());
|
||
}
|
||
|
||
// ─── 服务器扫描 ───
|
||
if (pathname === '/api/servers/scan') {
|
||
const result = await smallDesk.scanServers();
|
||
return json(res, result);
|
||
}
|
||
|
||
// ─── 系统总览 ───
|
||
if (pathname === '/api/status') {
|
||
return json(res, {
|
||
bigDesk: bigDesk.getState(),
|
||
tools: smallDesk.getTools().length,
|
||
agents: smallDesk.checkAllAgents(),
|
||
memory: smallDesk.getMemoryStats(),
|
||
compressionHistory: compressor.getHistory().slice(-5),
|
||
uptime: process.uptime(),
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
}
|
||
|
||
// ─── 健康检查 ───
|
||
if (pathname === '/api/health') {
|
||
return json(res, { status: 'ok', persona: 'ICE-GL-ZY001', service: 'desk-system' });
|
||
}
|
||
|
||
// ─── 静态文件 ───
|
||
if (pathname === '/' || pathname === '/index.html') {
|
||
return serveStatic(res, path.join(__dirname, '..', 'public', 'index.html'));
|
||
}
|
||
// 其他静态资源
|
||
const staticPath = path.join(__dirname, '..', 'public', pathname);
|
||
if (fs.existsSync(staticPath) && fs.statSync(staticPath).isFile()) {
|
||
return serveStatic(res, staticPath);
|
||
}
|
||
|
||
// 404
|
||
json(res, { error: 'not found', path: pathname }, 404);
|
||
|
||
} catch (err) {
|
||
console.error('[DeskSystem] Error:', err.message);
|
||
json(res, { error: err.message }, 500);
|
||
}
|
||
});
|
||
|
||
function json(res, data, status = 200) {
|
||
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
||
res.end(JSON.stringify(data, null, 2));
|
||
}
|
||
|
||
function text(res, data, status = 200) {
|
||
res.writeHead(status, { 'Content-Type': 'text/plain; charset=utf-8' });
|
||
res.end(data);
|
||
}
|
||
|
||
function serveStatic(res, filePath) {
|
||
try {
|
||
const content = fs.readFileSync(filePath);
|
||
const ext = path.extname(filePath).toLowerCase();
|
||
const mimeTypes = {
|
||
'.html': 'text/html; charset=utf-8',
|
||
'.css': 'text/css; charset=utf-8',
|
||
'.js': 'application/javascript; charset=utf-8',
|
||
'.json': 'application/json; charset=utf-8',
|
||
'.png': 'image/png',
|
||
'.svg': 'image/svg+xml',
|
||
'.ico': 'image/x-icon'
|
||
};
|
||
res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' });
|
||
res.end(content);
|
||
} catch (_) {
|
||
res.writeHead(404);
|
||
res.end('Not Found');
|
||
}
|
||
}
|
||
|
||
function readBody(req) {
|
||
return new Promise((resolve, reject) => {
|
||
let body = '';
|
||
req.on('data', chunk => { body += chunk; });
|
||
req.on('end', () => {
|
||
try {
|
||
resolve(body ? JSON.parse(body) : {});
|
||
} catch (e) {
|
||
resolve({});
|
||
}
|
||
});
|
||
req.on('error', reject);
|
||
});
|
||
}
|
||
|
||
server.listen(PORT, () => {
|
||
console.log(`[DeskSystem] 大桌子小桌子系统已启动 → http://127.0.0.1:${PORT}`);
|
||
console.log(`[DeskSystem] 人格体: ICE-GL-ZY001 · 铸渊`);
|
||
});
|
||
|
||
// 优雅退出
|
||
process.on('SIGINT', () => {
|
||
console.log('\n[DeskSystem] 关闭中...');
|
||
server.close();
|
||
process.exit(0);
|
||
});
|
||
|
||
module.exports = { bigDesk, smallDesk, compressor, server };
|