1. session-monitor: handle timestamp-prefixed transcript filenames
OpenClaw uses {ISO}_{sessionId}.jsonl — glob for *_{sessionId}.jsonl
when direct path doesn't exist.
2. session-monitor: skip stale sessions (>5min since last transcript write)
Prevents creating status boxes for every old session in sessions.json.
3. status-watcher: parse actual OpenClaw JSONL transcript format
Records are {type:'message', message:{role,content:[{type,name,...}]}}
not {type:'tool_call', name}. Now shows live tool calls with arguments
and assistant thinking text.
4. handler.js: fix module.exports for OpenClaw hook loader
Expects default export (function), not {handle: function}.
5. HOOK.md: add YAML frontmatter metadata for hook discovery.
105 lines
2.8 KiB
JavaScript
105 lines
2.8 KiB
JavaScript
'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;
|