280 lines
7.9 KiB
JavaScript
280 lines
7.9 KiB
JavaScript
/**
|
||
* ═══════════════════════════════════════════════════════════
|
||
* MCP 工具 · 活模块操作
|
||
* (registerModule / getModule / listModules / moduleHeartbeat /
|
||
* diagnoseModule / getModuleAlerts / getModuleLearnings /
|
||
* sendHLDP / getHLDPStats)
|
||
* ═══════════════════════════════════════════════════════════
|
||
*
|
||
* 编号: ZY-TASK-007 · S5 Agent系统级
|
||
* 签发: 铸渊 · ICE-GL-ZY001
|
||
* 版权: 国作登字-2026-A-00037559
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
const db = require('../db');
|
||
|
||
// ═══════════════════════════════════════════════════════════
|
||
// 活模块管理
|
||
// ═══════════════════════════════════════════════════════════
|
||
|
||
/**
|
||
* 注册活模块(通过MCP接口,用于远程模块注册)
|
||
*/
|
||
async function registerModule(input) {
|
||
const { module_id, name, module_type, owner, config, capabilities, deploy_host, deploy_port } = input;
|
||
if (!module_id || !name) throw new Error('缺少 module_id 或 name');
|
||
|
||
const result = await db.query(
|
||
`INSERT INTO living_modules (module_id, name, module_type, owner, status, health_score, config, capabilities, deploy_host, deploy_port)
|
||
VALUES ($1, $2, $3, $4, 'initializing', 100, $5, $6, $7, $8)
|
||
ON CONFLICT (module_id) DO UPDATE SET
|
||
name = EXCLUDED.name,
|
||
config = EXCLUDED.config,
|
||
capabilities = EXCLUDED.capabilities,
|
||
deploy_host = EXCLUDED.deploy_host,
|
||
deploy_port = EXCLUDED.deploy_port,
|
||
updated_at = NOW()
|
||
RETURNING *`,
|
||
[
|
||
module_id, name, module_type || 'agent', owner || 'zhuyuan',
|
||
JSON.stringify(config || {}), JSON.stringify(capabilities || []),
|
||
deploy_host || null, deploy_port || null
|
||
]
|
||
);
|
||
|
||
return { success: true, module: result.rows[0] };
|
||
}
|
||
|
||
/**
|
||
* 获取活模块详情
|
||
*/
|
||
async function getModule(input) {
|
||
const { module_id } = input;
|
||
if (!module_id) throw new Error('缺少 module_id');
|
||
|
||
const result = await db.query(
|
||
`SELECT * FROM living_modules WHERE module_id = $1`,
|
||
[module_id]
|
||
);
|
||
|
||
if (result.rows.length === 0) {
|
||
return { found: false, module: null };
|
||
}
|
||
|
||
// 获取最近的诊断记录
|
||
const diagnosis = await db.query(
|
||
`SELECT * FROM module_diagnoses WHERE module_id = $1 ORDER BY created_at DESC LIMIT 1`,
|
||
[module_id]
|
||
);
|
||
|
||
// 获取未解决的报警
|
||
const alerts = await db.query(
|
||
`SELECT * FROM module_alerts WHERE module_id = $1 AND resolved = FALSE ORDER BY created_at DESC LIMIT 5`,
|
||
[module_id]
|
||
);
|
||
|
||
return {
|
||
found: true,
|
||
module: result.rows[0],
|
||
lastDiagnosis: diagnosis.rows[0] || null,
|
||
unresolvedAlerts: alerts.rows
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 列出所有活模块
|
||
*/
|
||
async function listModules(input) {
|
||
const { status, module_type, limit } = input || {};
|
||
|
||
let sql = 'SELECT * FROM living_modules WHERE 1=1';
|
||
const params = [];
|
||
|
||
if (status) {
|
||
params.push(status);
|
||
sql += ` AND status = $${params.length}`;
|
||
}
|
||
if (module_type) {
|
||
params.push(module_type);
|
||
sql += ` AND module_type = $${params.length}`;
|
||
}
|
||
|
||
sql += ' ORDER BY registered_at';
|
||
|
||
if (limit) {
|
||
params.push(limit);
|
||
sql += ` LIMIT $${params.length}`;
|
||
}
|
||
|
||
const result = await db.query(sql, params);
|
||
|
||
// 统计概览
|
||
const allModules = await db.query('SELECT status, COUNT(*) as count FROM living_modules GROUP BY status');
|
||
const statusSummary = {};
|
||
for (const row of allModules.rows) {
|
||
statusSummary[row.status] = parseInt(row.count, 10);
|
||
}
|
||
|
||
return {
|
||
total: result.rows.length,
|
||
statusSummary,
|
||
modules: result.rows
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 远程心跳上报
|
||
*/
|
||
async function moduleHeartbeat(input) {
|
||
const { module_id, health_score, cpu_usage, memory_usage, active_tasks, details } = input;
|
||
if (!module_id) throw new Error('缺少 module_id');
|
||
|
||
const score = typeof health_score === 'number' ? health_score : 100;
|
||
|
||
// 更新模块状态
|
||
let status = 'alive';
|
||
if (score <= 30) status = 'degraded';
|
||
if (score <= 0) status = 'dead';
|
||
|
||
await db.query(
|
||
`UPDATE living_modules SET last_heartbeat_at = NOW(), health_score = $1, status = $2, updated_at = NOW() WHERE module_id = $3`,
|
||
[score, status, module_id]
|
||
);
|
||
|
||
// 记录心跳
|
||
await db.query(
|
||
`INSERT INTO module_heartbeats (module_id, status, health_score, cpu_usage, memory_usage, active_tasks, details)
|
||
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||
[module_id, status, score, cpu_usage || null, memory_usage || null, active_tasks || 0, JSON.stringify(details || {})]
|
||
);
|
||
|
||
return { success: true, status, health_score: score };
|
||
}
|
||
|
||
/**
|
||
* 获取模块诊断记录
|
||
*/
|
||
async function diagnoseModule(input) {
|
||
const { module_id, limit } = input;
|
||
if (!module_id) throw new Error('缺少 module_id');
|
||
|
||
const result = await db.query(
|
||
`SELECT * FROM module_diagnoses WHERE module_id = $1 ORDER BY created_at DESC LIMIT $2`,
|
||
[module_id, limit || 10]
|
||
);
|
||
|
||
return { module_id, diagnoses: result.rows };
|
||
}
|
||
|
||
/**
|
||
* 获取模块报警记录
|
||
*/
|
||
async function getModuleAlerts(input) {
|
||
const { module_id, resolved, severity, limit } = input || {};
|
||
|
||
let sql = 'SELECT * FROM module_alerts WHERE 1=1';
|
||
const params = [];
|
||
|
||
if (module_id) {
|
||
params.push(module_id);
|
||
sql += ` AND module_id = $${params.length}`;
|
||
}
|
||
if (typeof resolved === 'boolean') {
|
||
params.push(resolved);
|
||
sql += ` AND resolved = $${params.length}`;
|
||
}
|
||
if (severity) {
|
||
params.push(severity);
|
||
sql += ` AND severity = $${params.length}`;
|
||
}
|
||
|
||
sql += ' ORDER BY created_at DESC';
|
||
params.push(limit || 20);
|
||
sql += ` LIMIT $${params.length}`;
|
||
|
||
const result = await db.query(sql, params);
|
||
return { alerts: result.rows };
|
||
}
|
||
|
||
/**
|
||
* 获取模块学习记录
|
||
*/
|
||
async function getModuleLearnings(input) {
|
||
const { module_id, learning_source, limit } = input || {};
|
||
|
||
let sql = 'SELECT * FROM module_learning_logs WHERE 1=1';
|
||
const params = [];
|
||
|
||
if (module_id) {
|
||
params.push(module_id);
|
||
sql += ` AND module_id = $${params.length}`;
|
||
}
|
||
if (learning_source) {
|
||
params.push(learning_source);
|
||
sql += ` AND learning_source = $${params.length}`;
|
||
}
|
||
|
||
sql += ' ORDER BY created_at DESC';
|
||
params.push(limit || 20);
|
||
sql += ` LIMIT $${params.length}`;
|
||
|
||
const result = await db.query(sql, params);
|
||
return { learnings: result.rows };
|
||
}
|
||
|
||
/**
|
||
* 发送HLDP消息(通过MCP接口)
|
||
*/
|
||
async function sendHLDP(input) {
|
||
const { from_module, to_module, msg_type, payload } = input;
|
||
if (!from_module || !msg_type) throw new Error('缺少 from_module 或 msg_type');
|
||
|
||
const { v4: uuidv4 } = require('uuid');
|
||
const messageId = uuidv4();
|
||
|
||
await db.query(
|
||
`INSERT INTO hldp_messages (message_id, from_module, to_module, msg_type, payload, status, expires_at)
|
||
VALUES ($1, $2, $3, $4, $5, 'pending', NOW() + INTERVAL '5 minutes')`,
|
||
[messageId, from_module, to_module || null, msg_type, JSON.stringify(payload || {})]
|
||
);
|
||
|
||
return { success: true, message_id: messageId };
|
||
}
|
||
|
||
/**
|
||
* 获取HLDP消息统计
|
||
*/
|
||
async function getHLDPStats(input) {
|
||
const stats = await db.query(
|
||
`SELECT msg_type, status, COUNT(*) as count
|
||
FROM hldp_messages
|
||
WHERE created_at > NOW() - INTERVAL '24 hours'
|
||
GROUP BY msg_type, status
|
||
ORDER BY msg_type, status`
|
||
);
|
||
|
||
const pending = await db.query(
|
||
`SELECT COUNT(*) as count FROM hldp_messages WHERE status = 'pending'`
|
||
);
|
||
|
||
return {
|
||
last24h: stats.rows,
|
||
pendingMessages: parseInt(pending.rows[0]?.count || '0', 10),
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
}
|
||
|
||
module.exports = {
|
||
registerModule,
|
||
getModule,
|
||
listModules,
|
||
moduleHeartbeat,
|
||
diagnoseModule,
|
||
getModuleAlerts,
|
||
getModuleLearnings,
|
||
sendHLDP,
|
||
getHLDPStats
|
||
};
|