tasq/node_modules/@claude-flow/hooks/dist/daemons/index.js

443 lines
12 KiB
JavaScript

/**
* V3 Daemon Manager
*
* Manages background daemon processes for:
* - Metrics collection
* - Swarm monitoring
* - Pattern learning consolidation
* - Statusline updates
*/
/**
* Default daemon manager configuration
*/
const DEFAULT_CONFIG = {
pidDirectory: '.claude-flow/pids',
logDirectory: '.claude-flow/logs',
daemons: [],
autoRestart: true,
maxRestartAttempts: 3,
};
/**
* Daemon Manager - controls background daemon processes
*/
export class DaemonManager {
config;
daemons = new Map();
restartCounts = new Map();
constructor(config) {
this.config = { ...DEFAULT_CONFIG, ...config };
}
/**
* Register a daemon
*/
register(config, task) {
if (this.daemons.has(config.name)) {
throw new Error(`Daemon '${config.name}' is already registered`);
}
const state = {
name: config.name,
status: 'stopped',
executionCount: 0,
failureCount: 0,
};
this.daemons.set(config.name, { config, state, task });
}
/**
* Start a daemon
*/
async start(name) {
const daemon = this.daemons.get(name);
if (!daemon) {
throw new Error(`Daemon '${name}' not found`);
}
if (daemon.state.status === 'running') {
return; // Already running
}
if (!daemon.config.enabled) {
throw new Error(`Daemon '${name}' is disabled`);
}
daemon.state.status = 'starting';
daemon.state.startedAt = new Date();
try {
// Start interval timer
daemon.timer = setInterval(async () => {
await this.executeDaemonTask(name);
}, daemon.config.interval);
daemon.state.status = 'running';
daemon.state.pid = process.pid; // Use current process for in-process daemons
// Run initial execution
await this.executeDaemonTask(name);
}
catch (error) {
daemon.state.status = 'error';
daemon.state.error = error instanceof Error ? error.message : String(error);
throw error;
}
}
/**
* Stop a daemon
*/
async stop(name) {
const daemon = this.daemons.get(name);
if (!daemon) {
throw new Error(`Daemon '${name}' not found`);
}
if (daemon.state.status === 'stopped') {
return; // Already stopped
}
daemon.state.status = 'stopping';
if (daemon.timer) {
clearInterval(daemon.timer);
daemon.timer = undefined;
}
daemon.state.status = 'stopped';
daemon.state.pid = undefined;
}
/**
* Restart a daemon
*/
async restart(name) {
await this.stop(name);
await this.start(name);
}
/**
* Start all registered daemons
*/
async startAll() {
const promises = [];
for (const [name, daemon] of this.daemons) {
if (daemon.config.enabled) {
promises.push(this.start(name));
}
}
await Promise.all(promises);
}
/**
* Stop all daemons
*/
async stopAll() {
const promises = [];
for (const name of this.daemons.keys()) {
promises.push(this.stop(name));
}
await Promise.all(promises);
}
/**
* Get daemon state
*/
getState(name) {
return this.daemons.get(name)?.state;
}
/**
* Get all daemon states
*/
getAllStates() {
return Array.from(this.daemons.values()).map((d) => d.state);
}
/**
* Check if daemon is running
*/
isRunning(name) {
return this.daemons.get(name)?.state.status === 'running';
}
/**
* Update daemon interval
*/
updateInterval(name, interval) {
const daemon = this.daemons.get(name);
if (!daemon) {
throw new Error(`Daemon '${name}' not found`);
}
daemon.config.interval = interval;
// Restart if running to apply new interval
if (daemon.state.status === 'running') {
this.restart(name).catch(() => { });
}
}
/**
* Enable a daemon
*/
enable(name) {
const daemon = this.daemons.get(name);
if (daemon) {
daemon.config.enabled = true;
}
}
/**
* Disable a daemon
*/
disable(name) {
const daemon = this.daemons.get(name);
if (daemon) {
daemon.config.enabled = false;
this.stop(name).catch(() => { });
}
}
/**
* Get daemon count
*/
get count() {
return this.daemons.size;
}
/**
* Get running daemon count
*/
get runningCount() {
return Array.from(this.daemons.values()).filter((d) => d.state.status === 'running').length;
}
/**
* Execute a daemon task
*/
async executeDaemonTask(name) {
const daemon = this.daemons.get(name);
if (!daemon || !daemon.task) {
return;
}
try {
await daemon.task();
daemon.state.executionCount++;
daemon.state.lastUpdateAt = new Date();
daemon.state.error = undefined;
// Reset restart count on successful execution
this.restartCounts.set(name, 0);
}
catch (error) {
daemon.state.failureCount++;
daemon.state.error = error instanceof Error ? error.message : String(error);
// Handle auto-restart
if (this.config.autoRestart) {
const restartCount = (this.restartCounts.get(name) ?? 0) + 1;
this.restartCounts.set(name, restartCount);
if (restartCount <= this.config.maxRestartAttempts) {
// Schedule restart
setTimeout(() => {
this.restart(name).catch(() => { });
}, 1000 * restartCount); // Exponential backoff
}
else {
daemon.state.status = 'error';
}
}
}
}
}
/**
* Metrics Daemon - collects and syncs metrics
*/
export class MetricsDaemon {
manager;
metricsStore = new Map();
constructor(manager) {
this.manager = manager ?? new DaemonManager();
// Register metrics daemon
this.manager.register({
name: 'metrics-sync',
interval: 30000, // 30 seconds
enabled: true,
}, () => this.syncMetrics());
}
/**
* Start metrics collection
*/
async start() {
await this.manager.start('metrics-sync');
}
/**
* Stop metrics collection
*/
async stop() {
await this.manager.stop('metrics-sync');
}
/**
* Sync metrics
*/
async syncMetrics() {
// Collect various metrics
this.metricsStore.set('timestamp', new Date().toISOString());
this.metricsStore.set('memory', process.memoryUsage());
// Additional metrics would be collected here
}
/**
* Get current metrics
*/
getMetrics() {
return Object.fromEntries(this.metricsStore);
}
}
/**
* Swarm Monitor Daemon - monitors swarm activity
*/
export class SwarmMonitorDaemon {
manager;
swarmData = {
activeAgents: 0,
maxAgents: 15,
coordinationActive: false,
lastCheck: null,
};
constructor(manager) {
this.manager = manager ?? new DaemonManager();
// Register swarm monitor daemon
this.manager.register({
name: 'swarm-monitor',
interval: 3000, // 3 seconds
enabled: true,
}, () => this.checkSwarm());
}
/**
* Start swarm monitoring
*/
async start() {
await this.manager.start('swarm-monitor');
}
/**
* Stop swarm monitoring
*/
async stop() {
await this.manager.stop('swarm-monitor');
}
/**
* Check swarm status
*/
async checkSwarm() {
// In a real implementation, this would check running processes
// and coordination state
this.swarmData.lastCheck = new Date();
}
/**
* Get swarm data
*/
getSwarmData() {
return { ...this.swarmData };
}
/**
* Update active agent count
*/
updateAgentCount(count) {
this.swarmData.activeAgents = count;
}
/**
* Set coordination state
*/
setCoordinationActive(active) {
this.swarmData.coordinationActive = active;
}
}
/**
* Hooks Learning Daemon - consolidates learned patterns using ReasoningBank
*/
export class HooksLearningDaemon {
manager;
patternsLearned = 0;
routingAccuracy = 0;
reasoningBank = null;
lastConsolidation = null;
consolidationStats = {
totalRuns: 0,
patternsPromoted: 0,
patternsPruned: 0,
duplicatesRemoved: 0,
};
constructor(manager) {
this.manager = manager ?? new DaemonManager();
// Register hooks learning daemon
this.manager.register({
name: 'hooks-learning',
interval: 60000, // 60 seconds
enabled: true,
}, () => this.consolidate());
}
/**
* Start learning consolidation
*/
async start() {
// Lazy load ReasoningBank to avoid circular dependencies
try {
const { reasoningBank } = await import('../reasoningbank/index.js');
this.reasoningBank = reasoningBank;
await this.reasoningBank.initialize();
}
catch (error) {
console.warn('[HooksLearningDaemon] ReasoningBank not available:', error);
}
await this.manager.start('hooks-learning');
}
/**
* Stop learning consolidation
*/
async stop() {
await this.manager.stop('hooks-learning');
}
/**
* Consolidate learned patterns using ReasoningBank
*/
async consolidate() {
if (!this.reasoningBank) {
return;
}
try {
const result = await this.reasoningBank.consolidate();
// Update stats
this.consolidationStats.totalRuns++;
this.consolidationStats.patternsPromoted += result.patternsPromoted;
this.consolidationStats.patternsPruned += result.patternsPruned;
this.consolidationStats.duplicatesRemoved += result.duplicatesRemoved;
this.lastConsolidation = new Date();
// Update pattern count from ReasoningBank stats
const stats = this.reasoningBank.getStats();
this.patternsLearned = stats.shortTermCount + stats.longTermCount;
// Emit consolidation event
if (result.patternsPromoted > 0 || result.patternsPruned > 0) {
console.log(`[HooksLearningDaemon] Consolidated: ${result.patternsPromoted} promoted, ` +
`${result.patternsPruned} pruned, ${result.duplicatesRemoved} deduped`);
}
}
catch (error) {
console.error('[HooksLearningDaemon] Consolidation failed:', error);
}
}
/**
* Get learning stats
*/
getStats() {
return {
patternsLearned: this.patternsLearned,
routingAccuracy: this.routingAccuracy,
consolidationStats: { ...this.consolidationStats },
lastConsolidation: this.lastConsolidation,
};
}
/**
* Update pattern count
*/
updatePatternCount(count) {
this.patternsLearned = count;
}
/**
* Update routing accuracy
*/
updateRoutingAccuracy(accuracy) {
this.routingAccuracy = accuracy;
}
/**
* Get ReasoningBank stats (if available)
*/
getReasoningBankStats() {
if (!this.reasoningBank) {
return null;
}
return this.reasoningBank.getStats();
}
/**
* Force immediate consolidation
*/
async forceConsolidate() {
await this.consolidate();
}
}
/**
* Default daemon manager instance
*/
export const defaultDaemonManager = new DaemonManager();
export { DaemonManager as default, };
//# sourceMappingURL=index.js.map