'use strict'; /** * status-watcher-hook/handler.js * * Spawns the Live Status v4 watcher-manager daemon on gateway startup. * * Events: ["gateway:startup"] * * Behavior: * 1. Check PID file — if watcher is already running, do nothing. * 2. If not running, spawn watcher-manager.js as a detached background process. * 3. The hook handler returns immediately; the daemon runs independently. */ /* eslint-disable no-console */ const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); // PID file location (must match watcher-manager.js default) const PID_FILE = process.env.PID_FILE || '/tmp/status-watcher.pid'; // Path to watcher-manager.js (relative to this hook file's location) // Hook is in: workspace/hooks/status-watcher-hook/handler.js // Watcher is in: workspace/projects/openclaw-live-status/src/watcher-manager.js const WATCHER_PATH = path.resolve( __dirname, '../../projects/openclaw-live-status/src/watcher-manager.js', ); /** * Check if a process is alive given its PID. * Returns true if process exists and is running. */ function isProcessRunning(pid) { try { process.kill(pid, 0); return true; } catch (_err) { return false; } } /** * Check if the watcher daemon is already running via PID file. * Returns true if running, false if not (or PID file stale/missing). */ function isWatcherRunning() { try { // eslint-disable-next-line security/detect-non-literal-fs-filename const pidStr = fs.readFileSync(PID_FILE, 'utf8').trim(); const pid = parseInt(pidStr, 10); if (isNaN(pid) || pid <= 0) return false; return isProcessRunning(pid); } catch (_err) { // PID file missing or unreadable — watcher is not running return false; } } /** * Spawn the watcher daemon as a detached background process. * The parent (this hook handler) does not wait for it. */ function spawnWatcher() { if (!fs.existsSync(WATCHER_PATH)) { console.error('[status-watcher-hook] watcher-manager.js not found at:', WATCHER_PATH); console.error('[status-watcher-hook] Deploy the live-status project first: see install.sh'); return; } console.log('[status-watcher-hook] Starting Live Status v4 watcher daemon...'); const child = spawn(process.execPath, [WATCHER_PATH, 'start'], { detached: true, stdio: 'ignore', env: process.env, }); child.unref(); console.log( '[status-watcher-hook] Watcher daemon spawned (PID will be written to', PID_FILE + ')', ); } /** * Hook entry point — called by OpenClaw on gateway:startup event. */ async function handle(_event) { if (isWatcherRunning()) { console.log('[status-watcher-hook] Watcher already running, skipping spawn.'); return; } spawnWatcher(); } // OpenClaw hook loader expects a default export module.exports = handle; module.exports.default = handle;