From 006784db6303c4495c877d9ae5ecee39ad16d7a3 Mon Sep 17 00:00:00 2001 From: zhi Date: Fri, 20 Mar 2026 07:13:12 +0000 Subject: [PATCH] refactor(plugin): read config via api.pluginConfig and api.config - Mirror dirigent plugin registration pattern - Read base config from api.pluginConfig - Read live config from api.config.plugins.entries.harborforge-monitor.config - Support gateway_start/gateway_stop lifecycle only - Compile nested plugin/core files --- plugin/core/live-config.ts | 39 +++++ plugin/index.ts | 300 ++++++++++++++++++------------------- plugin/tsconfig.json | 2 +- 3 files changed, 188 insertions(+), 153 deletions(-) create mode 100644 plugin/core/live-config.ts diff --git a/plugin/core/live-config.ts b/plugin/core/live-config.ts new file mode 100644 index 0000000..86f60ef --- /dev/null +++ b/plugin/core/live-config.ts @@ -0,0 +1,39 @@ +export interface HarborForgeMonitorConfig { + enabled?: boolean; + backendUrl?: string; + identifier?: string; + apiKey?: string; + reportIntervalSec?: number; + httpFallbackIntervalSec?: number; + logLevel?: 'debug' | 'info' | 'warn' | 'error'; +} + +interface OpenClawPluginApiLike { + config?: Record; +} + +export function getLivePluginConfig( + api: OpenClawPluginApiLike, + fallback: HarborForgeMonitorConfig +): HarborForgeMonitorConfig { + const root = (api.config as Record) || {}; + const plugins = (root.plugins as Record) || {}; + const entries = (plugins.entries as Record) || {}; + const entry = (entries['harborforge-monitor'] as Record) || {}; + const cfg = (entry.config as Record) || {}; + + if (Object.keys(cfg).length > 0 || Object.keys(entry).length > 0) { + return { + ...fallback, + ...cfg, + enabled: + typeof cfg.enabled === 'boolean' + ? cfg.enabled + : typeof entry.enabled === 'boolean' + ? entry.enabled + : fallback.enabled, + } as HarborForgeMonitorConfig; + } + + return fallback; +} diff --git a/plugin/index.ts b/plugin/index.ts index ca6ee92..da2d247 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -6,21 +6,7 @@ import { spawn } from 'child_process'; import { join } from 'path'; import { existsSync } from 'fs'; - -interface PluginConfig { - enabled?: boolean; - backendUrl?: string; - identifier?: string; - apiKey?: string; - reportIntervalSec?: number; - httpFallbackIntervalSec?: number; - logLevel?: 'debug' | 'info' | 'warn' | 'error'; -} - -interface PluginEntryLike { - enabled?: boolean; - config?: PluginConfig; -} +import { getLivePluginConfig, type HarborForgeMonitorConfig } from './core/live-config'; interface PluginAPI { logger: { @@ -30,159 +16,169 @@ interface PluginAPI { warn: (...args: any[]) => void; }; version?: string; - isRunning?: () => boolean; + config?: Record; + pluginConfig?: Record; on: (event: string, handler: () => void) => void; registerTool: (factory: (ctx: any) => any) => void; } -export default function register(api: PluginAPI, rawConfig: PluginConfig | PluginEntryLike | undefined) { - const logger = api.logger || { - info: (...args: any[]) => console.log('[HF-Monitor]', ...args), - error: (...args: any[]) => console.error('[HF-Monitor]', ...args), - debug: (...args: any[]) => console.debug('[HF-Monitor]', ...args), - warn: (...args: any[]) => console.warn('[HF-Monitor]', ...args), - }; - - const entryLike = rawConfig as PluginEntryLike | undefined; - const config: PluginConfig = entryLike?.config - ? { - ...entryLike.config, - enabled: entryLike.config.enabled ?? entryLike.enabled, - } - : ((rawConfig as PluginConfig | undefined) ?? {}); - - const enabled = config.enabled !== false; - - logger.info('HarborForge Monitor plugin config resolved', { - enabled, - hasApiKey: Boolean(config.apiKey), - backendUrl: config.backendUrl ?? null, - identifier: config.identifier ?? null, - }); - - if (!enabled) { - logger.info('HarborForge Monitor plugin disabled'); - return; - } - - if (!config.apiKey) { - logger.warn('Missing config: apiKey'); - logger.warn('API authentication will fail. Generate apiKey from HarborForge Monitor admin.'); - } - - const serverPath = join(__dirname, 'server', 'telemetry.mjs'); - - if (!existsSync(serverPath)) { - logger.error('Telemetry server not found:', serverPath); - return; - } - - let sidecar: ReturnType | null = null; - - function startSidecar() { - if (sidecar) { - logger.debug('Sidecar already running'); - return; - } - - logger.info('Starting HarborForge Monitor telemetry server...'); - - const env = { - ...process.env, - HF_MONITOR_BACKEND_URL: config.backendUrl || 'https://monitor.hangman-lab.top', - HF_MONITOR_IDENTIFIER: config.identifier || '', - HF_MONITOR_API_KEY: config.apiKey || '', - HF_MONITOR_REPORT_INTERVAL: String(config.reportIntervalSec || 30), - HF_MONITOR_HTTP_FALLBACK_INTERVAL: String(config.httpFallbackIntervalSec || 60), - HF_MONITOR_LOG_LEVEL: config.logLevel || 'info', - OPENCLAW_PATH: process.env.OPENCLAW_PATH || join(process.env.HOME || '/root', '.openclaw'), - OPENCLAW_VERSION: api.version || 'unknown', +export default { + id: 'harborforge-monitor', + name: 'HarborForge Monitor', + register(api: PluginAPI) { + const logger = api.logger || { + info: (...args: any[]) => console.log('[HF-Monitor]', ...args), + error: (...args: any[]) => console.error('[HF-Monitor]', ...args), + debug: (...args: any[]) => console.debug('[HF-Monitor]', ...args), + warn: (...args: any[]) => console.warn('[HF-Monitor]', ...args), }; - sidecar = spawn('node', [serverPath], { - env, - detached: false, - stdio: ['ignore', 'pipe', 'pipe'] - }); + const baseConfig: HarborForgeMonitorConfig = { + enabled: true, + backendUrl: 'https://monitor.hangman-lab.top', + identifier: '', + reportIntervalSec: 30, + httpFallbackIntervalSec: 60, + logLevel: 'info', + ...(api.pluginConfig || {}), + }; - sidecar.stdout?.on('data', (data: Buffer) => { - logger.info('[telemetry]', data.toString().trim()); - }); + const serverPath = join(__dirname, 'server', 'telemetry.mjs'); + let sidecar: ReturnType | null = null; - sidecar.stderr?.on('data', (data: Buffer) => { - logger.error('[telemetry]', data.toString().trim()); - }); - - sidecar.on('exit', (code, signal) => { - logger.info(`Telemetry server exited (code: ${code}, signal: ${signal})`); - sidecar = null; - }); - - sidecar.on('error', (err: Error) => { - logger.error('Failed to start telemetry server:', err.message); - sidecar = null; - }); - - logger.info('Telemetry server started with PID:', sidecar.pid); - } - - function stopSidecar() { - if (!sidecar) { - logger.debug('Telemetry server not running'); - return; + function resolveConfig() { + return getLivePluginConfig(api, baseConfig); } - logger.info('Stopping HarborForge Monitor telemetry server...'); - sidecar.kill('SIGTERM'); + function startSidecar() { + const live = resolveConfig(); + const enabled = live.enabled !== false; - const timeout = setTimeout(() => { - if (sidecar && !sidecar.killed) { - logger.warn('Telemetry server did not exit gracefully, forcing kill'); - sidecar.kill('SIGKILL'); + logger.info('HarborForge Monitor plugin config resolved', { + enabled, + hasApiKey: Boolean(live.apiKey), + backendUrl: live.backendUrl ?? null, + identifier: live.identifier ?? null, + }); + + if (!enabled) { + logger.info('HarborForge Monitor plugin disabled'); + return; } - }, 5000); - sidecar.on('exit', () => { - clearTimeout(timeout); - }); - } + if (sidecar) { + logger.debug('Sidecar already running'); + return; + } - // Hook into Gateway lifecycle - api.on('gateway_start', () => { - logger.info('Gateway starting, starting telemetry server...'); - startSidecar(); - }); + if (!live.apiKey) { + logger.warn('Missing config: apiKey'); + logger.warn('API authentication will fail. Generate apiKey from HarborForge Monitor admin.'); + } - api.on('gateway_stop', () => { - logger.info('Gateway stopping, stopping telemetry server...'); - stopSidecar(); - }); + if (!existsSync(serverPath)) { + logger.error('Telemetry server not found:', serverPath); + return; + } - // Handle process signals - process.on('SIGTERM', stopSidecar); - process.on('SIGINT', stopSidecar); + logger.info('Starting HarborForge Monitor telemetry server...'); - // Register status tool - api.registerTool(() => ({ - name: 'harborforge_monitor_status', - description: 'Get HarborForge Monitor plugin status', - parameters: { - type: 'object', - properties: {} - }, - async execute() { - return { - enabled: true, - sidecarRunning: sidecar !== null && sidecar.exitCode === null, - pid: sidecar?.pid || null, - config: { - backendUrl: config.backendUrl, - identifier: config.identifier || 'auto-detected', - reportIntervalSec: config.reportIntervalSec - } + const env = { + ...process.env, + HF_MONITOR_BACKEND_URL: live.backendUrl || 'https://monitor.hangman-lab.top', + HF_MONITOR_IDENTIFIER: live.identifier || '', + HF_MONITOR_API_KEY: live.apiKey || '', + HF_MONITOR_REPORT_INTERVAL: String(live.reportIntervalSec || 30), + HF_MONITOR_HTTP_FALLBACK_INTERVAL: String(live.httpFallbackIntervalSec || 60), + HF_MONITOR_LOG_LEVEL: live.logLevel || 'info', + OPENCLAW_PATH: process.env.OPENCLAW_PATH || join(process.env.HOME || '/root', '.openclaw'), + OPENCLAW_VERSION: api.version || 'unknown', }; - } - })); - logger.info('HarborForge Monitor plugin registered'); -} + sidecar = spawn('node', [serverPath], { + env, + detached: false, + stdio: ['ignore', 'pipe', 'pipe'], + }); + + sidecar.stdout?.on('data', (data: Buffer) => { + logger.info('[telemetry]', data.toString().trim()); + }); + + sidecar.stderr?.on('data', (data: Buffer) => { + logger.error('[telemetry]', data.toString().trim()); + }); + + sidecar.on('exit', (code, signal) => { + logger.info(`Telemetry server exited (code: ${code}, signal: ${signal})`); + sidecar = null; + }); + + sidecar.on('error', (err: Error) => { + logger.error('Failed to start telemetry server:', err.message); + sidecar = null; + }); + + logger.info('Telemetry server started with PID:', sidecar.pid); + } + + function stopSidecar() { + if (!sidecar) { + logger.debug('Telemetry server not running'); + return; + } + + logger.info('Stopping HarborForge Monitor telemetry server...'); + sidecar.kill('SIGTERM'); + + const timeout = setTimeout(() => { + if (sidecar && !sidecar.killed) { + logger.warn('Telemetry server did not exit gracefully, forcing kill'); + sidecar.kill('SIGKILL'); + } + }, 5000); + + sidecar.on('exit', () => { + clearTimeout(timeout); + }); + } + + api.on('gateway_start', () => { + logger.info('gateway_start received, starting telemetry server...'); + startSidecar(); + }); + + api.on('gateway_stop', () => { + logger.info('gateway_stop received, stopping telemetry server...'); + stopSidecar(); + }); + + process.on('SIGTERM', stopSidecar); + process.on('SIGINT', stopSidecar); + + api.registerTool(() => ({ + name: 'harborforge_monitor_status', + description: 'Get HarborForge Monitor plugin status', + parameters: { + type: 'object', + properties: {}, + }, + async execute() { + const live = resolveConfig(); + return { + enabled: live.enabled !== false, + sidecarRunning: sidecar !== null && sidecar.exitCode === null, + pid: sidecar?.pid || null, + config: { + backendUrl: live.backendUrl, + identifier: live.identifier || 'auto-detected', + reportIntervalSec: live.reportIntervalSec, + hasApiKey: Boolean(live.apiKey), + }, + }; + }, + })); + + logger.info('HarborForge Monitor plugin registered'); + }, +}; diff --git a/plugin/tsconfig.json b/plugin/tsconfig.json index 3c56872..3d63e25 100644 --- a/plugin/tsconfig.json +++ b/plugin/tsconfig.json @@ -12,6 +12,6 @@ "declarationMap": true, "sourceMap": true }, - "include": ["*.ts"], + "include": ["**/*.ts"], "exclude": ["node_modules"] }