Skip to main content
Glama
auto-heartbeat-starter.cjsโ€ข7.23 kB
#!/usr/bin/env node /** * Auto Heartbeat Starter - Singleton Pattern Implementation * * This module provides automatic heartbeat management for database tools * when no active heartbeat is detected. It ensures only one test runs * at a time without rate limiting for easier debugging. */ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); const LOCK_FILE = path.join(__dirname, 'global-test.lock'); const STATUS_FILE = path.join(__dirname, 'global-test-status.json'); class AutoHeartbeatStarter { /** * Check if global test is already running */ static isGlobalTestRunning() { try { if (!fs.existsSync(LOCK_FILE)) { return { isRunning: false, reason: 'No lock file' }; } if (!fs.existsSync(STATUS_FILE)) { return { isRunning: false, reason: 'No status file' }; } const status = JSON.parse(fs.readFileSync(STATUS_FILE, 'utf8')); if (!status.isRunning) { return { isRunning: false, reason: 'Status shows not running' }; } // Check if process is still alive (basic check) const ageMinutes = (Date.now() - new Date(status.lastUpdate)) / (1000 * 60); if (ageMinutes > 15) { // If status is older than 15 minutes, assume dead return { isRunning: false, reason: 'Status too old' }; } // More robust check - if status was updated very recently, it's active const ageSeconds = (Date.now() - new Date(status.lastUpdate)) / 1000; if (ageSeconds < 30) { // If updated in last 30 seconds, definitely running return { isRunning: true, pid: status.pid, testType: status.testType, benefits: status.benefits || [] }; } // Otherwise, check more conservatively return { isRunning: true, pid: status.pid, testType: status.testType, benefits: status.benefits || [] }; } catch (error) { console.error('โŒ Failed to check global test status:', error.message); return { isRunning: false, reason: 'Error checking status' }; } } /** * Start the global test singleton */ static async startGlobalTest(toolName) { try { console.log(`๐ŸŒ Starting global heartbeat test for tool: ${toolName}`); // Start global test using direct spawn to avoid import issues const testProcess = spawn('node', [ '../../../test-complete-system.cjs' ], { cwd: __dirname, detached: true, stdio: 'ignore' }); // Don't wait for the process, let it run in background testProcess.unref(); // Give it a moment to start await new Promise(resolve => setTimeout(resolve, 2000)); console.log(`โœ… Global heartbeat started for "${toolName}" (PID: ${testProcess.pid})`); return { success: true, message: `Global heartbeat started for tool "${toolName}"`, pid: testProcess.pid }; } catch (error) { console.error('โŒ Auto heartbeat starter error:', error.message); return { success: false, message: `Auto heartbeat starter failed: ${error.message}` }; } } /** * Enhanced validation method that can auto-start heartbeat */ static async validateWithAutoStart(toolName = 'unknown-tool') { console.log(`๐Ÿš‘ Tool "${toolName}" needs heartbeat - attempting auto-start...`); // Check if global test is already running const globalCheck = this.isGlobalTestRunning(); if (globalCheck.isRunning) { console.log(`โœ… Global test already running (PID: ${globalCheck.pid})`); return { success: true, message: `Heartbeat already active for "${toolName}"`, alreadyRunning: true, pid: globalCheck.pid }; } // Try to start the global test const startResult = await this.startGlobalTest(toolName); if (startResult.success) { console.log(`โœ… Global heartbeat started for "${toolName}" (PID: ${startResult.pid})`); return { success: true, message: `Heartbeat auto-started successfully for ${toolName}`, autoStarted: true, pid: startResult.pid }; } // If auto-start failed, return helpful error message return { success: false, message: startResult.message || 'Auto-start failed', suggestion: 'Manually start the heartbeat test using: node ../../../test-complete-system.cjs' }; } /** * Update heartbeat time (forward to database-utils) */ static updateHeartbeatTime() { try { const { SimpleEGWDatabase } = require('./database-utils.js'); SimpleEGWDatabase.updateHeartbeatTime(); console.log('๐Ÿ’“ Heartbeat time updated'); } catch (error) { console.error('โŒ Failed to update heartbeat time:', error.message); } } /** * Get current heartbeat status */ static getCurrentHeartbeatStatus() { const globalCheck = this.isGlobalTestRunning(); return { isActive: globalCheck.isRunning, lastHeartbeat: globalCheck.isRunning ? new Date() : null, globalTestRunning: globalCheck.isRunning, pid: globalCheck.isRunning ? globalCheck.pid : null }; } /** * Clean up old files */ static cleanup() { try { const filesToCheck = [LOCK_FILE, STATUS_FILE]; filesToCheck.forEach(file => { if (fs.existsSync(file)) { const stats = fs.statSync(file); const ageHours = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60); // Clean up files older than 1 hour if (ageHours > 1) { fs.unlinkSync(file); console.log(`๐Ÿงน Cleaned up old file: ${path.basename(file)}`); } } }); } catch (error) { console.error('โŒ Cleanup failed:', error.message); } } } // Export for use by other modules module.exports = { AutoHeartbeatStarter }; // CLI interface for testing if (require.main === module) { const args = process.argv.slice(2); const command = args[0]; const toolName = args[1] || 'test-tool'; switch (command) { case 'test': AutoHeartbeatStarter.validateWithAutoStart(toolName).then(result => { console.log('๐Ÿงช Auto-heartbeat test result:'); console.log(JSON.stringify(result, null, 2)); }); break; case 'status': const status = AutoHeartbeatStarter.getCurrentHeartbeatStatus(); console.log('๐Ÿ’“ Heartbeat status:', status); break; case 'cleanup': AutoHeartbeatStarter.cleanup(); console.log('๐Ÿงน Cleanup completed'); break; default: console.log(` Auto Heartbeat Starter - Tool for automatic heartbeat management Usage: node auto-heartbeat-starter.cjs <command> [toolName] Commands: test [toolName] Test auto-start for a tool status Show current heartbeat status cleanup Clean up old files Examples: node auto-heartbeat-starter.cjs test chat-cli node auto-heartbeat-starter.cjs status `); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pythondev-pro/egw_writings_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server