#!/usr/bin/env node /** * skyeye/hibernation/daily-hibernation.js * ═══════════════════════════════════════════════ * 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 * 📜 Copyright: 国作登字-2026-A-00037559 * ═══════════════════════════════════════════════ * * 🌙 日休眠执行脚本 * 执行每日浅睡眠:轻量自查 → 微调优 → 归档 * * 用法: * node daily-hibernation.js [--planned-minutes=N] */ 'use strict'; const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '../..'); const SKYEYE_DIR = path.resolve(__dirname, '..'); const GUARDS_DIR = path.join(SKYEYE_DIR, 'guards'); const LOGS_DIR = path.join(SKYEYE_DIR, 'logs'); const BUFFER_DIR = path.join(ROOT, 'buffer'); const CP_DIR = path.join(__dirname, 'checkpoints'); const LEDGER_PATH = path.join(SKYEYE_DIR, 'quota-ledger.json'); // ━━━ 工具函数 ━━━ function getTimestamp() { return new Date().toISOString(); } function getBeijingTime() { return new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }); } 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) { const dir = path.dirname(filePath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8'); } // ━━━ Phase 1: 轻量自查(约 60% 时间)━━━ function phaseHealthCheck() { console.log('[Daily Hibernation] Phase 1: 轻量自查'); const results = { guards: {}, workflow_triggers_today: 0, errors_today: 0, buffer_backlog: 0, checkin_anomalies: 0 }; // ① Guard 健康心跳 if (fs.existsSync(GUARDS_DIR)) { const guardFiles = fs.readdirSync(GUARDS_DIR) .filter(f => f.endsWith('.json') && f !== 'guard-template.json'); for (const file of guardFiles) { const guard = loadJSON(path.join(GUARDS_DIR, file)); if (guard) { const name = file.replace('-guard.json', '').replace('.json', ''); results.guards[name] = { status: guard.health_check ? guard.health_check.last_status || 'unknown' : 'unknown', self_heals_today: guard.health_check ? guard.health_check.consecutive_failures || 0 : 0 }; } } } console.log(` ✅ Guard 心跳检查完成: ${Object.keys(results.guards).length} 个 Guard`); // ② 配额日结 const ledger = loadJSON(LEDGER_PATH); if (ledger && ledger.services) { const actions = ledger.services.github_actions; if (actions) { results.quota_daily = { actions_minutes_used: actions.current_used || 0, actions_remaining: (actions.monthly_limit || 2000) - (actions.current_used || 0) }; } } console.log(' ✅ 配额日结完成'); // ③ buffer 检查 const inboxDir = path.join(BUFFER_DIR, 'inbox'); if (fs.existsSync(inboxDir)) { try { const devDirs = fs.readdirSync(inboxDir).filter(d => { const stat = fs.statSync(path.join(inboxDir, d)); return stat.isDirectory(); }); for (const dev of devDirs) { const devPath = path.join(inboxDir, dev); const files = fs.readdirSync(devPath).filter(f => f.endsWith('.json')); results.buffer_backlog += files.length; } } catch (e) { // ignore } } console.log(` ✅ Buffer 检查完成: ${results.buffer_backlog} 条堆积`); // ④ 小兵签到汇总 // Read from daily skyeye logs const dailyLogDir = path.join(LOGS_DIR, 'daily'); if (fs.existsSync(dailyLogDir)) { const todayStr = getDateStr(); const todayLogs = fs.readdirSync(dailyLogDir) .filter(f => f.includes(todayStr) && f.endsWith('.json')); results.workflow_triggers_today = todayLogs.length * 10; // estimate } console.log(` ✅ 小兵签到汇总完成`); // ⑤ 异常计数汇总 results.errors_today = 0; // will be updated from scan logs if available console.log(` ✅ 异常计数汇总完成: ${results.errors_today} 个错误`); return results; } // ━━━ Phase 2: 微调优(约 30% 时间)━━━ function phaseMicroOptimize(healthResults) { console.log('[Daily Hibernation] Phase 2: 微调优'); const optimizations = []; // ① Guard 参数微调 if (fs.existsSync(GUARDS_DIR)) { const guardFiles = fs.readdirSync(GUARDS_DIR) .filter(f => f.endsWith('.json') && f !== 'guard-template.json'); for (const file of guardFiles) { const guardPath = path.join(GUARDS_DIR, file); const guard = loadJSON(guardPath); if (!guard) continue; const name = file.replace('-guard.json', '').replace('.json', ''); const guardHealth = healthResults.guards[name]; // 如果今天有自愈触发,微调参数 if (guardHealth && guardHealth.self_heals_today > 0) { // 降频以减少负载 if (guard.trigger_policy && guard.trigger_policy.auto_adjust) { optimizations.push({ target: `${name}-guard`, action: 'noted self-heal events for frequency review', reason: `${guardHealth.self_heals_today} self-heals today` }); } } } } console.log(` ✅ Guard 参数微调完成: ${optimizations.length} 项调整`); // ② buffer 紧急 flush(如堆积过大) if (healthResults.buffer_backlog > 500) { optimizations.push({ target: 'buffer', action: 'flagged for emergency flush', reason: `buffer backlog at ${healthResults.buffer_backlog}` }); console.log(` ⚠️ Buffer 堆积过大 (${healthResults.buffer_backlog}),标记紧急 flush`); } // ③ 明日配额预调 if (healthResults.quota_daily) { const remaining = healthResults.quota_daily.actions_remaining || 2000; if (remaining < 200) { optimizations.push({ target: 'quota', action: 'flagged low remaining actions minutes', reason: `only ${remaining} minutes remaining` }); console.log(` ⚠️ Actions 配额偏低: 剩余 ${remaining} 分钟`); } } return optimizations; } // ━━━ Phase 3: 归档(约 10% 时间)━━━ function phaseArchive(healthResults, optimizations, plannedMinutes) { console.log('[Daily Hibernation] Phase 3: 归档'); const dateStr = getDateStr(); const startTime = new Date(); startTime.setMinutes(startTime.getMinutes() - plannedMinutes); // approximate start const checkpoint = { checkpoint_id: `DAILY-CP-${dateStr}`, date: new Date().toISOString().slice(0, 10), hibernation: { start_time: startTime.toISOString(), end_time: getTimestamp(), duration_minutes: plannedMinutes, planned_minutes: plannedMinutes }, health_summary: { guards: healthResults.guards, workflow_triggers_today: healthResults.workflow_triggers_today || 0, errors_today: healthResults.errors_today || 0, buffer_backlog: healthResults.buffer_backlog || 0, checkin_anomalies: healthResults.checkin_anomalies || 0 }, quota_daily: healthResults.quota_daily || {}, micro_optimizations: optimizations, issues_found: [], issues_auto_fixed: optimizations.length, issues_need_human: 0 }; // 写入 checkpoint 文件 const cpPath = path.join(CP_DIR, `daily-cp-${dateStr}.json`); saveJSON(cpPath, checkpoint); console.log(` ✅ Checkpoint 已写入: ${cpPath}`); // 更新 daily-health-summary.json const summaryPath = path.join(__dirname, 'daily-health-summary.json'); saveJSON(summaryPath, { last_checkpoint: checkpoint.checkpoint_id, last_date: checkpoint.date, guards_online: Object.keys(healthResults.guards).length, all_healthy: Object.values(healthResults.guards).every(g => g.status === 'healthy'), optimizations_today: optimizations.length, updated_at: getTimestamp() }); console.log(' ✅ daily-health-summary.json 已更新'); return checkpoint; } // ━━━ 主流程 ━━━ function run() { const args = process.argv.slice(2); const minutesArg = args.find(a => a.startsWith('--planned-minutes=')); const plannedMinutes = minutesArg ? parseInt(minutesArg.split('=')[1], 10) : 12; console.log('═══════════════════════════════════════════════'); console.log('🌙 日休眠执行脚本启动'); console.log(`═══════════════════════════════════════════════`); console.log(`[Daily Hibernation] Timestamp: ${getTimestamp()}`); console.log(`[Daily Hibernation] Beijing Time: ${getBeijingTime()}`); console.log(`[Daily Hibernation] Planned duration: ${plannedMinutes} minutes`); console.log(''); // Phase 1: 轻量自查 const healthResults = phaseHealthCheck(); console.log(''); // Phase 2: 微调优 const optimizations = phaseMicroOptimize(healthResults); console.log(''); // Phase 3: 归档 const checkpoint = phaseArchive(healthResults, optimizations, plannedMinutes); console.log(''); console.log('═══════════════════════════════════════════════'); console.log('🌙 日休眠完成'); console.log(` 自查项: ${Object.keys(healthResults.guards).length + 4} 项 ✅`); console.log(` 微调优: ${optimizations.length} 项`); console.log(` 修复: ${checkpoint.issues_auto_fixed} 项`); console.log('═══════════════════════════════════════════════'); // 输出供 workflow 使用 console.log(`::set-output name=checkpoint_id::${checkpoint.checkpoint_id}`); console.log(`::set-output name=optimizations::${optimizations.length}`); console.log(`::set-output name=guards_online::${Object.keys(healthResults.guards).length}`); return checkpoint; } module.exports = { run, phaseHealthCheck, phaseMicroOptimize, phaseArchive }; if (require.main === module) { run(); }