#!/usr/bin/env node // Agent Health Monitor for OpenClaw // Monitors agent crashes, errors, and service health // Sends notifications via configured channels (Telegram, etc.) const fs = require('fs'); const path = require('path'); class AgentHealthMonitor { constructor() { this.config = this.loadConfig(); this.logDir = '/root/.openclaw/workspace/logs/agents'; this.ensureLogDir(); } loadConfig() { try { const configPath = '/root/.openclaw/openclaw.json'; return JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch (error) { console.error('Failed to load OpenClaw config:', error); return {}; } } ensureLogDir() { if (!fs.existsSync(this.logDir)) { fs.mkdirSync(this.logDir, { recursive: true }); } } async sendNotification(message, severity = 'error') { // Log to file first const timestamp = new Date().toISOString(); const logEntry = `[${timestamp}] [${severity.toUpperCase()}] ${message}\n`; const logFile = path.join(this.logDir, `health-${new Date().toISOString().split('T')[0]}.log`); fs.appendFileSync(logFile, logEntry); // Send via Telegram if configured if (this.config.channels?.telegram?.enabled) { await this.sendTelegramNotification(message, severity); } } async sendTelegramNotification(message, severity) { const botToken = this.config.channels.telegram.botToken; const chatId = '5237946060'; // Your Telegram ID if (!botToken) { console.error('Telegram bot token not configured'); return; } try { const url = `https://api.telegram.org/bot${botToken}/sendMessage`; const payload = { chat_id: chatId, text: `🚨 OpenClaw Agent Alert (${severity})\n\n${message}`, parse_mode: 'Markdown' }; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { console.error('Failed to send Telegram notification:', await response.text()); } } catch (error) { console.error('Telegram notification error:', error); } } monitorProcess(processName, checkFunction) { // Set up process monitoring process.on('uncaughtException', async (error) => { await this.sendNotification( `Uncaught exception in ${processName}:\n${error.stack || error.message}`, 'critical' ); process.exit(1); }); process.on('unhandledRejection', async (reason, promise) => { await this.sendNotification( `Unhandled rejection in ${processName}:\nReason: ${reason}\nPromise: ${promise}`, 'error' ); }); // Custom health check if (checkFunction) { setInterval(async () => { try { const isHealthy = await checkFunction(); if (!isHealthy) { await this.sendNotification( `${processName} health check failed`, 'warning' ); } } catch (error) { await this.sendNotification( `${processName} health check error: ${error.message}`, 'error' ); } }, 30000); // Check every 30 seconds } } } module.exports = AgentHealthMonitor;