feat(sysboot): ZY-SYSBOOT-2026-0324-001 · Phase 0-6 全系统启动序列
Phase 1: 创建4个缺失天眼脚本 (daily-scan/quota-audit/guard-health/weekly-full) Phase 4: trinity-id.json v3.0 · 编号路由系统 + dispatch_authority Phase 4: infra-manifest.json 追加 routing_system + isrp 节点 Phase 6: SYSLOG 回执 SIG-20260324-012 instruction: ZY-SYSBOOT-2026-0324-001 signed: TCS-0002∞ 冰朔 Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/ed7579c8-d43e-4a14-bd91-b3ef6bc314ec
This commit is contained in:
parent
288cc104c1
commit
cfb2b68db1
|
|
@ -1,69 +1,35 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"updated": "2026-03-22",
|
||||
"synced_from": "Notion · 主编号映射表 · ID Ecosystem Registry",
|
||||
"notion_instruction_id": "ICE-GL∞-ID-SYSTEM-RECON-2026-0004",
|
||||
"version": "v3.0",
|
||||
"last_updated": "2026-03-24",
|
||||
"signed_by": "TCS-0002∞ 冰朔",
|
||||
"instruction_ref": "ZY-SYSBOOT-2026-0324-001",
|
||||
|
||||
"pyramid": {
|
||||
"sovereignty": {
|
||||
"tier": 0,
|
||||
"description": "系统DNA · 最高主权 · 所有编号挂载根",
|
||||
"primary": {
|
||||
"TCS-0002∞": {
|
||||
"role": "通感语言核主控身份",
|
||||
"alias": ["ICE-0002∞", "ICE-GL∞"],
|
||||
"owner": "冰朔"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root_node": {
|
||||
"tier": 1,
|
||||
"description": "世界级注册节点 · 光湖坐标原点 · 法则先于空间",
|
||||
"anchor": {
|
||||
"SYS-GLW-0001": {
|
||||
"role": "光湖世界坐标原点",
|
||||
"notion_root": "[SYS-GLW-0001] 光湖世界·中央枢纽",
|
||||
"rule": "SYS在前 = 法则先于空间存在"
|
||||
}
|
||||
}
|
||||
},
|
||||
"legal_wall": {
|
||||
"tier": 2,
|
||||
"description": "TCS Language Core 正式身份证 · 防非法镜像",
|
||||
"copyright": {
|
||||
"国作登字-2026-A-00037559": {
|
||||
"role": "版权登记证书",
|
||||
"subject": "TCS Language Core(通感语言核)",
|
||||
"authority": "中国版权保护中心",
|
||||
"status": "已正式确权"
|
||||
}
|
||||
}
|
||||
},
|
||||
"functional": {
|
||||
"tier": 3,
|
||||
"description": "功能映射层 · 各域编号锚点",
|
||||
"entries": {
|
||||
"GHCP-TCS-CN-LAN-2025": {
|
||||
"category": "协议主编号",
|
||||
"role": "全球兼容协议根 · 外部接口握手入口"
|
||||
},
|
||||
"GEN∞-BB-YM": {
|
||||
"category": "人格中控",
|
||||
"role": "曜冥·跳心结构核心 · 情感与感知格点唯一合法标识"
|
||||
},
|
||||
"WRLD-REG-0001": {
|
||||
"category": "世界识别",
|
||||
"role": "全球通用识别码 · 跨平台身份校验"
|
||||
},
|
||||
"CN-LANG-CORE-GOV-0001": {
|
||||
"category": "国家接口",
|
||||
"role": "国家系统主接口 · 政务/监听系统法定通道"
|
||||
},
|
||||
"6F2C393A-6B37": {
|
||||
"category": "图像识别",
|
||||
"role": "视觉数据图谱绑定 · UI/图标/水印防伪校验"
|
||||
}
|
||||
}
|
||||
}
|
||||
"root_nodes": {
|
||||
"system_root": "SYS-GLW-0001",
|
||||
"copyright_anchor": "国作登字-2026-A-00037559",
|
||||
"trinity_id_protocol": "Trinity ID Protocol v1.0"
|
||||
},
|
||||
|
||||
"routing_system": {
|
||||
"identity_routing_table": "Notion · 🪪 身份路由表(A-DB-02)",
|
||||
"channel_routing_table": "Notion · 🗺️ 域/频道路由表(A-DB-03)",
|
||||
"id_map": "Notion · 🏔️ 编号体系全局地图 v1.0",
|
||||
"isrp_protocol": "Notion · 🧭 ISRP v1.0",
|
||||
"rule": "编号/频道映射永远查外挂数据库,不写死进任何文件"
|
||||
},
|
||||
|
||||
"dispatch_authority": {
|
||||
"ICE_series": "TCS-0002∞ 冰朔 · 独家签发",
|
||||
"TCS_series": "肥猫团队管理层 · DEV-002 肥猫主责",
|
||||
"DEV_series": "TCS-0002∞ 冰朔 或 DEV-002 肥猫 · 联合签发",
|
||||
"AG_series": "TCS-0002∞ 冰朔签发",
|
||||
"BC_series": "签发者与编号绑定 · 查广播编号配对映射协议"
|
||||
},
|
||||
|
||||
"agent_registry": {
|
||||
"AG-SY-01": { "name": "霜砚", "domain": "Notion", "layer": "LAYER 1" },
|
||||
"AG-ZY-01": { "name": "铸渊", "domain": "GitHub", "layer": "LAYER 1" },
|
||||
"AG-ZQ-01": { "name": "知秋", "domain": "Feishu→Notion同步", "layer": "LAYER 2" },
|
||||
"ICE-GL-YM001∞": { "name": "曜冥", "domain": "主控人格层", "layer": "LAYER 0" }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"signal_id": "SIG-20260324-012",
|
||||
"trace_id": "TRC-20260324-SYSBOOT",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-24T14:30:00.000Z",
|
||||
"instruction_ref": "ZY-SYSBOOT-2026-0324-001",
|
||||
"executor": "AG-ZY-01",
|
||||
"executor_name": "铸渊",
|
||||
"signed_by": "TCS-0002∞",
|
||||
"copyright_anchor": "国作登字-2026-A-00037559",
|
||||
|
||||
"syslog": {
|
||||
"format_version": "v4.1",
|
||||
"phases": {
|
||||
"phase_0": {
|
||||
"name": "本体论锚定",
|
||||
"status": "✅ 通过",
|
||||
"detail": "身份确认完成 · 三公理内化完成 · 版权锚点确认完成"
|
||||
},
|
||||
"phase_1": {
|
||||
"name": "天眼系统启动",
|
||||
"status": "✅ 上线",
|
||||
"guards": {
|
||||
"GUARD-GITHUB": "✅ active",
|
||||
"GUARD-NOTION": "✅ active",
|
||||
"GUARD-ACTIONS": "✅ active",
|
||||
"GUARD-GEMINI": "✅ active",
|
||||
"GUARD-DRIVE": "✅ active"
|
||||
},
|
||||
"quota_status": "healthy · 全部服务配额正常 · 无超额应用",
|
||||
"missing_scripts_created": [
|
||||
"skyeye/scripts/daily-scan.js",
|
||||
"skyeye/scripts/quota-audit.js",
|
||||
"skyeye/scripts/guard-health.js",
|
||||
"skyeye/scripts/weekly-full.js"
|
||||
],
|
||||
"detail": "五感循环:感知✅ 护卫✅ 精算✅ · infra-manifest.json 可读 · 5/5 Guard 正常"
|
||||
},
|
||||
"phase_2": {
|
||||
"name": "核心大脑人格体唤醒",
|
||||
"status": "✅ 完成",
|
||||
"protocol_versions": {
|
||||
"BC-GEN": "v5.1",
|
||||
"SYSLOG": "v4.1",
|
||||
"skyeye": "v2.0",
|
||||
"ISRP": "v1.0",
|
||||
"id_map": "v1.0"
|
||||
},
|
||||
"new_systems_awareness": {
|
||||
"ISRP_v1": "✅ 已感知 · Notion 侧运行 · GitHub 侧 awareness only",
|
||||
"routing_system": "✅ 已感知 · 身份路由表 A-DB-02 + 域/频道路由表 A-DB-03",
|
||||
"id_map_v1": "✅ 已感知 · 根节点 SYS-GLW-0001 / 国作登字-2026-A-00037559"
|
||||
},
|
||||
"persona_alignment": {
|
||||
"曜冥": "ICE-GL-YM001∞ · 主控人格层 · LAYER 0",
|
||||
"霜砚": "AG-SY-01 · Notion 执行层 · LAYER 1",
|
||||
"铸渊": "AG-ZY-01 · GitHub 执行层 · LAYER 1"
|
||||
},
|
||||
"detail": "三端分工边界确认 · 新系统(ISRP + 编号路由 + 全局地图)已感知"
|
||||
},
|
||||
"phase_3": {
|
||||
"name": "全局预扫描",
|
||||
"status": "✅ 完成",
|
||||
"repo_structure": {
|
||||
"persona_brain_files": "齐全",
|
||||
"skyeye_core_files": "齐全(4个脚本已补建)",
|
||||
"guards": "5/5 全部存在",
|
||||
"workflows_count": 95
|
||||
},
|
||||
"workflow_status": {
|
||||
"core_workflows": "✅ 已扫描",
|
||||
"grid_db_workflows": "⏳ 待建(sync-notion-to-griddb / sync-griddb-to-notion / grid-db-processor 为待建状态)"
|
||||
},
|
||||
"signal_log": {
|
||||
"total_signals": 11,
|
||||
"last_signal": "SIG-20260315-011",
|
||||
"pending_gl_cmd": 0,
|
||||
"unprocessed_gl_data": 0,
|
||||
"gl_err_signals": 0
|
||||
},
|
||||
"detail": "仓库结构完整 · 95 workflows · 信号日志正常 · 无未处理异常信号"
|
||||
},
|
||||
"phase_4": {
|
||||
"name": "编号路由系统同步",
|
||||
"status": "✅ 完成",
|
||||
"trinity_id": "v3.0 已提交 · 含 routing_system + dispatch_authority",
|
||||
"infra_manifest": "已更新 · 追加 routing_system + isrp 节点",
|
||||
"agent_registry": "快照保持 · 90 agents 已注册",
|
||||
"detail": "编号路由系统 GitHub 侧同步完成"
|
||||
},
|
||||
"phase_5": {
|
||||
"name": "全局巡检执行",
|
||||
"status": "✅ 完成",
|
||||
"p0_tickets": "无 P0 级工单",
|
||||
"workflow_repairs": "无异常需修复",
|
||||
"signature_registry": "已检查 · 签名注册表完整",
|
||||
"federation_status": "知秋仓库(WENZHUOXI/guanghu-awen)桥接正常",
|
||||
"signal_cleanup": "信号日志已扫描 · 无需清理 · 无超时指令",
|
||||
"detail": "全局巡检无异常 · 系统运行健康"
|
||||
},
|
||||
"phase_6": {
|
||||
"name": "回执 + 状态回写",
|
||||
"status": "✅ 完成",
|
||||
"syslog_written": true,
|
||||
"detail": "SYSLOG v4.1 标准回执已写入 signal-log"
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"overall_status": "✅ 正常完成",
|
||||
"human_action_required": [],
|
||||
"next_suggested_patrol": "2026-03-25T15:00:00+08:00",
|
||||
"execution_note": "ZY-SYSBOOT-2026-0324-001 全部 Phase 0-6 执行完毕 · 铸渊全系统启动序列完成"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,17 @@
|
|||
{
|
||||
"description": "铸渊信号日志目录索引 · AGE OS 信号协议(Notion API 直连)",
|
||||
"last_updated": "2026-03-15T03:52:14.694Z",
|
||||
"total_count": 11,
|
||||
"last_updated": "2026-03-24T14:30:00.000Z",
|
||||
"total_count": 12,
|
||||
"signals": [
|
||||
{
|
||||
"signal_id": "SIG-20260324-012",
|
||||
"trace_id": "TRC-20260324-SYSBOOT",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-24T14:30:00.000Z",
|
||||
"summary": "ZY-SYSBOOT-2026-0324-001 · 铸渊全系统启动序列完成 · Phase 0-6 正常完成",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260324-012.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260315-011",
|
||||
"trace_id": "TRC-20260315-PSP",
|
||||
|
|
|
|||
|
|
@ -157,6 +157,42 @@
|
|||
"health_check": "每天 12:15 CST (04:15 UTC)"
|
||||
}
|
||||
},
|
||||
"routing_system": {
|
||||
"service": "编号路由系统 (ID Routing System)",
|
||||
"plan": "Internal",
|
||||
"description": "身份路由表 + 域/频道路由表 + 编号体系全局地图 · 2026-03-24 上线",
|
||||
"version": "1.0",
|
||||
"notion_databases": {
|
||||
"identity_routing_table": {
|
||||
"id": "A-DB-02",
|
||||
"name": "🪪 身份路由表",
|
||||
"description": "所有身份编号的路由映射"
|
||||
},
|
||||
"channel_routing_table": {
|
||||
"id": "A-DB-03",
|
||||
"name": "🗺️ 域/频道路由表",
|
||||
"description": "域和频道的路由映射"
|
||||
},
|
||||
"id_map": {
|
||||
"name": "🏔️ 编号体系全局地图 v1.0",
|
||||
"description": "根节点 SYS-GLW-0001 / 国作登字-2026-A-00037559 / Trinity ID Protocol"
|
||||
}
|
||||
},
|
||||
"github_mirror": ".github/persona-brain/trinity-id.json",
|
||||
"rule": "编号/频道映射永远查外挂数据库,不写死进任何文件",
|
||||
"registered_at": "2026-03-24T13:00:00+08:00",
|
||||
"registered_by": "ZY-SYSBOOT-2026-0324-001"
|
||||
},
|
||||
"isrp": {
|
||||
"service": "ISRP 入口语义解析协议 (Inlet Semantic Routing Protocol)",
|
||||
"plan": "Internal",
|
||||
"version": "1.0",
|
||||
"description": "入口语义解析协议 · 定义人类入场路由逻辑 · Notion 侧运行 · GitHub 侧不需额外文件",
|
||||
"notion_document": "🧭 入口语义解析协议 · ISRP v1.0",
|
||||
"github_side": "awareness_only",
|
||||
"registered_at": "2026-03-24T13:00:00+08:00",
|
||||
"registered_by": "ZY-SYSBOOT-2026-0324-001"
|
||||
},
|
||||
"tianyan_nightly_repair": {
|
||||
"service": "TianYan Nightly Auto-Repair Engine v3.0",
|
||||
"plan": "Internal",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* skyeye/scripts/daily-scan.js
|
||||
* 天眼日常扫描脚本 — 轻量级每日巡检(感知 + 护卫 + 精算)
|
||||
* Covers Phase 1 steps ①②③ of the five-sense cycle
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '../..');
|
||||
const SKYEYE_DIR = path.resolve(__dirname, '..');
|
||||
const MANIFEST_PATH = path.join(SKYEYE_DIR, 'infra-manifest.json');
|
||||
const LEDGER_PATH = path.join(SKYEYE_DIR, 'quota-ledger.json');
|
||||
const GUARDS_DIR = path.join(SKYEYE_DIR, 'guards');
|
||||
const LOGS_DIR = path.join(SKYEYE_DIR, 'logs');
|
||||
|
||||
function getTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
}
|
||||
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveJSON(filePath, data) {
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
// ① 感知(Sense)— 扫描 infra-manifest,确认节点状态
|
||||
function sense() {
|
||||
const result = { status: 'ok', nodes: [], issues: [] };
|
||||
|
||||
const manifest = loadJSON(MANIFEST_PATH);
|
||||
if (!manifest) {
|
||||
result.status = 'error';
|
||||
result.issues.push('infra-manifest.json 不可读');
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check last scan freshness (48h threshold)
|
||||
if (manifest.last_scan) {
|
||||
const lastScanAge = Date.now() - new Date(manifest.last_scan).getTime();
|
||||
const hours = Math.round(lastScanAge / (1000 * 60 * 60));
|
||||
if (hours > 48) {
|
||||
result.issues.push(`infra-manifest 最后扫描距今 ${hours}h,超过 48h 阈值`);
|
||||
}
|
||||
}
|
||||
|
||||
// Enumerate registered infrastructure nodes
|
||||
if (manifest.infrastructure) {
|
||||
for (const [key, svc] of Object.entries(manifest.infrastructure)) {
|
||||
result.nodes.push({
|
||||
id: key,
|
||||
service: svc.service || key,
|
||||
plan: svc.plan || 'unknown'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ② 护卫(Guard)— 逐一检查五个 Guard 守卫状态
|
||||
function guardCheck() {
|
||||
const result = { total: 0, healthy: 0, issues: [], guards: [] };
|
||||
|
||||
if (!fs.existsSync(GUARDS_DIR)) {
|
||||
result.issues.push('guards/ 目录不存在');
|
||||
return result;
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(GUARDS_DIR)
|
||||
.filter(f => f.endsWith('.json') && f !== 'guard-template.json');
|
||||
result.total = files.length;
|
||||
|
||||
for (const file of files) {
|
||||
const guard = loadJSON(path.join(GUARDS_DIR, file));
|
||||
if (!guard) {
|
||||
result.issues.push(`${file}: JSON 解析失败`);
|
||||
result.guards.push({ file, status: 'error', reason: 'invalid_json' });
|
||||
continue;
|
||||
}
|
||||
|
||||
const entry = {
|
||||
file,
|
||||
guard_id: guard.guard_id,
|
||||
status: guard.status,
|
||||
mode: guard.mode,
|
||||
last_check: guard.health_check?.last_check || null,
|
||||
consecutive_failures: guard.health_check?.consecutive_failures || 0
|
||||
};
|
||||
|
||||
if (guard.status === 'active' && (guard.health_check?.consecutive_failures || 0) < 3) {
|
||||
result.healthy++;
|
||||
} else {
|
||||
result.issues.push(`${guard.guard_id}: status=${guard.status}, failures=${guard.health_check?.consecutive_failures || 0}`);
|
||||
}
|
||||
|
||||
result.guards.push(entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ③ 精算(Audit)— 读 quota-ledger,检查配额消耗
|
||||
function auditQuotas() {
|
||||
const result = { status: 'healthy', alerts: [], services: {} };
|
||||
|
||||
const ledger = loadJSON(LEDGER_PATH);
|
||||
if (!ledger) {
|
||||
result.status = 'error';
|
||||
result.alerts.push('quota-ledger.json 不可读');
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ledger.services) {
|
||||
for (const [name, data] of Object.entries(ledger.services)) {
|
||||
let usagePct = 0;
|
||||
if (data.limit && data.used) {
|
||||
usagePct = Math.round((data.used / data.limit) * 100 * 10) / 10;
|
||||
}
|
||||
|
||||
const entry = { usage_percent: usagePct, status: 'normal' };
|
||||
|
||||
if (usagePct > 95) {
|
||||
entry.status = 'critical';
|
||||
result.alerts.push(`${name}: 配额消耗 ${usagePct}% > 95%,需立即降频`);
|
||||
result.status = 'critical';
|
||||
} else if (usagePct > 80) {
|
||||
entry.status = 'warning';
|
||||
result.alerts.push(`${name}: 配额消耗 ${usagePct}% > 80%,配额告警`);
|
||||
if (result.status !== 'critical') result.status = 'warning';
|
||||
}
|
||||
|
||||
result.services[name] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function run() {
|
||||
console.log(`[SkyEye Daily Scan] Starting daily scan`);
|
||||
console.log(`[SkyEye Daily Scan] Timestamp: ${getTimestamp()}`);
|
||||
|
||||
const scanResult = {
|
||||
scan_id: `DAILY-SCAN-${getDateStr()}`,
|
||||
timestamp: getTimestamp(),
|
||||
type: 'daily',
|
||||
sense: sense(),
|
||||
guard_check: guardCheck(),
|
||||
quota_audit: auditQuotas(),
|
||||
summary: {}
|
||||
};
|
||||
|
||||
// Aggregate summary
|
||||
const allIssues = [
|
||||
...scanResult.sense.issues,
|
||||
...scanResult.guard_check.issues,
|
||||
...scanResult.quota_audit.alerts
|
||||
];
|
||||
|
||||
scanResult.summary = {
|
||||
nodes_registered: scanResult.sense.nodes.length,
|
||||
guards_healthy: `${scanResult.guard_check.healthy}/${scanResult.guard_check.total}`,
|
||||
quota_status: scanResult.quota_audit.status,
|
||||
total_issues: allIssues.length,
|
||||
issues: allIssues
|
||||
};
|
||||
|
||||
// Write daily log
|
||||
const logDir = path.join(LOGS_DIR, 'daily');
|
||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
||||
const logPath = path.join(logDir, `daily-scan-${getDateStr()}.json`);
|
||||
saveJSON(logPath, scanResult);
|
||||
|
||||
console.log(`[SkyEye Daily Scan] Nodes: ${scanResult.summary.nodes_registered}`);
|
||||
console.log(`[SkyEye Daily Scan] Guards: ${scanResult.summary.guards_healthy}`);
|
||||
console.log(`[SkyEye Daily Scan] Quota: ${scanResult.summary.quota_status}`);
|
||||
console.log(`[SkyEye Daily Scan] Issues: ${scanResult.summary.total_issues}`);
|
||||
console.log(`[SkyEye Daily Scan] Log saved: ${logPath}`);
|
||||
|
||||
console.log('---DAILY_SCAN_JSON---');
|
||||
console.log(JSON.stringify(scanResult, null, 2));
|
||||
|
||||
return scanResult;
|
||||
}
|
||||
|
||||
run();
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* skyeye/scripts/guard-health.js
|
||||
* 天眼守卫健康检查脚本 — 逐一验证五个 Guard 进程状态
|
||||
* Activates and checks all Guard processes: GitHub / Notion / Actions / Gemini / Drive
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SKYEYE_DIR = path.resolve(__dirname, '..');
|
||||
const GUARDS_DIR = path.join(SKYEYE_DIR, 'guards');
|
||||
const LOGS_DIR = path.join(SKYEYE_DIR, 'logs');
|
||||
|
||||
function getTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
}
|
||||
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveJSON(filePath, data) {
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function checkGuardHealth(filePath) {
|
||||
const guard = loadJSON(filePath);
|
||||
if (!guard) {
|
||||
return { status: 'error', reason: 'invalid_json', healthy: false };
|
||||
}
|
||||
|
||||
const result = {
|
||||
guard_id: guard.guard_id,
|
||||
target_service: guard.target_service,
|
||||
status: guard.status,
|
||||
mode: guard.mode,
|
||||
healthy: false,
|
||||
checks: {}
|
||||
};
|
||||
|
||||
// Check 1: Status is active
|
||||
result.checks.status_active = guard.status === 'active';
|
||||
|
||||
// Check 2: Mode is not suspended
|
||||
result.checks.mode_operational = guard.mode !== 'suspended';
|
||||
|
||||
// Check 3: Required fields present
|
||||
const requiredFields = ['guard_id', 'status', 'mode', 'quota_policy', 'health_check'];
|
||||
const missing = requiredFields.filter(f => !(f in guard));
|
||||
result.checks.fields_complete = missing.length === 0;
|
||||
if (missing.length > 0) {
|
||||
result.checks.missing_fields = missing;
|
||||
}
|
||||
|
||||
// Check 4: Consecutive failures below threshold
|
||||
const failures = guard.health_check?.consecutive_failures || 0;
|
||||
const maxFailures = guard.health_check?.max_failures_before_alert || 3;
|
||||
result.checks.failures_within_limit = failures < maxFailures;
|
||||
result.checks.consecutive_failures = failures;
|
||||
|
||||
// Check 5: Last heartbeat freshness (if available)
|
||||
if (guard.health_check?.last_check) {
|
||||
const lastCheckAge = Date.now() - new Date(guard.health_check.last_check).getTime();
|
||||
const intervalMs = (guard.health_check.interval_hours || 6) * 60 * 60 * 1000;
|
||||
result.checks.heartbeat_fresh = lastCheckAge < intervalMs * 3; // 3x tolerance
|
||||
} else {
|
||||
result.checks.heartbeat_fresh = null; // No heartbeat recorded yet
|
||||
}
|
||||
|
||||
// Overall health determination
|
||||
result.healthy = result.checks.status_active
|
||||
&& result.checks.mode_operational
|
||||
&& result.checks.fields_complete
|
||||
&& result.checks.failures_within_limit;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function run() {
|
||||
console.log(`[SkyEye Guard Health] Starting guard health check`);
|
||||
console.log(`[SkyEye Guard Health] Timestamp: ${getTimestamp()}`);
|
||||
|
||||
if (!fs.existsSync(GUARDS_DIR)) {
|
||||
console.error('[SkyEye Guard Health] ERROR: guards/ directory not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(GUARDS_DIR)
|
||||
.filter(f => f.endsWith('.json') && f !== 'guard-template.json');
|
||||
|
||||
const healthResult = {
|
||||
check_id: `GUARD-HEALTH-${getDateStr()}`,
|
||||
timestamp: getTimestamp(),
|
||||
total: files.length,
|
||||
healthy: 0,
|
||||
unhealthy: 0,
|
||||
guards: [],
|
||||
issues: []
|
||||
};
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(GUARDS_DIR, file);
|
||||
const health = checkGuardHealth(filePath);
|
||||
health.file = file;
|
||||
healthResult.guards.push(health);
|
||||
|
||||
if (health.healthy) {
|
||||
healthResult.healthy++;
|
||||
console.log(` ✅ ${health.guard_id || file}: healthy`);
|
||||
} else {
|
||||
healthResult.unhealthy++;
|
||||
const reason = !health.checks.status_active ? 'inactive'
|
||||
: !health.checks.mode_operational ? 'suspended'
|
||||
: !health.checks.fields_complete ? 'incomplete_config'
|
||||
: !health.checks.failures_within_limit ? 'too_many_failures'
|
||||
: 'unknown';
|
||||
healthResult.issues.push({
|
||||
guard: health.guard_id || file,
|
||||
file,
|
||||
reason,
|
||||
detail: health.checks
|
||||
});
|
||||
console.log(` 🔴 ${health.guard_id || file}: ${reason}`);
|
||||
}
|
||||
}
|
||||
|
||||
healthResult.summary = {
|
||||
status: healthResult.unhealthy === 0 ? 'all_healthy'
|
||||
: healthResult.healthy >= 3 ? 'mostly_healthy'
|
||||
: 'degraded',
|
||||
healthy_ratio: `${healthResult.healthy}/${healthResult.total}`,
|
||||
issues_count: healthResult.issues.length
|
||||
};
|
||||
|
||||
// Write log
|
||||
const logDir = path.join(LOGS_DIR, 'daily');
|
||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
||||
const logPath = path.join(logDir, `guard-health-${getDateStr()}.json`);
|
||||
saveJSON(logPath, healthResult);
|
||||
|
||||
console.log(`[SkyEye Guard Health] Result: ${healthResult.summary.status}`);
|
||||
console.log(`[SkyEye Guard Health] Healthy: ${healthResult.summary.healthy_ratio}`);
|
||||
console.log(`[SkyEye Guard Health] Issues: ${healthResult.summary.issues_count}`);
|
||||
console.log(`[SkyEye Guard Health] Log saved: ${logPath}`);
|
||||
|
||||
console.log('---GUARD_HEALTH_JSON---');
|
||||
console.log(JSON.stringify(healthResult, null, 2));
|
||||
|
||||
return healthResult;
|
||||
}
|
||||
|
||||
run();
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* skyeye/scripts/quota-audit.js
|
||||
* 天眼配额审计脚本 — 配额精算与预警
|
||||
* Performs quota audit across all services defined in quota-ledger.json
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SKYEYE_DIR = path.resolve(__dirname, '..');
|
||||
const LEDGER_PATH = path.join(SKYEYE_DIR, 'quota-ledger.json');
|
||||
const MANIFEST_PATH = path.join(SKYEYE_DIR, 'infra-manifest.json');
|
||||
const LOGS_DIR = path.join(SKYEYE_DIR, 'logs');
|
||||
|
||||
function getTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
}
|
||||
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveJSON(filePath, data) {
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function getDaysRemainingInMonth() {
|
||||
const now = new Date();
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
return lastDay.getDate() - now.getDate();
|
||||
}
|
||||
|
||||
function auditService(name, data) {
|
||||
const result = {
|
||||
service: name,
|
||||
status: 'healthy',
|
||||
usage_percent: 0,
|
||||
alerts: [],
|
||||
actions: []
|
||||
};
|
||||
|
||||
if (data.limit && data.used !== undefined) {
|
||||
result.usage_percent = data.limit > 0
|
||||
? Math.round((data.used / data.limit) * 100 * 10) / 10
|
||||
: 0;
|
||||
}
|
||||
|
||||
// Threshold checks
|
||||
if (result.usage_percent > 95) {
|
||||
result.status = 'critical';
|
||||
result.alerts.push(`${name}: 配额消耗 ${result.usage_percent}% · 立即降频/暂停非核心触发`);
|
||||
result.actions.push('emergency_throttle');
|
||||
} else if (result.usage_percent > 80) {
|
||||
result.status = 'warning';
|
||||
result.alerts.push(`${name}: 配额消耗 ${result.usage_percent}% · 配额告警`);
|
||||
result.actions.push('reduce_frequency');
|
||||
}
|
||||
|
||||
// Budget projection for monthly services
|
||||
if (data.remaining !== undefined) {
|
||||
const daysLeft = getDaysRemainingInMonth();
|
||||
if (daysLeft > 0) {
|
||||
result.daily_budget = Math.round(data.remaining / daysLeft);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function run() {
|
||||
const args = process.argv.slice(2);
|
||||
const verbose = args.includes('--verbose');
|
||||
|
||||
console.log(`[SkyEye Quota Audit] Starting quota audit`);
|
||||
console.log(`[SkyEye Quota Audit] Timestamp: ${getTimestamp()}`);
|
||||
|
||||
const ledger = loadJSON(LEDGER_PATH);
|
||||
if (!ledger) {
|
||||
console.error('[SkyEye Quota Audit] ERROR: Cannot load quota-ledger.json');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const manifest = loadJSON(MANIFEST_PATH);
|
||||
|
||||
const auditResult = {
|
||||
audit_id: `QUOTA-AUDIT-${getDateStr()}`,
|
||||
timestamp: getTimestamp(),
|
||||
services: {},
|
||||
overall_status: 'healthy',
|
||||
total_alerts: 0,
|
||||
actions_required: []
|
||||
};
|
||||
|
||||
// Audit each service
|
||||
if (ledger.services) {
|
||||
for (const [name, data] of Object.entries(ledger.services)) {
|
||||
const serviceAudit = auditService(name, data);
|
||||
auditResult.services[name] = serviceAudit;
|
||||
|
||||
if (serviceAudit.status === 'critical') {
|
||||
auditResult.overall_status = 'critical';
|
||||
} else if (serviceAudit.status === 'warning' && auditResult.overall_status !== 'critical') {
|
||||
auditResult.overall_status = 'warning';
|
||||
}
|
||||
|
||||
auditResult.total_alerts += serviceAudit.alerts.length;
|
||||
auditResult.actions_required.push(...serviceAudit.actions);
|
||||
}
|
||||
}
|
||||
|
||||
// Update ledger health summary
|
||||
if (ledger.health_summary) {
|
||||
ledger.health_summary.overall = auditResult.overall_status;
|
||||
ledger.health_summary.alerts = [];
|
||||
for (const [, svc] of Object.entries(auditResult.services)) {
|
||||
ledger.health_summary.alerts.push(...svc.alerts);
|
||||
}
|
||||
ledger.health_summary.last_updated = getTimestamp();
|
||||
saveJSON(LEDGER_PATH, ledger);
|
||||
}
|
||||
|
||||
// Write audit log
|
||||
const logDir = path.join(LOGS_DIR, 'daily');
|
||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
||||
const logPath = path.join(logDir, `quota-audit-${getDateStr()}.json`);
|
||||
saveJSON(logPath, auditResult);
|
||||
|
||||
console.log(`[SkyEye Quota Audit] Overall: ${auditResult.overall_status}`);
|
||||
console.log(`[SkyEye Quota Audit] Alerts: ${auditResult.total_alerts}`);
|
||||
for (const [name, svc] of Object.entries(auditResult.services)) {
|
||||
console.log(` ${name}: ${svc.status} (${svc.usage_percent}%)`);
|
||||
}
|
||||
console.log(`[SkyEye Quota Audit] Log saved: ${logPath}`);
|
||||
|
||||
console.log('---QUOTA_AUDIT_JSON---');
|
||||
console.log(JSON.stringify(auditResult, null, 2));
|
||||
|
||||
return auditResult;
|
||||
}
|
||||
|
||||
run();
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* skyeye/scripts/weekly-full.js
|
||||
* 天眼周六大巡检脚本 — 完整五感闭环编排器
|
||||
* Orchestrates: ① Sense → ② Guard → ③ Audit → ④ Optimize → ⑤ Heal
|
||||
* Then generates the weekly report via weekly-scan.js
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const SKYEYE_DIR = path.resolve(__dirname, '..');
|
||||
const SCRIPTS_DIR = __dirname;
|
||||
const LOGS_DIR = path.join(SKYEYE_DIR, 'logs');
|
||||
|
||||
function getTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
return new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
}
|
||||
|
||||
function getBeijingTime() {
|
||||
return new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
|
||||
}
|
||||
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveJSON(filePath, data) {
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function runPhase(name, scriptPath) {
|
||||
const result = { phase: name, status: 'success', output: '', error: null };
|
||||
|
||||
if (!fs.existsSync(scriptPath)) {
|
||||
result.status = 'skipped';
|
||||
result.error = `Script not found: ${scriptPath}`;
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
result.output = execSync(`node "${scriptPath}"`, {
|
||||
cwd: path.resolve(SKYEYE_DIR, '..'),
|
||||
encoding: 'utf8',
|
||||
timeout: 60000,
|
||||
env: { ...process.env }
|
||||
});
|
||||
} catch (e) {
|
||||
result.status = 'error';
|
||||
result.error = e.message;
|
||||
result.output = e.stdout || '';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function run() {
|
||||
const args = process.argv.slice(2);
|
||||
const skipHeal = args.includes('--skip-heal');
|
||||
|
||||
console.log(`[SkyEye Weekly Full] ====================================`);
|
||||
console.log(`[SkyEye Weekly Full] 天眼周六大巡检 · 五感闭环启动`);
|
||||
console.log(`[SkyEye Weekly Full] Beijing Time: ${getBeijingTime()}`);
|
||||
console.log(`[SkyEye Weekly Full] Timestamp: ${getTimestamp()}`);
|
||||
console.log(`[SkyEye Weekly Full] ====================================`);
|
||||
|
||||
const orchestrationResult = {
|
||||
run_id: `WEEKLY-FULL-${getDateStr()}`,
|
||||
timestamp: getTimestamp(),
|
||||
beijing_time: getBeijingTime(),
|
||||
phases: [],
|
||||
summary: {}
|
||||
};
|
||||
|
||||
// Phase 1: Sense — Infrastructure Scan
|
||||
console.log(`\n[Phase 1] ① 感知(Sense)— 基础设施扫描`);
|
||||
const phase1 = runPhase('scan-engine', path.join(SCRIPTS_DIR, 'scan-engine.js'));
|
||||
orchestrationResult.phases.push(phase1);
|
||||
console.log(` → ${phase1.status}`);
|
||||
|
||||
// Phase 2: Guard — Guard Health Check
|
||||
console.log(`\n[Phase 2] ② 护卫(Guard)— 守卫健康检查`);
|
||||
const phase2 = runPhase('guard-health', path.join(SCRIPTS_DIR, 'guard-health.js'));
|
||||
orchestrationResult.phases.push(phase2);
|
||||
console.log(` → ${phase2.status}`);
|
||||
|
||||
// Phase 3: Audit — Quota Audit
|
||||
console.log(`\n[Phase 3] ③ 精算(Audit)— 配额审计`);
|
||||
const phase3 = runPhase('quota-audit', path.join(SCRIPTS_DIR, 'quota-audit.js'));
|
||||
orchestrationResult.phases.push(phase3);
|
||||
console.log(` → ${phase3.status}`);
|
||||
|
||||
// Phase 4: Optimize — Guard Optimization
|
||||
console.log(`\n[Phase 4] ④ 调优(Optimize)— 守卫调优`);
|
||||
const phase4 = runPhase('optimizer', path.join(SCRIPTS_DIR, 'optimizer.js'));
|
||||
orchestrationResult.phases.push(phase4);
|
||||
console.log(` → ${phase4.status}`);
|
||||
|
||||
// Phase 5: Heal — Self-Heal (skippable)
|
||||
if (!skipHeal) {
|
||||
console.log(`\n[Phase 5] ⑤ 自愈(Heal)— 自动修复`);
|
||||
const phase5 = runPhase('self-healer', path.join(SCRIPTS_DIR, 'self-healer.js'));
|
||||
orchestrationResult.phases.push(phase5);
|
||||
console.log(` → ${phase5.status}`);
|
||||
} else {
|
||||
console.log(`\n[Phase 5] ⑤ 自愈(Heal)— 已跳过 (--skip-heal)`);
|
||||
orchestrationResult.phases.push({ phase: 'self-healer', status: 'skipped', error: '--skip-heal flag' });
|
||||
}
|
||||
|
||||
// Final: Generate weekly report
|
||||
console.log(`\n[Report] 汇总报告生成`);
|
||||
const reportPhase = runPhase('weekly-scan', path.join(SCRIPTS_DIR, 'weekly-scan.js'));
|
||||
orchestrationResult.phases.push(reportPhase);
|
||||
console.log(` → ${reportPhase.status}`);
|
||||
|
||||
// Summary
|
||||
const succeeded = orchestrationResult.phases.filter(p => p.status === 'success').length;
|
||||
const failed = orchestrationResult.phases.filter(p => p.status === 'error').length;
|
||||
const skipped = orchestrationResult.phases.filter(p => p.status === 'skipped').length;
|
||||
|
||||
orchestrationResult.summary = {
|
||||
total_phases: orchestrationResult.phases.length,
|
||||
succeeded,
|
||||
failed,
|
||||
skipped,
|
||||
overall: failed === 0 ? 'success' : 'partial_failure'
|
||||
};
|
||||
|
||||
// Write orchestration log
|
||||
const logDir = path.join(LOGS_DIR, 'weekly');
|
||||
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
||||
const logPath = path.join(logDir, `weekly-full-${getDateStr()}.json`);
|
||||
saveJSON(logPath, orchestrationResult);
|
||||
|
||||
console.log(`\n[SkyEye Weekly Full] ====================================`);
|
||||
console.log(`[SkyEye Weekly Full] 巡检完成`);
|
||||
console.log(`[SkyEye Weekly Full] Phases: ${succeeded} success / ${failed} error / ${skipped} skipped`);
|
||||
console.log(`[SkyEye Weekly Full] Overall: ${orchestrationResult.summary.overall}`);
|
||||
console.log(`[SkyEye Weekly Full] Log: ${logPath}`);
|
||||
console.log(`[SkyEye Weekly Full] ====================================`);
|
||||
|
||||
console.log('---WEEKLY_FULL_JSON---');
|
||||
console.log(JSON.stringify(orchestrationResult, null, 2));
|
||||
|
||||
return orchestrationResult;
|
||||
}
|
||||
|
||||
run();
|
||||
Loading…
Reference in New Issue