openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
128 lines • 4.18 kB
JavaScript
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import process from 'node:process';
import readline from 'node:readline';
import { parseArgs } from 'node:util';
import { logger, flushLogs, saveLogsToFile } from '../../utils/logger.js';
import { WatchdogMessageType } from '../types.js';
import { ClearcutSender } from './ClearcutSender.js';
function parseWatchdogArgs() {
const { values } = parseArgs({
options: {
'parent-pid': { type: 'string' },
'app-version': { type: 'string' },
'os-type': { type: 'string' },
'log-file': { type: 'string' },
'clearcut-endpoint': { type: 'string' },
'clearcut-force-flush-interval-ms': { type: 'string' },
'clearcut-include-pid-header': { type: 'boolean' },
},
strict: true,
});
// Verify required arguments
const parentPid = parseInt(values['parent-pid'] ?? '', 10);
const appVersion = values['app-version'];
const osType = parseInt(values['os-type'] ?? '', 10);
if (isNaN(parentPid) || !appVersion || isNaN(osType)) {
console.error('Invalid arguments provided for watchdog process: ', JSON.stringify({ parentPid, appVersion, osType }));
process.exit(1);
}
// Parse Optional Arguments
const logFile = values['log-file'];
const clearcutEndpoint = values['clearcut-endpoint'];
const clearcutIncludePidHeader = values['clearcut-include-pid-header'];
let clearcutForceFlushIntervalMs;
if (values['clearcut-force-flush-interval-ms']) {
const parsed = parseInt(values['clearcut-force-flush-interval-ms'], 10);
if (!isNaN(parsed)) {
clearcutForceFlushIntervalMs = parsed;
}
}
return {
parentPid,
appVersion,
osType,
logFile,
clearcutEndpoint,
clearcutForceFlushIntervalMs,
clearcutIncludePidHeader,
};
}
function main() {
const { parentPid, appVersion, osType, logFile, clearcutEndpoint, clearcutForceFlushIntervalMs, clearcutIncludePidHeader, } = parseWatchdogArgs();
let logStream;
if (logFile) {
logStream = saveLogsToFile(logFile);
}
const exit = (code) => {
if (!logStream) {
process.exit(code);
}
void flushLogs(logStream).finally(() => {
process.exit(code);
});
};
logger?.('Watchdog started', JSON.stringify({
pid: process.pid,
parentPid,
version: appVersion,
osType,
}, null, 2));
const sender = new ClearcutSender({
appVersion,
osType: osType,
clearcutEndpoint,
forceFlushIntervalMs: clearcutForceFlushIntervalMs,
includePidHeader: clearcutIncludePidHeader,
});
let isShuttingDown = false;
function onParentDeath(reason) {
if (isShuttingDown) {
return;
}
isShuttingDown = true;
logger?.(`Parent death detected (${reason}). Sending shutdown event...`);
sender
.sendShutdownEvent()
.then(() => {
logger?.('Shutdown event sent. Exiting.');
exit(0);
})
.catch(err => {
logger?.('Failed to send shutdown event', err);
exit(1);
});
}
process.stdin.on('end', () => onParentDeath('stdin end'));
process.stdin.on('close', () => onParentDeath('stdin close'));
process.on('disconnect', () => onParentDeath('ipc disconnect'));
const rl = readline.createInterface({
input: process.stdin,
terminal: false,
});
rl.on('line', line => {
try {
if (!line.trim()) {
return;
}
const msg = JSON.parse(line);
if (msg.type === WatchdogMessageType.LOG_EVENT && msg.payload) {
sender.enqueueEvent(msg.payload);
}
}
catch (err) {
logger?.('Failed to parse IPC message', err);
}
});
}
try {
main();
}
catch (err) {
console.error('Watchdog fatal error:', err);
process.exit(1);
}
//# sourceMappingURL=main.js.map