[grade=pending] QA sprint: commit 103 at-risk files from multi-agent sprint work

Committed by Hermes autonomous QA sprint 2026-08-13.
These files were modified during the Aug 12 sprint but never committed.
This commit is contained in:
Krystie
2026-08-12 17:34:10 -07:00
parent 0bf4406253
commit 503de258b8
105 changed files with 14057 additions and 2632 deletions
+21 -20
View File
@@ -8,6 +8,7 @@ const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const credentialManager = require('./credential-manager');
const cryptoUtils = require('../security/crypto-utils');
const { log } = require('../utils/logging');
// JWT signing secret - derived from encryption key for consistency
const JWT_SECRET = cryptoUtils.loadOrCreateKey();
@@ -19,7 +20,7 @@ const API_KEY_METADATA_NAMESPACE = 'auth.metadata';
class AuthManager {
constructor() {
this.keyMetadataCache = new Map(); // Cache for API key metadata
console.log('[AuthManager] Initialized');
log.info('auth', 'Initialized');
}
/**
@@ -44,10 +45,10 @@ class AuthManager {
{ expiresIn }
);
console.log(`[AuthManager] Generated JWT for user: ${payload.sub}, expires in: ${expiresIn}`);
log.info('auth', 'Generated JWT', { user: payload.sub, expiresIn });
return token;
} catch (error) {
console.error('[AuthManager] JWT generation failed:', error.message);
log.error('auth', error, { operation: 'jwtGenerate' });
throw error;
}
}
@@ -68,11 +69,11 @@ class AuthManager {
};
} catch (error) {
if (error.name === 'TokenExpiredError') {
console.log('[AuthManager] JWT token expired');
log.info('auth', 'JWT token expired');
} else if (error.name === 'JsonWebTokenError') {
console.log('[AuthManager] JWT token invalid:', error.message);
log.info('auth', 'JWT token invalid', { error: error.message });
} else {
console.error('[AuthManager] JWT verification failed:', error.message);
log.error('auth', error, { operation: 'jwtVerify' });
}
return null;
}
@@ -116,7 +117,7 @@ class AuthManager {
// Cache metadata
this.keyMetadataCache.set(keyId, metadata);
console.log(`[AuthManager] Generated API key: ${name} (${keyId})`);
log.info('auth', 'Generated API key', { name, keyId });
return {
key: apiKey,
@@ -126,7 +127,7 @@ class AuthManager {
createdAt: metadata.createdAt
};
} catch (error) {
console.error('[AuthManager] API key generation failed:', error.message);
log.error('auth', error, { operation: 'apiKeyGenerate' });
throw error;
}
}
@@ -154,30 +155,30 @@ class AuthManager {
// Retrieve stored hash
const storedHash = await credentialManager.retrieve(credentialKey);
if (!storedHash) {
console.log(`[AuthManager] API key not found: ${keyId}`);
log.info('auth', 'API key not found', { keyId });
return null;
}
// Verify key matches stored hash
const providedHash = crypto.createHash('sha256').update(key).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(storedHash), Buffer.from(providedHash))) {
console.log(`[AuthManager] API key hash mismatch: ${keyId}`);
log.info('auth', 'API key hash mismatch', { keyId });
return null;
}
// Get metadata
const metadata = await this.getKeyMetadata(keyId);
if (!metadata) {
console.log(`[AuthManager] API key metadata not found: ${keyId}`);
log.info('auth', 'API key metadata not found', { keyId });
return null;
}
// Update last used timestamp (non-blocking)
this.updateLastUsed(keyId, metadata).catch(err =>
console.error(`[AuthManager] Failed to update lastUsed for ${keyId}:`, err.message)
log.error('auth', err, { keyId, operation: 'updateLastUsed' })
);
console.log(`[AuthManager] API key verified: ${metadata.name} (${keyId})`);
log.info('auth', 'API key verified', { name: metadata.name, keyId });
return {
keyId,
@@ -185,7 +186,7 @@ class AuthManager {
name: metadata.name
};
} catch (error) {
console.error('[AuthManager] API key verification failed:', error.message);
log.error('auth', error, { operation: 'apiKeyVerify' });
return null;
}
}
@@ -205,10 +206,10 @@ class AuthManager {
this.keyMetadataCache.delete(keyId);
console.log(`[AuthManager] Revoked API key: ${keyId}`);
log.info('auth', 'Revoked API key', { keyId });
return true;
} catch (error) {
console.error(`[AuthManager] Failed to revoke API key ${keyId}:`, error.message);
log.error('auth', error, { keyId, operation: 'revoke' });
return false;
}
}
@@ -233,7 +234,7 @@ class AuthManager {
return keys;
} catch (error) {
console.error('[AuthManager] Failed to list API keys:', error.message);
log.error('auth', error, { operation: 'listApiKeys' });
return [];
}
}
@@ -262,7 +263,7 @@ class AuthManager {
return metadata;
} catch (error) {
console.error(`[AuthManager] Failed to get metadata for ${keyId}:`, error.message);
log.error('auth', error, { keyId, operation: 'getMetadata' });
return null;
}
}
@@ -285,7 +286,7 @@ class AuthManager {
this.keyMetadataCache.set(keyId, updatedMetadata);
} catch (error) {
console.error(`[AuthManager] Failed to update lastUsed for ${keyId}:`, error.message);
log.error('auth', error, { keyId, operation: 'updateLastUsed' });
}
}
@@ -294,7 +295,7 @@ class AuthManager {
*/
clearCache() {
this.keyMetadataCache.clear();
console.log('[AuthManager] Cache cleared');
log.info('auth', 'Cache cleared');
}
}
@@ -50,7 +50,7 @@ class AutoRestartManager extends EventEmitter {
super();
this.ctx = ctx;
this.log = ctx.log || console;
this.logError = ctx.logError || ((_ctx, err) => console.error(err));
this.logError = ctx.logError || ((_ctx, err) => process.stderr.write(`[auto-restart] ${err?.message || err}\n`));
this.docker = ctx.docker;
this.healthChecker = ctx.healthChecker;
this.notification = ctx.notification;
@@ -41,7 +41,7 @@ class ConfigDriftDetector extends EventEmitter {
super();
this.ctx = ctx;
this.log = ctx.log || console;
this.logError = ctx.logError || ((_c, err) => console.error(err));
this.logError = ctx.logError || ((_c, err) => process.stderr.write(`[config-drift] ${err?.message || err}\n`));
this.docker = ctx.docker;
this.servicesStateManager = ctx.servicesStateManager;
this.notification = ctx.notification;
@@ -8,6 +8,7 @@ const keychainManager = require('../security/keychain-manager');
const cryptoUtils = require('../security/crypto-utils');
const lockfile = require('proper-lockfile');
const fs = require('fs');
const { log } = require('../utils/logging');
const path = require('path');
const platformPaths = require('../../platform-paths');
@@ -33,7 +34,7 @@ class CredentialManager {
stale: 30000
};
console.log(`[CredentialManager] Initialized with ${this.useKeychain ? 'OS keychain' : 'encrypted file'} storage`);
log.info('cred', 'Initialized', { storage: this.useKeychain ? 'keychain' : 'file' });
}
/**
@@ -60,19 +61,19 @@ class CredentialManager {
// Store metadata separately in file
await this.storeMetadata(key, metadata);
this.cache.set(key, { value, exp: Date.now() + this.CACHE_TTL_MS });
console.log(`[CredentialManager] Stored '${key}' in OS keychain`);
log.info('cred', 'Stored credential in keychain', { key });
return true;
}
console.warn(`[CredentialManager] Keychain storage failed for '${key}', falling back to encrypted file`);
log.warn('cred', 'Keychain storage failed, falling back to encrypted file', { key });
}
// Fallback to encrypted file storage
await this.storeInFile(key, value, metadata);
this.cache.set(key, { value, exp: Date.now() + this.CACHE_TTL_MS });
console.log(`[CredentialManager] Stored '${key}' in encrypted file`);
log.info('cred', 'Stored credential in encrypted file', { key });
return true;
} catch (error) {
console.error(`[CredentialManager] Failed to store '${key}':`, error.message);
log.error('cred', error, { key, operation: 'store' });
return false;
}
}
@@ -109,7 +110,7 @@ class CredentialManager {
}
return value;
} catch (error) {
console.error(`[CredentialManager] Failed to retrieve '${key}':`, error.message);
log.error('cred', error, { key, operation: 'retrieve' });
return null;
}
}
@@ -132,10 +133,10 @@ class CredentialManager {
// Remove from file storage
await this.deleteFromFile(key);
console.log(`[CredentialManager] Deleted '${key}'`);
log.info('cred', 'Deleted credential', { key });
return true;
} catch (error) {
console.error(`[CredentialManager] Failed to delete '${key}':`, error.message);
log.error('cred', error, { key, operation: 'delete' });
return false;
}
}
@@ -149,7 +150,7 @@ class CredentialManager {
const credentials = await this.loadCredentialsFile();
return Object.keys(credentials);
} catch (error) {
console.error('[CredentialManager] Failed to list credentials:', error.message);
log.error('cred', error, { operation: 'list' });
return [];
}
}
@@ -175,7 +176,7 @@ class CredentialManager {
async rotateEncryptionKey() {
let release;
try {
console.log('[CredentialManager] Starting encryption key rotation...');
log.info('cred', 'Starting encryption key rotation');
// Ensure file exists before locking
this._ensureFileExists();
@@ -186,7 +187,7 @@ class CredentialManager {
const keys = Object.keys(credentials);
if (keys.length === 0) {
console.log('[CredentialManager] No credentials to rotate');
log.info('cred', 'No credentials to rotate');
return true;
}
@@ -219,10 +220,10 @@ class CredentialManager {
// Clear cache to force reload
this.cache.clear();
console.log(`[CredentialManager] Successfully rotated ${keys.length} credentials`);
log.info('cred', 'Rotated credentials', { count: keys.length });
return true;
} catch (error) {
console.error('[CredentialManager] Key rotation failed:', error.message);
log.error('cred', error, { operation: 'rotate' });
return false;
} finally {
if (release) {
@@ -255,12 +256,12 @@ class CredentialManager {
if (migrated > 0) {
this.cache.clear();
console.log(`[CredentialManager] Migrated ${migrated} plaintext credentials to encrypted format`);
log.info('cred', 'Migrated plaintext credentials', { count: migrated });
}
return { migrated, skipped, total: migrated + skipped };
} catch (error) {
console.error('[CredentialManager] Migration failed:', error.message);
log.error('cred', error, { operation: 'migrate' });
throw error;
}
}
@@ -365,14 +366,11 @@ class CredentialManager {
// Most common cause: the encryption key on disk is different from
// the key that originally encrypted this entry (rotated by a
// container recreate that didn't preserve CREDENTIALS_FILE env).
console.warn(
`[CredentialManager] '${key}' is present but cannot be decrypted ` +
`(likely encryption-key mismatch): ${decryptErr.message}`
);
log.warn('cred', 'Credential present but cannot be decrypted (likely encryption-key mismatch)', { key, error: decryptErr.message });
return { status: 'unreadable', value: null, error: decryptErr.message };
}
} catch (err) {
console.error(`[CredentialManager] diagnose('${key}') failed:`, err.message);
log.error('cred', err, { key, operation: 'diagnose' });
return { status: 'malformed', value: null, error: err.message };
}
}
@@ -404,7 +402,7 @@ class CredentialManager {
const data = fs.readFileSync(CREDENTIALS_FILE, 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('[CredentialManager] Failed to load credentials file:', error.message);
log.error('cred', error, { operation: 'loadFile' });
return {};
}
}
@@ -440,10 +438,10 @@ class CredentialManager {
await this._lockedUpdate(() => backup.credentials);
this.cache.clear();
console.log('[CredentialManager] Successfully imported backup');
log.info('cred', 'Successfully imported backup');
return true;
} catch (error) {
console.error('[CredentialManager] Failed to import backup:', error.message);
log.error('cred', error, { operation: 'importBackup' });
return false;
}
}
+19 -17
View File
@@ -6,8 +6,10 @@
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const lockfile = require('proper-lockfile');
const platformPaths = require('../../platform-paths');
const { log } = require('../utils/logging');
const LOCK_DIR = process.env.PORT_LOCK_DIR || path.join(platformPaths.dataDir, '.port-locks');
const LOCK_TIMEOUT = 120000; // 2 minutes
@@ -35,7 +37,7 @@ class PortLockManager {
ensureLockDirectory() {
if (!fs.existsSync(LOCK_DIR)) {
fs.mkdirSync(LOCK_DIR, { recursive: true });
console.log('[PortLockManager] Created lock directory:', LOCK_DIR);
log.info('portlock', 'Created lock directory', { dir: LOCK_DIR });
}
}
@@ -57,13 +59,13 @@ class PortLockManager {
throw new Error('Ports must be a non-empty array');
}
const lockId = `lock-${Date.now()}-${Math.random().toString(36).substring(7)}`;
const lockId = `lock-${Date.now()}-${crypto.randomBytes(8).toString('hex')}`;
const sortedPorts = [...new Set(ports)].sort((a, b) => parseInt(a) - parseInt(b));
const acquiredLocks = [];
const releaseFunctions = [];
try {
console.log(`[PortLockManager] Acquiring locks for ports: ${sortedPorts.join(', ')}`);
log.info('portlock', 'Acquiring locks', { ports: sortedPorts });
// Acquire locks in sorted order to prevent deadlocks
for (const port of sortedPorts) {
@@ -83,7 +85,7 @@ class PortLockManager {
acquiredLocks.push(port);
releaseFunctions.push(release);
console.log(`[PortLockManager] Locked port ${port}`);
log.info('portlock', 'Locked port', { port });
}
// Store lock information
@@ -93,18 +95,18 @@ class PortLockManager {
timestamp: Date.now()
});
console.log(`[PortLockManager] Successfully acquired all locks (ID: ${lockId})`);
log.info('portlock', 'Acquired all locks', { lockId });
return lockId;
} catch (error) {
// Release any locks we managed to acquire
console.error(`[PortLockManager] Failed to acquire all locks:`, error.message);
log.error('portlock', error, { operation: 'acquire', lockId });
for (const release of releaseFunctions) {
try {
await release();
} catch (releaseError) {
console.error(`[PortLockManager] Error releasing lock during cleanup:`, releaseError.message);
log.error('portlock', releaseError, { operation: 'releaseCleanup', lockId });
}
}
@@ -120,11 +122,11 @@ class PortLockManager {
const lockInfo = this.activeLocks.get(lockId);
if (!lockInfo) {
console.warn(`[PortLockManager] Lock ID ${lockId} not found (may have been released already)`);
log.warn('portlock', 'Lock ID not found', { lockId });
return;
}
console.log(`[PortLockManager] Releasing locks for ports: ${lockInfo.ports.join(', ')}`);
log.info('portlock', 'Releasing locks', { lockId, ports: lockInfo.ports });
const errors = [];
@@ -133,16 +135,16 @@ class PortLockManager {
await release();
} catch (error) {
errors.push(error.message);
console.error(`[PortLockManager] Error releasing lock:`, error.message);
log.error('portlock', error, { operation: 'release', lockId });
}
}
this.activeLocks.delete(lockId);
if (errors.length > 0) {
console.warn(`[PortLockManager] Released locks with ${errors.length} errors`);
log.warn('portlock', 'Released locks with errors', { lockId, errorCount: errors.length });
} else {
console.log(`[PortLockManager] Successfully released all locks (ID: ${lockId})`);
log.info('portlock', 'Released all locks', { lockId });
}
}
@@ -151,7 +153,7 @@ class PortLockManager {
* Removes locks older than LOCK_STALE_THRESHOLD
*/
async cleanupStaleLocks() {
console.log('[PortLockManager] Cleaning up stale locks...');
log.info('portlock', 'Cleaning up stale locks');
this.ensureLockDirectory();
@@ -174,20 +176,20 @@ class PortLockManager {
// Lock is stale or not locked, safe to remove
fs.unlinkSync(lockFilePath);
cleaned++;
console.log(`[PortLockManager] Removed stale lock: ${file}`);
log.info('portlock', 'Removed stale lock', { file });
}
} catch (error) {
// File might not exist or might have been removed by another process
if (error.code !== 'ENOENT') {
errors++;
console.warn(`[PortLockManager] Error checking lock ${file}:`, error.message);
log.warn('portlock', 'Error checking lock', { file, error: error.message });
}
}
}
console.log(`[PortLockManager] Cleanup complete: ${cleaned} stale locks removed, ${errors} errors`);
log.info('portlock', 'Cleanup complete', { cleaned, errors });
} catch (error) {
console.error('[PortLockManager] Error during cleanup:', error.message);
log.error('portlock', error, { operation: 'cleanup' });
}
}
+33 -32
View File
@@ -9,6 +9,7 @@ const EventEmitter = require('events');
const fs = require('fs');
const path = require('path');
const platformPaths = require('../../platform-paths');
const { log } = require('../utils/logging');
const docker = new Docker();
@@ -59,17 +60,17 @@ class ResourceMonitor extends EventEmitter {
*/
start() {
if (this.monitoring) {
console.log('[ResourceMonitor] Already monitoring');
log.info('monitor', 'Already monitoring');
return;
}
console.log('[ResourceMonitor] Starting container monitoring');
log.info('monitor', 'Starting container monitoring');
this.monitoring = true;
this.monitoringInterval = setInterval(() => this.collectStats(), MONITORING_INTERVAL);
// Hourly rollup — fires once an hour, computes the previous full hour
this.hourlyRollupTimer = setInterval(() => {
try { this.rollupHourly(); } catch (e) { console.error('[ResourceMonitor] hourly rollup error:', e.message); }
try { this.rollupHourly(); } catch (e) { log.error('monitor', e, { rollup: 'hourly' }); }
}, ROLLUP_HOURLY_INTERVAL);
// Daily rollup — schedule first run at the next midnight, then fire every 24h
@@ -77,9 +78,9 @@ class ResourceMonitor extends EventEmitter {
const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 5);
const msUntilMidnight = nextMidnight.getTime() - now.getTime();
setTimeout(() => {
try { this.rollupDaily(); } catch (e) { console.error('[ResourceMonitor] daily rollup error:', e.message); }
try { this.rollupDaily(); } catch (e) { log.error('monitor', e, { rollup: 'daily' }); }
this.dailyRollupTimer = setInterval(() => {
try { this.rollupDaily(); } catch (e) { console.error('[ResourceMonitor] daily rollup error:', e.message); }
try { this.rollupDaily(); } catch (e) { log.error('monitor', e, { rollup: 'daily' }); }
}, ROLLUP_DAILY_INTERVAL);
}, msUntilMidnight);
@@ -93,7 +94,7 @@ class ResourceMonitor extends EventEmitter {
stop() {
if (!this.monitoring) return;
console.log('[ResourceMonitor] Stopping container monitoring');
log.info('monitor', 'Stopping container monitoring');
this.monitoring = false;
if (this.monitoringInterval) {
@@ -131,7 +132,7 @@ class ResourceMonitor extends EventEmitter {
this.checkAlerts(containerInfo.Id, containerInfo.Names[0], stats);
}
} catch (error) {
console.error(`[ResourceMonitor] Error collecting stats for ${containerInfo.Names[0]}:`, error.message);
log.error('monitor', error, { container: containerInfo.Names[0] });
}
}
@@ -143,7 +144,7 @@ class ResourceMonitor extends EventEmitter {
this.saveStats();
}
} catch (error) {
console.error('[ResourceMonitor] Error collecting container stats:', error.message);
log.error('monitor', error, { phase: 'collectStats' });
}
}
@@ -329,7 +330,7 @@ class ResourceMonitor extends EventEmitter {
// Send notification if manager is configured
if (this.notificationManager) {
this.notificationManager.sendAlert(alertPayload).catch(err => {
console.error('[ResourceMonitor] Failed to send alert notification:', err.message);
log.error('monitor', err, { phase: 'sendAlert' });
});
}
@@ -357,7 +358,7 @@ class ResourceMonitor extends EventEmitter {
*/
async restartContainer(containerId, containerName, alerts) {
try {
console.log(`[ResourceMonitor] Auto-restarting ${containerName} due to alerts:`, alerts.map(a => a.type).join(', '));
log.info('monitor', 'Auto-restarting container', { container: containerName, alerts: alerts.map(a => a.type) });
const container = docker.getContainer(containerId);
await container.restart();
@@ -377,11 +378,11 @@ class ResourceMonitor extends EventEmitter {
timestamp: new Date().toISOString(),
reason: alerts
}).catch(err => {
console.error('[ResourceMonitor] Failed to send auto-restart notification:', err.message);
log.error('monitor', err, { phase: 'sendAutoRestart' });
});
}
} catch (error) {
console.error(`[ResourceMonitor] Failed to restart ${containerName}:`, error.message);
log.error('monitor', error, { container: containerName, phase: 'restart' });
}
}
@@ -390,7 +391,7 @@ class ResourceMonitor extends EventEmitter {
*/
triggerWorkflows(eventType, eventData) {
if (!this.workflowEngine) {
console.log('[ResourceMonitor] Workflow engine not set, skipping workflow trigger');
log.info('monitor', 'Workflow engine not set, skipping workflow trigger');
return;
}
@@ -398,14 +399,14 @@ class ResourceMonitor extends EventEmitter {
this.workflowEngine.triggerForEvent(eventType, eventData)
.then(results => {
if (results && results.length > 0) {
console.log(`[ResourceMonitor] Triggered ${results.length} workflow(s) for ${eventType}`);
log.info('monitor', `Triggered workflows for ${eventType}`, { count: results.length });
}
})
.catch(err => {
console.error('[ResourceMonitor] Workflow trigger error:', err.message);
log.error('monitor', err, { phase: 'workflowTrigger' });
});
} catch (error) {
console.error('[ResourceMonitor] Error triggering workflows:', error.message);
log.error('monitor', error, { phase: 'workflowTrigger' });
}
}
@@ -414,7 +415,7 @@ class ResourceMonitor extends EventEmitter {
*/
setWorkflowEngine(workflowEngine) {
this.workflowEngine = workflowEngine;
console.log('[ResourceMonitor] Workflow engine configured');
log.info('monitor', 'Workflow engine configured');
}
/**
@@ -562,10 +563,10 @@ class ResourceMonitor extends EventEmitter {
if (fs.existsSync(ALERT_HISTORY_FILE)) {
const data = JSON.parse(fs.readFileSync(ALERT_HISTORY_FILE, 'utf8'));
this.alertHistory = Array.isArray(data) ? data : [];
console.log(`[ResourceMonitor] Loaded ${this.alertHistory.length} alert history entries`);
log.info('monitor', 'Loaded alert history', { count: this.alertHistory.length });
}
} catch (error) {
console.error('[ResourceMonitor] Error loading alert history:', error.message);
log.error('monitor', error, { operation: 'loadAlertHistory' });
}
}
@@ -576,7 +577,7 @@ class ResourceMonitor extends EventEmitter {
try {
fs.writeFileSync(ALERT_HISTORY_FILE, JSON.stringify(this.alertHistory, null, 2));
} catch (error) {
console.error('[ResourceMonitor] Error saving alert history:', error.message);
log.error('monitor', error, { operation: 'saveAlertHistory' });
}
}
@@ -606,10 +607,10 @@ class ResourceMonitor extends EventEmitter {
if (fs.existsSync(STATS_FILE)) {
const data = JSON.parse(fs.readFileSync(STATS_FILE, 'utf8'));
this.stats = new Map(Object.entries(data));
console.log(`[ResourceMonitor] Loaded stats for ${this.stats.size} containers`);
log.info('monitor', 'Loaded stats', { containerCount: this.stats.size });
}
} catch (error) {
console.error('[ResourceMonitor] Error loading stats:', error.message);
log.error('monitor', error, { operation: 'loadStats' });
}
}
@@ -621,7 +622,7 @@ class ResourceMonitor extends EventEmitter {
const data = Object.fromEntries(this.stats);
fs.writeFileSync(STATS_FILE, JSON.stringify(data, null, 2));
} catch (error) {
console.error('[ResourceMonitor] Error saving stats:', error.message);
log.error('monitor', error, { operation: 'saveStats' });
}
}
@@ -633,10 +634,10 @@ class ResourceMonitor extends EventEmitter {
if (fs.existsSync(ALERT_CONFIG_FILE)) {
const data = JSON.parse(fs.readFileSync(ALERT_CONFIG_FILE, 'utf8'));
this.alerts = new Map(Object.entries(data));
console.log(`[ResourceMonitor] Loaded alert config for ${this.alerts.size} containers`);
log.info('monitor', 'Loaded alert config', { containerCount: this.alerts.size });
}
} catch (error) {
console.error('[ResourceMonitor] Error loading alert config:', error.message);
log.error('monitor', error, { operation: 'loadAlertConfig' });
}
}
@@ -648,7 +649,7 @@ class ResourceMonitor extends EventEmitter {
const data = Object.fromEntries(this.alerts);
fs.writeFileSync(ALERT_CONFIG_FILE, JSON.stringify(data, null, 2));
} catch (error) {
console.error('[ResourceMonitor] Error saving alert config:', error.message);
log.error('monitor', error, { operation: 'saveAlertConfig' });
}
}
@@ -902,10 +903,10 @@ class ResourceMonitor extends EventEmitter {
if (fs.existsSync(STATS_HOURLY_FILE)) {
const data = JSON.parse(fs.readFileSync(STATS_HOURLY_FILE, 'utf8'));
this.hourlyHistory = new Map(Object.entries(data));
console.log(`[ResourceMonitor] Loaded hourly rollups for ${this.hourlyHistory.size} containers`);
log.info('monitor', 'Loaded hourly rollups', { containerCount: this.hourlyHistory.size });
}
} catch (error) {
console.error('[ResourceMonitor] Error loading hourly stats:', error.message);
log.error('monitor', error, { operation: 'loadHourlyStats' });
}
}
@@ -917,7 +918,7 @@ class ResourceMonitor extends EventEmitter {
const data = Object.fromEntries(this.hourlyHistory);
fs.writeFileSync(STATS_HOURLY_FILE, JSON.stringify(data, null, 2));
} catch (error) {
console.error('[ResourceMonitor] Error saving hourly stats:', error.message);
log.error('monitor', error, { operation: 'saveHourlyStats' });
}
}
@@ -929,10 +930,10 @@ class ResourceMonitor extends EventEmitter {
if (fs.existsSync(STATS_DAILY_FILE)) {
const data = JSON.parse(fs.readFileSync(STATS_DAILY_FILE, 'utf8'));
this.dailyHistory = new Map(Object.entries(data));
console.log(`[ResourceMonitor] Loaded daily rollups for ${this.dailyHistory.size} containers`);
log.info('monitor', 'Loaded daily rollups', { containerCount: this.dailyHistory.size });
}
} catch (error) {
console.error('[ResourceMonitor] Error loading daily stats:', error.message);
log.error('monitor', error, { operation: 'loadDailyStats' });
}
}
@@ -944,7 +945,7 @@ class ResourceMonitor extends EventEmitter {
const data = Object.fromEntries(this.dailyHistory);
fs.writeFileSync(STATS_DAILY_FILE, JSON.stringify(data, null, 2));
} catch (error) {
console.error('[ResourceMonitor] Error saving daily stats:', error.message);
log.error('monitor', error, { operation: 'saveDailyStats' });
}
}