@layr-labs/hourglass-performer
Version:
TypeScript SDK for building Hourglass AVS performers
248 lines • 8.19 kB
JavaScript
"use strict";
// Health and status management for the performer server
Object.defineProperty(exports, "__esModule", { value: true });
exports.HealthManager = exports.UptimeHealthProvider = exports.MemoryHealthProvider = void 0;
const performer_1 = require("../types/performer");
/**
* Built-in health check providers
*/
class MemoryHealthProvider {
constructor(maxMemoryMB = 512) {
this.maxMemoryMB = maxMemoryMB;
this.name = 'memory';
}
async check() {
const memUsage = process.memoryUsage();
const usedMB = memUsage.heapUsed / 1024 / 1024;
return {
healthy: usedMB < this.maxMemoryMB,
details: {
usedMB: Math.round(usedMB),
maxMB: this.maxMemoryMB,
heapUsed: memUsage.heapUsed,
heapTotal: memUsage.heapTotal,
},
error: usedMB >= this.maxMemoryMB ? `Memory usage ${Math.round(usedMB)}MB exceeds limit ${this.maxMemoryMB}MB` : undefined,
};
}
}
exports.MemoryHealthProvider = MemoryHealthProvider;
class UptimeHealthProvider {
constructor(startTime = Date.now()) {
this.startTime = startTime;
this.name = 'uptime';
}
async check() {
const uptime = Date.now() - this.startTime;
return {
healthy: true,
details: {
uptimeMs: uptime,
uptimeSeconds: Math.round(uptime / 1000),
startTime: this.startTime,
},
};
}
}
exports.UptimeHealthProvider = UptimeHealthProvider;
/**
* Health manager for tracking performer health and status
*/
class HealthManager {
constructor(logger, config = {}) {
this.logger = logger;
this.currentStatus = performer_1.PerformerStatus.STARTING;
this.providers = new Map();
this.failures = 0;
this.startTime = Date.now();
this.taskCount = 0;
this.errors = [];
this.customMetrics = new Map();
this.config = {
checkInterval: config.checkInterval ?? 30000, // 30 seconds
maxFailures: config.maxFailures ?? 3,
timeout: config.timeout ?? 5000, // 5 seconds
autoRecover: config.autoRecover ?? true,
};
// Add built-in health check providers
this.addProvider(new MemoryHealthProvider());
this.addProvider(new UptimeHealthProvider(this.startTime));
}
/**
* Start the health manager
*/
start() {
this.currentStatus = performer_1.PerformerStatus.READY_FOR_TASK;
this.logger.info('Health manager started');
// Start periodic health checks
if (this.config.checkInterval > 0) {
this.healthCheckInterval = setInterval(() => {
this.performHealthCheck().catch(error => {
this.logger.error('Health check failed', { error: error.message });
});
}, this.config.checkInterval);
}
}
/**
* Stop the health manager
*/
stop() {
this.currentStatus = performer_1.PerformerStatus.STOPPING;
if (this.healthCheckInterval) {
clearInterval(this.healthCheckInterval);
this.healthCheckInterval = undefined;
}
this.logger.info('Health manager stopped');
}
/**
* Add a custom health check provider
*/
addProvider(provider) {
this.providers.set(provider.name, provider);
this.logger.debug(`Added health check provider: ${provider.name}`);
}
/**
* Remove a health check provider
*/
removeProvider(name) {
this.providers.delete(name);
this.logger.debug(`Removed health check provider: ${name}`);
}
/**
* Get current status
*/
getStatus() {
return this.currentStatus;
}
/**
* Set status manually
*/
setStatus(status) {
const oldStatus = this.currentStatus;
this.currentStatus = status;
if (oldStatus !== status) {
this.logger.info(`Status changed: ${oldStatus} -> ${status}`);
}
}
/**
* Record a task execution
*/
recordTask() {
this.taskCount++;
this.lastTaskTime = Date.now();
}
/**
* Record an error
*/
recordError(error) {
this.errors.push(`${new Date().toISOString()}: ${error}`);
// Keep only last 10 errors
if (this.errors.length > 10) {
this.errors = this.errors.slice(-10);
}
// Update status if too many recent errors
const recentErrors = this.errors.filter(e => {
const errorTime = new Date(e.split(':')[0]).getTime();
return Date.now() - errorTime < 60000; // Last minute
});
if (recentErrors.length >= 3) {
this.setStatus(performer_1.PerformerStatus.ERROR);
}
}
/**
* Set custom metric
*/
setCustomMetric(key, value) {
this.customMetrics.set(key, value);
}
/**
* Get custom metric
*/
getCustomMetric(key) {
return this.customMetrics.get(key);
}
/**
* Perform comprehensive health check
*/
async performHealthCheck() {
const timestamp = Date.now();
let overallHealthy = true;
const providerResults = {};
// Run all health check providers
for (const [name, provider] of this.providers) {
try {
const result = await Promise.race([
provider.check(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Health check timeout')), this.config.timeout))
]);
providerResults[name] = result;
if (!result.healthy) {
overallHealthy = false;
if (result.error) {
this.recordError(`Health check failed [${name}]: ${result.error}`);
}
}
}
catch (error) {
overallHealthy = false;
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
providerResults[name] = { healthy: false, error: errorMsg };
this.recordError(`Health check error [${name}]: ${errorMsg}`);
}
}
// Update failure count and status
if (overallHealthy) {
this.failures = 0;
if (this.currentStatus === performer_1.PerformerStatus.ERROR && this.config.autoRecover) {
this.setStatus(performer_1.PerformerStatus.READY_FOR_TASK);
}
}
else {
this.failures++;
if (this.failures >= this.config.maxFailures) {
this.setStatus(performer_1.PerformerStatus.ERROR);
}
}
// Create health check result
const result = {
status: this.currentStatus,
timestamp,
details: {
uptime: timestamp - this.startTime,
memoryUsage: process.memoryUsage(),
taskCount: this.taskCount,
lastTaskTime: this.lastTaskTime,
errors: [...this.errors],
},
custom: {
providers: providerResults,
failures: this.failures,
maxFailures: this.config.maxFailures,
...Object.fromEntries(this.customMetrics),
},
};
this.lastHealthCheck = result;
return result;
}
/**
* Get last health check result
*/
getLastHealthCheck() {
return this.lastHealthCheck;
}
/**
* Get health summary for monitoring
*/
getHealthSummary() {
const memUsage = process.memoryUsage();
return {
status: this.currentStatus,
uptime: Date.now() - this.startTime,
taskCount: this.taskCount,
errorCount: this.errors.length,
memoryUsageMB: Math.round(memUsage.heapUsed / 1024 / 1024),
};
}
}
exports.HealthManager = HealthManager;
//# sourceMappingURL=healthManager.js.map