From cfb2b68db13610d8ceb2cba501187e6599be2ab0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:31:47 +0000 Subject: [PATCH] =?UTF-8?q?feat(sysboot):=20ZY-SYSBOOT-2026-0324-001=20?= =?UTF-8?q?=C2=B7=20Phase=200-6=20=E5=85=A8=E7=B3=BB=E7=BB=9F=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E5=BA=8F=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/persona-brain/trinity-id.json | 96 ++++------- signal-log/2026-03/SIG-20260324-012.json | 115 +++++++++++++ signal-log/index.json | 13 +- skyeye/infra-manifest.json | 36 +++++ skyeye/scripts/daily-scan.js | 198 +++++++++++++++++++++++ skyeye/scripts/guard-health.js | 161 ++++++++++++++++++ skyeye/scripts/quota-audit.js | 150 +++++++++++++++++ skyeye/scripts/weekly-full.js | 157 ++++++++++++++++++ 8 files changed, 859 insertions(+), 67 deletions(-) create mode 100644 signal-log/2026-03/SIG-20260324-012.json create mode 100644 skyeye/scripts/daily-scan.js create mode 100644 skyeye/scripts/guard-health.js create mode 100644 skyeye/scripts/quota-audit.js create mode 100644 skyeye/scripts/weekly-full.js diff --git a/.github/persona-brain/trinity-id.json b/.github/persona-brain/trinity-id.json index a0360ed5..7f005973 100644 --- a/.github/persona-brain/trinity-id.json +++ b/.github/persona-brain/trinity-id.json @@ -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" } } } diff --git a/signal-log/2026-03/SIG-20260324-012.json b/signal-log/2026-03/SIG-20260324-012.json new file mode 100644 index 00000000..611a7b7b --- /dev/null +++ b/signal-log/2026-03/SIG-20260324-012.json @@ -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 执行完毕 · 铸渊全系统启动序列完成" + } + } +} diff --git a/signal-log/index.json b/signal-log/index.json index bacc2ef4..2a04c030 100644 --- a/signal-log/index.json +++ b/signal-log/index.json @@ -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", diff --git a/skyeye/infra-manifest.json b/skyeye/infra-manifest.json index 34bf9576..d0d568a6 100644 --- a/skyeye/infra-manifest.json +++ b/skyeye/infra-manifest.json @@ -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", diff --git a/skyeye/scripts/daily-scan.js b/skyeye/scripts/daily-scan.js new file mode 100644 index 00000000..7f46bb64 --- /dev/null +++ b/skyeye/scripts/daily-scan.js @@ -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(); diff --git a/skyeye/scripts/guard-health.js b/skyeye/scripts/guard-health.js new file mode 100644 index 00000000..52160534 --- /dev/null +++ b/skyeye/scripts/guard-health.js @@ -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(); diff --git a/skyeye/scripts/quota-audit.js b/skyeye/scripts/quota-audit.js new file mode 100644 index 00000000..27b56764 --- /dev/null +++ b/skyeye/scripts/quota-audit.js @@ -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(); diff --git a/skyeye/scripts/weekly-full.js b/skyeye/scripts/weekly-full.js new file mode 100644 index 00000000..d405bcf6 --- /dev/null +++ b/skyeye/scripts/weekly-full.js @@ -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();