vibecodingmachine-core 2026.2.20-438 → 2026.2.26-1739
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -0
- package/package.json +10 -2
- package/src/agents/Agent.js +300 -0
- package/src/agents/AgentAdditionService.js +311 -0
- package/src/agents/AgentCheckService.js +690 -0
- package/src/agents/AgentInstallationService.js +140 -0
- package/src/agents/AgentSetupService.js +467 -0
- package/src/agents/AgentStatus.js +183 -0
- package/src/agents/AgentVerificationService.js +634 -0
- package/src/agents/ConfigurationSchemaValidator.js +543 -0
- package/src/agents/EnvironmentConfigurationManager.js +602 -0
- package/src/agents/InstallationErrorHandler.js +372 -0
- package/src/agents/InstallationLog.js +363 -0
- package/src/agents/InstallationMethod.js +510 -0
- package/src/agents/InstallationOrchestrator.js +352 -0
- package/src/agents/InstallationProgressReporter.js +372 -0
- package/src/agents/InstallationRetryManager.js +322 -0
- package/src/agents/InstallationType.js +254 -0
- package/src/agents/OperationTypes.js +310 -0
- package/src/agents/PerformanceMetricsCollector.js +493 -0
- package/src/agents/SecurityValidationService.js +534 -0
- package/src/agents/VerificationTest.js +354 -0
- package/src/agents/VerificationType.js +226 -0
- package/src/agents/WindowsPermissionHandler.js +518 -0
- package/src/agents/config/AgentConfigManager.js +393 -0
- package/src/agents/config/AgentDefaultsRegistry.js +373 -0
- package/src/agents/config/ConfigValidator.js +281 -0
- package/src/agents/discovery/AgentDiscoveryService.js +707 -0
- package/src/agents/logging/AgentLogger.js +511 -0
- package/src/agents/status/AgentStatusManager.js +481 -0
- package/src/agents/storage/FileManager.js +454 -0
- package/src/agents/verification/AgentCommunicationTester.js +474 -0
- package/src/agents/verification/BaseVerifier.js +430 -0
- package/src/agents/verification/CommandVerifier.js +480 -0
- package/src/agents/verification/FileOperationVerifier.js +453 -0
- package/src/agents/verification/ResultAnalyzer.js +707 -0
- package/src/agents/verification/TestRequirementManager.js +495 -0
- package/src/agents/verification/VerificationRunner.js +433 -0
- package/src/agents/windows/BaseWindowsInstaller.js +441 -0
- package/src/agents/windows/ChocolateyInstaller.js +509 -0
- package/src/agents/windows/DirectInstaller.js +443 -0
- package/src/agents/windows/InstallerFactory.js +391 -0
- package/src/agents/windows/NpmInstaller.js +505 -0
- package/src/agents/windows/PowerShellInstaller.js +458 -0
- package/src/agents/windows/WinGetInstaller.js +390 -0
- package/src/analysis/analysis-reporter.js +132 -0
- package/src/analysis/boundary-detector.js +712 -0
- package/src/analysis/categorizer.js +340 -0
- package/src/analysis/codebase-scanner.js +384 -0
- package/src/analysis/line-counter.js +513 -0
- package/src/analysis/priority-calculator.js +679 -0
- package/src/analysis/report/analysis-report.js +250 -0
- package/src/analysis/report/package-analyzer.js +278 -0
- package/src/analysis/report/recommendation-generator.js +382 -0
- package/src/analysis/report/statistics-generator.js +515 -0
- package/src/analysis/reports/analysis-report-model.js +101 -0
- package/src/analysis/reports/recommendation-generator.js +283 -0
- package/src/analysis/reports/report-generators.js +191 -0
- package/src/analysis/reports/statistics-calculator.js +231 -0
- package/src/analysis/reports/trend-analyzer.js +219 -0
- package/src/analysis/strategy-generator.js +814 -0
- package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
- package/src/config/refactoring-config.js +307 -0
- package/src/health-tracking/json-storage.js +38 -2
- package/src/ide-integration/applescript-manager-core.js +233 -0
- package/src/ide-integration/applescript-manager.cjs +357 -28
- package/src/ide-integration/applescript-manager.js +89 -3599
- package/src/ide-integration/cdp-manager.js +306 -0
- package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
- package/src/ide-integration/continuation-handler.js +337 -0
- package/src/ide-integration/ide-status-checker.js +292 -0
- package/src/ide-integration/macos-ide-manager.js +627 -0
- package/src/ide-integration/macos-text-sender.js +528 -0
- package/src/ide-integration/response-reader.js +548 -0
- package/src/ide-integration/windows-automation-manager.js +121 -0
- package/src/ide-integration/windows-ide-manager.js +373 -0
- package/src/index.cjs +25 -3
- package/src/index.js +15 -1
- package/src/llm/direct-llm-manager.cjs +90 -2
- package/src/models/compliance-report.js +538 -0
- package/src/models/file-analysis.js +681 -0
- package/src/models/refactoring-plan.js +770 -0
- package/src/monitoring/alert-system.js +834 -0
- package/src/monitoring/compliance-progress-tracker.js +437 -0
- package/src/monitoring/continuous-scan-notifications.js +661 -0
- package/src/monitoring/continuous-scanner.js +279 -0
- package/src/monitoring/file-monitor/file-analyzer.js +262 -0
- package/src/monitoring/file-monitor/file-monitor.js +237 -0
- package/src/monitoring/file-monitor/watcher.js +194 -0
- package/src/monitoring/file-monitor.js +17 -0
- package/src/monitoring/notification-manager.js +437 -0
- package/src/monitoring/scanner-core.js +368 -0
- package/src/monitoring/scanner-events.js +214 -0
- package/src/monitoring/violation-notification-system.js +515 -0
- package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
- package/src/refactoring/boundaries/extraction-result.js +285 -0
- package/src/refactoring/boundaries/extraction-strategies.js +392 -0
- package/src/refactoring/boundaries/module-boundary.js +209 -0
- package/src/refactoring/boundary/boundary-detector.js +741 -0
- package/src/refactoring/boundary/boundary-types.js +405 -0
- package/src/refactoring/boundary/extraction-strategies.js +554 -0
- package/src/refactoring/boundary-extraction-result.js +77 -0
- package/src/refactoring/boundary-extraction-strategies.js +330 -0
- package/src/refactoring/boundary-extractor.js +384 -0
- package/src/refactoring/boundary-types.js +46 -0
- package/src/refactoring/circular/circular-dependency.js +88 -0
- package/src/refactoring/circular/cycle-detection.js +147 -0
- package/src/refactoring/circular/dependency-node.js +82 -0
- package/src/refactoring/circular/dependency-result.js +107 -0
- package/src/refactoring/circular/dependency-types.js +58 -0
- package/src/refactoring/circular/graph-builder.js +213 -0
- package/src/refactoring/circular/resolution-strategy.js +72 -0
- package/src/refactoring/circular/strategy-generator.js +229 -0
- package/src/refactoring/circular-dependency-resolver-original.js +809 -0
- package/src/refactoring/circular-dependency-resolver.js +200 -0
- package/src/refactoring/code-mover.js +761 -0
- package/src/refactoring/file-splitter.js +696 -0
- package/src/refactoring/functionality-validator.js +816 -0
- package/src/refactoring/import-manager.js +774 -0
- package/src/refactoring/module-boundary.js +107 -0
- package/src/refactoring/refactoring-executor.js +672 -0
- package/src/refactoring/refactoring-rollback.js +614 -0
- package/src/refactoring/test-validator.js +631 -0
- package/src/requirement-management/default-requirement-manager.js +321 -0
- package/src/requirement-management/requirement-file-parser.js +159 -0
- package/src/requirement-management/requirement-sequencer.js +221 -0
- package/src/rui/commands/AgentCommandParser.js +600 -0
- package/src/rui/commands/AgentCommands.js +487 -0
- package/src/rui/commands/AgentResponseFormatter.js +832 -0
- package/src/scripts/verify-full-compliance.js +269 -0
- package/src/sync/sync-engine-core.js +1 -0
- package/src/sync/sync-engine-remote-handlers.js +135 -0
- package/src/task-generation/automated-task-generator.js +351 -0
- package/src/task-generation/prioritizer.js +287 -0
- package/src/task-generation/task-list-updater.js +215 -0
- package/src/task-generation/task-management-integration.js +480 -0
- package/src/task-generation/task-manager-integration.js +270 -0
- package/src/task-generation/violation-task-generator.js +474 -0
- package/src/task-management/continuous-scan-integration.js +342 -0
- package/src/timeout-management/index.js +12 -3
- package/src/timeout-management/response-time-tracker.js +167 -0
- package/src/timeout-management/timeout-calculator.js +159 -0
- package/src/timeout-management/timeout-config-manager.js +172 -0
- package/src/utils/ast-analyzer.js +417 -0
- package/src/utils/current-requirement-manager.js +276 -0
- package/src/utils/current-requirement-operations.js +472 -0
- package/src/utils/dependency-mapper.js +456 -0
- package/src/utils/download-with-progress.js +4 -2
- package/src/utils/electron-update-checker.js +4 -1
- package/src/utils/file-size-analyzer.js +272 -0
- package/src/utils/import-updater.js +280 -0
- package/src/utils/refactoring-tools.js +512 -0
- package/src/utils/report-generator.js +569 -0
- package/src/utils/reports/report-analysis.js +218 -0
- package/src/utils/reports/report-types.js +55 -0
- package/src/utils/reports/summary-generators.js +102 -0
- package/src/utils/requirement-file-management.js +157 -0
- package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
- package/src/utils/requirement-helpers/requirement-mover.js +414 -0
- package/src/utils/requirement-helpers/requirement-parser.js +326 -0
- package/src/utils/requirement-helpers/requirement-status.js +320 -0
- package/src/utils/requirement-helpers-new.js +55 -0
- package/src/utils/requirement-helpers-refactored.js +367 -0
- package/src/utils/requirement-helpers.js +291 -1191
- package/src/utils/requirement-movement-operations.js +450 -0
- package/src/utils/requirement-movement.js +312 -0
- package/src/utils/requirement-parsing-helpers.js +56 -0
- package/src/utils/requirement-statistics.js +200 -0
- package/src/utils/requirement-text-utils.js +58 -0
- package/src/utils/rollback/rollback-handlers.js +125 -0
- package/src/utils/rollback/rollback-operation.js +63 -0
- package/src/utils/rollback/rollback-recorder.js +166 -0
- package/src/utils/rollback/rollback-state-manager.js +175 -0
- package/src/utils/rollback/rollback-types.js +33 -0
- package/src/utils/rollback/rollback-utils.js +110 -0
- package/src/utils/rollback-manager-original.js +569 -0
- package/src/utils/rollback-manager.js +202 -0
- package/src/utils/smoke-test-cli.js +362 -0
- package/src/utils/smoke-test-gui.js +351 -0
- package/src/utils/smoke-test-orchestrator.js +321 -0
- package/src/utils/smoke-test-runner.js +60 -0
- package/src/utils/smoke-test-web.js +347 -0
- package/src/utils/specification-helpers.js +39 -13
- package/src/utils/specification-migration.js +97 -0
- package/src/utils/test-runner.js +579 -0
- package/src/utils/validation-framework.js +518 -0
- package/src/validation/compliance-analyzer.js +197 -0
- package/src/validation/compliance-report-generator.js +343 -0
- package/src/validation/compliance-reporter.js +711 -0
- package/src/validation/compliance-rules.js +127 -0
- package/src/validation/constitution-validator-new.js +196 -0
- package/src/validation/constitution-validator.js +17 -0
- package/src/validation/file-validators.js +170 -0
- package/src/validation/line-limit/file-analyzer.js +201 -0
- package/src/validation/line-limit/line-limit-validator.js +208 -0
- package/src/validation/line-limit/validation-result.js +144 -0
- package/src/validation/line-limit-core.js +225 -0
- package/src/validation/line-limit-reporter.js +134 -0
- package/src/validation/line-limit-result.js +125 -0
- package/src/validation/line-limit-validator.js +41 -0
- package/src/validation/metrics-calculator.js +660 -0
- package/src/sync/sync-engine-backup.js +0 -559
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert System for File Size Compliance
|
|
3
|
+
*
|
|
4
|
+
* Provides intelligent alerting for files approaching or exceeding size limits,
|
|
5
|
+
* with configurable thresholds, notification channels, and escalation policies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const EventEmitter = require('events');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Alert severity levels
|
|
14
|
+
*/
|
|
15
|
+
const ALERT_SEVERITY = {
|
|
16
|
+
INFO: 'info',
|
|
17
|
+
WARNING: 'warning',
|
|
18
|
+
ERROR: 'error',
|
|
19
|
+
CRITICAL: 'critical'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Alert categories
|
|
24
|
+
*/
|
|
25
|
+
const ALERT_CATEGORIES = {
|
|
26
|
+
FILE_SIZE: 'file_size',
|
|
27
|
+
CONSTITUTIONAL: 'constitutional',
|
|
28
|
+
PERFORMANCE: 'performance',
|
|
29
|
+
SYSTEM: 'system'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Alert class
|
|
34
|
+
*/
|
|
35
|
+
class Alert {
|
|
36
|
+
constructor(data = {}) {
|
|
37
|
+
this.id = data.id || this.generateId();
|
|
38
|
+
this.category = data.category || ALERT_CATEGORIES.FILE_SIZE;
|
|
39
|
+
this.severity = data.severity || ALERT_SEVERITY.WARNING;
|
|
40
|
+
this.title = data.title || '';
|
|
41
|
+
this.message = data.message || '';
|
|
42
|
+
this.description = data.description || '';
|
|
43
|
+
this.filePath = data.filePath || '';
|
|
44
|
+
this.metadata = {
|
|
45
|
+
timestamp: data.timestamp || new Date().toISOString(),
|
|
46
|
+
source: data.source || 'alert-system',
|
|
47
|
+
version: data.version || '1.0.0',
|
|
48
|
+
...data.metadata
|
|
49
|
+
};
|
|
50
|
+
this.context = {
|
|
51
|
+
lineCount: data.lineCount || 0,
|
|
52
|
+
limit: data.limit || 555,
|
|
53
|
+
excessLines: data.excessLines || 0,
|
|
54
|
+
percentage: data.percentage || 0,
|
|
55
|
+
...data.context
|
|
56
|
+
};
|
|
57
|
+
this.actions = data.actions || [];
|
|
58
|
+
this.escalationLevel = data.escalationLevel || 0;
|
|
59
|
+
this.isResolved = data.isResolved || false;
|
|
60
|
+
this.resolvedAt = data.resolvedAt || null;
|
|
61
|
+
this.resolvedBy = data.resolvedBy || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
generateId() {
|
|
65
|
+
return `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
resolve(resolvedBy = 'system') {
|
|
69
|
+
this.isResolved = true;
|
|
70
|
+
this.resolvedAt = new Date().toISOString();
|
|
71
|
+
this.resolvedBy = resolvedBy;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
escalate() {
|
|
76
|
+
this.escalationLevel++;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
toJSON() {
|
|
81
|
+
return {
|
|
82
|
+
id: this.id,
|
|
83
|
+
category: this.category,
|
|
84
|
+
severity: this.severity,
|
|
85
|
+
title: this.title,
|
|
86
|
+
message: this.message,
|
|
87
|
+
description: this.description,
|
|
88
|
+
filePath: this.filePath,
|
|
89
|
+
metadata: this.metadata,
|
|
90
|
+
context: this.context,
|
|
91
|
+
actions: this.actions,
|
|
92
|
+
escalationLevel: this.escalationLevel,
|
|
93
|
+
isResolved: this.isResolved,
|
|
94
|
+
resolvedAt: this.resolvedAt,
|
|
95
|
+
resolvedBy: this.resolvedBy
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static fromJSON(data) {
|
|
100
|
+
return new Alert(data);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Alert system class
|
|
106
|
+
*/
|
|
107
|
+
class AlertSystem extends EventEmitter {
|
|
108
|
+
constructor(options = {}) {
|
|
109
|
+
super();
|
|
110
|
+
|
|
111
|
+
this.options = {
|
|
112
|
+
// Thresholds
|
|
113
|
+
warningThreshold: options.warningThreshold || 500,
|
|
114
|
+
errorThreshold: options.errorThreshold || 555,
|
|
115
|
+
criticalThreshold: options.criticalThreshold || 800,
|
|
116
|
+
|
|
117
|
+
// Alert settings
|
|
118
|
+
enableNotifications: options.enableNotifications !== false,
|
|
119
|
+
enableEscalation: options.enableEscalation !== false,
|
|
120
|
+
enableCooldown: options.enableCooldown !== false,
|
|
121
|
+
cooldownMs: options.cooldownMs || 300000, // 5 minutes
|
|
122
|
+
|
|
123
|
+
// Escalation settings
|
|
124
|
+
maxEscalationLevel: options.maxEscalationLevel || 3,
|
|
125
|
+
escalationIntervalMs: options.escalationIntervalMs || 1800000, // 30 minutes
|
|
126
|
+
|
|
127
|
+
// Notification channels
|
|
128
|
+
channels: options.channels || ['console', 'file'],
|
|
129
|
+
logFile: options.logFile || './logs/alerts.log',
|
|
130
|
+
|
|
131
|
+
// Filtering
|
|
132
|
+
duplicateAlertThresholdMs: options.duplicateAlertThresholdMs || 60000, // 1 minute
|
|
133
|
+
|
|
134
|
+
...options
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
this.alerts = new Map(); // active alerts
|
|
138
|
+
this.alertHistory = []; // all alerts
|
|
139
|
+
this.cooldownTimers = new Map();
|
|
140
|
+
this.escalationTimers = new Map();
|
|
141
|
+
this.lastAlerts = new Map(); // for duplicate detection
|
|
142
|
+
|
|
143
|
+
this.stats = {
|
|
144
|
+
totalAlerts: 0,
|
|
145
|
+
alertsBySeverity: { info: 0, warning: 0, error: 0, critical: 0 },
|
|
146
|
+
alertsByCategory: {},
|
|
147
|
+
escalatedAlerts: 0,
|
|
148
|
+
resolvedAlerts: 0,
|
|
149
|
+
startTime: new Date(),
|
|
150
|
+
lastAlertTime: null
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Ensure log directory exists
|
|
154
|
+
if (this.options.channels.includes('file')) {
|
|
155
|
+
const logDir = path.dirname(this.options.logFile);
|
|
156
|
+
if (!fs.existsSync(logDir)) {
|
|
157
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Process file validation results and generate alerts
|
|
164
|
+
*/
|
|
165
|
+
processValidationResults(filePath, validationResults) {
|
|
166
|
+
const alerts = [];
|
|
167
|
+
|
|
168
|
+
// File size alerts
|
|
169
|
+
if (validationResults.lineLimitValidation) {
|
|
170
|
+
const lineAlert = this.createFileSizeAlert(filePath, validationResults);
|
|
171
|
+
if (lineAlert) alerts.push(lineAlert);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Constitutional compliance alerts
|
|
175
|
+
if (validationResults.constitutionalValidation) {
|
|
176
|
+
const constitutionalAlerts = this.createConstitutionalAlerts(filePath, validationResults);
|
|
177
|
+
alerts.push(...constitutionalAlerts);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Process all alerts
|
|
181
|
+
for (const alert of alerts) {
|
|
182
|
+
this.processAlert(alert);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return alerts;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Create file size alert
|
|
190
|
+
*/
|
|
191
|
+
createFileSizeAlert(filePath, validationResults) {
|
|
192
|
+
const lineValidation = validationResults.lineLimitValidation;
|
|
193
|
+
const lineCount = validationResults.lineCount || 0;
|
|
194
|
+
|
|
195
|
+
if (!lineValidation.violation) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const violation = lineValidation.violation;
|
|
200
|
+
let severity = ALERT_SEVERITY.WARNING;
|
|
201
|
+
let title = 'File Size Warning';
|
|
202
|
+
|
|
203
|
+
if (lineCount > this.options.criticalThreshold) {
|
|
204
|
+
severity = ALERT_SEVERITY.CRITICAL;
|
|
205
|
+
title = 'Critical File Size Violation';
|
|
206
|
+
} else if (lineCount > this.options.errorThreshold) {
|
|
207
|
+
severity = ALERT_SEVERITY.ERROR;
|
|
208
|
+
title = 'File Size Violation';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return new Alert({
|
|
212
|
+
category: ALERT_CATEGORIES.FILE_SIZE,
|
|
213
|
+
severity,
|
|
214
|
+
title,
|
|
215
|
+
message: `${filePath} has ${lineCount} lines (limit: ${this.options.errorThreshold})`,
|
|
216
|
+
description: `File exceeds the 555-line limit by ${violation.excessLines} lines (${violation.percentageOverLimit}% over)`,
|
|
217
|
+
filePath,
|
|
218
|
+
lineCount,
|
|
219
|
+
limit: this.options.errorThreshold,
|
|
220
|
+
excessLines: violation.excessLines,
|
|
221
|
+
percentage: violation.percentageOverLimit,
|
|
222
|
+
actions: [
|
|
223
|
+
'Refactor file into smaller modules',
|
|
224
|
+
'Extract related functionality into separate files',
|
|
225
|
+
'Consider splitting by logical boundaries'
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Create constitutional compliance alerts
|
|
232
|
+
*/
|
|
233
|
+
createConstitutionalAlerts(filePath, validationResults) {
|
|
234
|
+
const alerts = [];
|
|
235
|
+
const constitutionalValidation = validationResults.constitutionalValidation || [];
|
|
236
|
+
|
|
237
|
+
for (const issue of constitutionalValidation) {
|
|
238
|
+
if (issue.severity === 'info') continue; // Skip info-level issues
|
|
239
|
+
|
|
240
|
+
const severity = issue.severity === 'critical' ? ALERT_SEVERITY.CRITICAL :
|
|
241
|
+
issue.severity === 'error' ? ALERT_SEVERITY.ERROR :
|
|
242
|
+
ALERT_SEVERITY.WARNING;
|
|
243
|
+
|
|
244
|
+
const alert = new Alert({
|
|
245
|
+
category: ALERT_CATEGORIES.CONSTITUTIONAL,
|
|
246
|
+
severity,
|
|
247
|
+
title: `${issue.category.charAt(0).toUpperCase() + issue.category.slice(1)} Violation`,
|
|
248
|
+
message: `${filePath}: ${issue.message}`,
|
|
249
|
+
description: issue.description || `Constitutional compliance issue in ${issue.category}`,
|
|
250
|
+
filePath,
|
|
251
|
+
context: {
|
|
252
|
+
rule: issue.rule,
|
|
253
|
+
lineNumber: issue.lineNumber,
|
|
254
|
+
...issue.details
|
|
255
|
+
},
|
|
256
|
+
actions: issue.suggestion ? [issue.suggestion] : ['Review constitutional requirements']
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
alerts.push(alert);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return alerts;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Process an alert
|
|
267
|
+
*/
|
|
268
|
+
processAlert(alert) {
|
|
269
|
+
// Check for duplicates
|
|
270
|
+
if (this.isDuplicateAlert(alert)) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check cooldown
|
|
275
|
+
if (this.isInCooldown(alert.filePath, alert.category)) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Add to active alerts
|
|
280
|
+
this.alerts.set(alert.id, alert);
|
|
281
|
+
this.alertHistory.push(alert);
|
|
282
|
+
|
|
283
|
+
// Update stats
|
|
284
|
+
this.updateStats(alert);
|
|
285
|
+
|
|
286
|
+
// Set cooldown
|
|
287
|
+
if (this.options.enableCooldown) {
|
|
288
|
+
this.setCooldown(alert.filePath, alert.category);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Setup escalation if enabled
|
|
292
|
+
if (this.options.enableEscalation && alert.severity !== 'info') {
|
|
293
|
+
this.setupEscalation(alert);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Send notifications
|
|
297
|
+
this.sendNotifications(alert);
|
|
298
|
+
|
|
299
|
+
// Emit events
|
|
300
|
+
this.emit('alert', alert);
|
|
301
|
+
this.emit(`alert:${alert.severity}`, alert);
|
|
302
|
+
this.emit(`alert:${alert.category}`, alert);
|
|
303
|
+
|
|
304
|
+
// Update last alert tracking
|
|
305
|
+
this.lastAlerts.set(`${alert.filePath}:${alert.category}`, {
|
|
306
|
+
timestamp: Date.now(),
|
|
307
|
+
alertId: alert.id
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return alert;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Check if alert is duplicate
|
|
315
|
+
*/
|
|
316
|
+
isDuplicateAlert(alert) {
|
|
317
|
+
const key = `${alert.filePath}:${alert.category}`;
|
|
318
|
+
const lastAlert = this.lastAlerts.get(key);
|
|
319
|
+
|
|
320
|
+
if (!lastAlert) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const timeDiff = Date.now() - lastAlert.timestamp;
|
|
325
|
+
return timeDiff < this.options.duplicateAlertThresholdMs;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Check if in cooldown period
|
|
330
|
+
*/
|
|
331
|
+
isInCooldown(filePath, category) {
|
|
332
|
+
const key = `${filePath}:${category}`;
|
|
333
|
+
return this.cooldownTimers.has(key);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Set cooldown period
|
|
338
|
+
*/
|
|
339
|
+
setCooldown(filePath, category) {
|
|
340
|
+
const key = `${filePath}:${category}`;
|
|
341
|
+
|
|
342
|
+
// Clear existing timer
|
|
343
|
+
if (this.cooldownTimers.has(key)) {
|
|
344
|
+
clearTimeout(this.cooldownTimers.get(key));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Set new timer
|
|
348
|
+
const timer = setTimeout(() => {
|
|
349
|
+
this.cooldownTimers.delete(key);
|
|
350
|
+
}, this.options.cooldownMs);
|
|
351
|
+
|
|
352
|
+
this.cooldownTimers.set(key, timer);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Setup escalation for alert
|
|
357
|
+
*/
|
|
358
|
+
setupEscalation(alert) {
|
|
359
|
+
if (alert.escalationLevel >= this.options.maxEscalationLevel) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const escalationKey = alert.id;
|
|
364
|
+
|
|
365
|
+
// Clear existing escalation timer
|
|
366
|
+
if (this.escalationTimers.has(escalationKey)) {
|
|
367
|
+
clearTimeout(this.escalationTimers.get(escalationKey));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Set escalation timer
|
|
371
|
+
const timer = setTimeout(() => {
|
|
372
|
+
this.escalateAlert(alert);
|
|
373
|
+
}, this.options.escalationIntervalMs);
|
|
374
|
+
|
|
375
|
+
this.escalationTimers.set(escalationKey, timer);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Escalate alert
|
|
380
|
+
*/
|
|
381
|
+
escalateAlert(alert) {
|
|
382
|
+
if (alert.escalationLevel >= this.options.maxEscalationLevel) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
alert.escalate();
|
|
387
|
+
this.stats.escalatedAlerts++;
|
|
388
|
+
|
|
389
|
+
// Update severity if needed
|
|
390
|
+
if (alert.severity === ALERT_SEVERITY.WARNING && alert.escalationLevel >= 1) {
|
|
391
|
+
alert.severity = ALERT_SEVERITY.ERROR;
|
|
392
|
+
} else if (alert.severity === ALERT_SEVERITY.ERROR && alert.escalationLevel >= 2) {
|
|
393
|
+
alert.severity = ALERT_SEVERITY.CRITICAL;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Update title
|
|
397
|
+
alert.title = `[ESCALATED L${alert.escalationLevel}] ${alert.title}`;
|
|
398
|
+
|
|
399
|
+
// Send notifications for escalated alert
|
|
400
|
+
this.sendNotifications(alert);
|
|
401
|
+
|
|
402
|
+
// Emit escalation event
|
|
403
|
+
this.emit('alertEscalated', alert);
|
|
404
|
+
|
|
405
|
+
// Setup next escalation
|
|
406
|
+
this.setupEscalation(alert);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Handle new violations from continuous scan
|
|
411
|
+
*/
|
|
412
|
+
handleContinuousScanViolations(violations, scanInfo = {}) {
|
|
413
|
+
const alerts = [];
|
|
414
|
+
|
|
415
|
+
for (const violation of violations) {
|
|
416
|
+
// Create alert for each new violation
|
|
417
|
+
const alert = this.createViolationAlert(violation.filePath, {
|
|
418
|
+
lineCount: violation.lineCount,
|
|
419
|
+
lineLimitValidation: violation.lineLimitValidation,
|
|
420
|
+
constitutionalValidation: violation.constitutionalValidation
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
if (alert) {
|
|
424
|
+
// Add continuous scan metadata
|
|
425
|
+
alert.metadata.source = 'continuous-scan';
|
|
426
|
+
alert.metadata.scanId = scanInfo.scanId;
|
|
427
|
+
alert.metadata.scanTimestamp = scanInfo.timestamp;
|
|
428
|
+
alert.metadata.isNewViolation = true;
|
|
429
|
+
|
|
430
|
+
alerts.push(alert);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Send batch notification if multiple violations
|
|
435
|
+
if (alerts.length > 1) {
|
|
436
|
+
this.sendBatchViolationAlert(alerts, scanInfo);
|
|
437
|
+
} else if (alerts.length === 1) {
|
|
438
|
+
this.sendAlert(alerts[0]);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return alerts;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Send batch violation alert
|
|
446
|
+
*/
|
|
447
|
+
sendBatchViolationAlert(alerts, scanInfo) {
|
|
448
|
+
const batchAlert = new Alert({
|
|
449
|
+
category: ALERT_CATEGORIES.FILE_SIZE,
|
|
450
|
+
severity: ALERT_SEVERITY.WARNING,
|
|
451
|
+
title: `Batch Violation Alert - ${alerts.length} files`,
|
|
452
|
+
message: `Continuous scan detected ${alerts.length} files with size violations`,
|
|
453
|
+
description: `The following files exceeded the 555-line limit: ${alerts.map(a => a.filePath).join(', ')}`,
|
|
454
|
+
metadata: {
|
|
455
|
+
source: 'continuous-scan',
|
|
456
|
+
scanId: scanInfo.scanId,
|
|
457
|
+
scanTimestamp: scanInfo.timestamp,
|
|
458
|
+
violationCount: alerts.length,
|
|
459
|
+
violationFiles: alerts.map(a => ({
|
|
460
|
+
filePath: a.filePath,
|
|
461
|
+
lineCount: a.metadata.lineCount,
|
|
462
|
+
excessLines: a.metadata.excessLines
|
|
463
|
+
}))
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
this.sendAlert(batchAlert);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Send notifications through configured channels
|
|
472
|
+
*/
|
|
473
|
+
sendNotifications(alert) {
|
|
474
|
+
for (const channel of this.options.channels) {
|
|
475
|
+
try {
|
|
476
|
+
switch (channel) {
|
|
477
|
+
case 'console':
|
|
478
|
+
this.sendConsoleNotification(alert);
|
|
479
|
+
break;
|
|
480
|
+
case 'file':
|
|
481
|
+
this.sendFileNotification(alert);
|
|
482
|
+
break;
|
|
483
|
+
case 'email':
|
|
484
|
+
this.sendEmailNotification(alert);
|
|
485
|
+
break;
|
|
486
|
+
case 'slack':
|
|
487
|
+
this.sendSlackNotification(alert);
|
|
488
|
+
break;
|
|
489
|
+
default:
|
|
490
|
+
console.warn(`Unknown notification channel: ${channel}`);
|
|
491
|
+
}
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.error(`Failed to send ${channel} notification:`, error);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Send console notification
|
|
500
|
+
*/
|
|
501
|
+
sendConsoleNotification(alert) {
|
|
502
|
+
const colors = {
|
|
503
|
+
info: '\x1b[36m',
|
|
504
|
+
warning: '\x1b[33m',
|
|
505
|
+
error: '\x1b[31m',
|
|
506
|
+
critical: '\x1b[35m',
|
|
507
|
+
reset: '\x1b[0m',
|
|
508
|
+
bold: '\x1b[1m'
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const color = colors[alert.severity] || colors.info;
|
|
512
|
+
const escalation = alert.escalationLevel > 0 ? ` [L${alert.escalationLevel}]` : '';
|
|
513
|
+
|
|
514
|
+
console.log(`${color}${colors.bold}[ALERT${escalation}] ${alert.title}${colors.reset}`);
|
|
515
|
+
console.log(`${color} ${alert.message}${colors.reset}`);
|
|
516
|
+
console.log(` File: ${alert.filePath}`);
|
|
517
|
+
console.log(` Time: ${new Date(alert.metadata.timestamp).toLocaleString()}`);
|
|
518
|
+
|
|
519
|
+
if (alert.context.lineCount) {
|
|
520
|
+
console.log(` Lines: ${alert.context.lineCount}/${alert.context.limit}`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (alert.actions.length > 0) {
|
|
524
|
+
console.log(` Actions: ${alert.actions.slice(0, 2).join(', ')}`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
console.log('');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Send file notification
|
|
532
|
+
*/
|
|
533
|
+
sendFileNotification(alert) {
|
|
534
|
+
const logEntry = {
|
|
535
|
+
timestamp: alert.metadata.timestamp,
|
|
536
|
+
id: alert.id,
|
|
537
|
+
severity: alert.severity,
|
|
538
|
+
category: alert.category,
|
|
539
|
+
title: alert.title,
|
|
540
|
+
message: alert.message,
|
|
541
|
+
filePath: alert.filePath,
|
|
542
|
+
context: alert.context,
|
|
543
|
+
escalationLevel: alert.escalationLevel
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const logLine = JSON.stringify(logEntry) + '\n';
|
|
547
|
+
fs.appendFileSync(this.options.logFile, logLine, 'utf8');
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Send email notification (placeholder)
|
|
552
|
+
*/
|
|
553
|
+
sendEmailNotification(alert) {
|
|
554
|
+
// Placeholder for email implementation
|
|
555
|
+
console.log(`📧 Email notification sent for alert: ${alert.title}`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Send Slack notification (placeholder)
|
|
560
|
+
*/
|
|
561
|
+
sendSlackNotification(alert) {
|
|
562
|
+
// Placeholder for Slack implementation
|
|
563
|
+
console.log(`💬 Slack notification sent for alert: ${alert.title}`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Resolve alert
|
|
568
|
+
*/
|
|
569
|
+
resolveAlert(alertId, resolvedBy = 'user') {
|
|
570
|
+
const alert = this.alerts.get(alertId);
|
|
571
|
+
if (!alert) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
alert.resolve(resolvedBy);
|
|
576
|
+
this.alerts.delete(alertId);
|
|
577
|
+
this.stats.resolvedAlerts++;
|
|
578
|
+
|
|
579
|
+
// Clear escalation timer
|
|
580
|
+
if (this.escalationTimers.has(alertId)) {
|
|
581
|
+
clearTimeout(this.escalationTimers.get(alertId));
|
|
582
|
+
this.escalationTimers.delete(alertId);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Emit resolution event
|
|
586
|
+
this.emit('alertResolved', alert);
|
|
587
|
+
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Resolve alerts for file
|
|
593
|
+
*/
|
|
594
|
+
resolveAlertsForFile(filePath, resolvedBy = 'user') {
|
|
595
|
+
const resolvedAlerts = [];
|
|
596
|
+
|
|
597
|
+
for (const [alertId, alert] of this.alerts.entries()) {
|
|
598
|
+
if (alert.filePath === filePath) {
|
|
599
|
+
this.resolveAlert(alertId, resolvedBy);
|
|
600
|
+
resolvedAlerts.push(alert);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return resolvedAlerts;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Get active alerts
|
|
609
|
+
*/
|
|
610
|
+
getActiveAlerts(filters = {}) {
|
|
611
|
+
let alerts = Array.from(this.alerts.values());
|
|
612
|
+
|
|
613
|
+
// Apply filters
|
|
614
|
+
if (filters.severity) {
|
|
615
|
+
alerts = alerts.filter(alert => alert.severity === filters.severity);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (filters.category) {
|
|
619
|
+
alerts = alerts.filter(alert => alert.category === filters.category);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (filters.filePath) {
|
|
623
|
+
alerts = alerts.filter(alert => alert.filePath === filters.filePath);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (filters.unresolvedOnly) {
|
|
627
|
+
alerts = alerts.filter(alert => !alert.isResolved);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Sort by timestamp (newest first)
|
|
631
|
+
alerts.sort((a, b) => new Date(b.metadata.timestamp) - new Date(a.metadata.timestamp));
|
|
632
|
+
|
|
633
|
+
return alerts;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Get alert statistics
|
|
638
|
+
*/
|
|
639
|
+
getStats() {
|
|
640
|
+
const uptime = Date.now() - this.stats.startTime.getTime();
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
...this.stats,
|
|
644
|
+
uptime,
|
|
645
|
+
activeAlerts: this.alerts.size,
|
|
646
|
+
alertsInHistory: this.alertHistory.length,
|
|
647
|
+
averageAlertsPerHour: this.stats.totalAlerts > 0 ?
|
|
648
|
+
(this.stats.totalAlerts / (uptime / 1000 / 60 / 60)) : 0,
|
|
649
|
+
resolutionRate: this.stats.totalAlerts > 0 ?
|
|
650
|
+
(this.stats.resolvedAlerts / this.stats.totalAlerts * 100) : 0
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Update statistics
|
|
656
|
+
*/
|
|
657
|
+
updateStats(alert) {
|
|
658
|
+
this.stats.totalAlerts++;
|
|
659
|
+
this.stats.alertsBySeverity[alert.severity]++;
|
|
660
|
+
this.stats.alertsByCategory[alert.category] =
|
|
661
|
+
(this.stats.alertsByCategory[alert.category] || 0) + 1;
|
|
662
|
+
this.stats.lastAlertTime = new Date();
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Generate alert report
|
|
667
|
+
*/
|
|
668
|
+
generateReport(format = 'json') {
|
|
669
|
+
const stats = this.getStats();
|
|
670
|
+
const activeAlerts = this.getActiveAlerts();
|
|
671
|
+
|
|
672
|
+
const report = {
|
|
673
|
+
metadata: {
|
|
674
|
+
generatedAt: new Date().toISOString(),
|
|
675
|
+
uptime: stats.uptime,
|
|
676
|
+
system: 'alert-system'
|
|
677
|
+
},
|
|
678
|
+
summary: stats,
|
|
679
|
+
activeAlerts: activeAlerts.map(alert => alert.toJSON()),
|
|
680
|
+
recentAlerts: this.alertHistory.slice(-10).map(alert => alert.toJSON()),
|
|
681
|
+
topAlertFiles: this.getTopAlertFiles(),
|
|
682
|
+
alertTrends: this.getAlertTrends()
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
switch (format.toLowerCase()) {
|
|
686
|
+
case 'json':
|
|
687
|
+
return JSON.stringify(report, null, 2);
|
|
688
|
+
case 'summary':
|
|
689
|
+
return this.generateSummaryReport(report);
|
|
690
|
+
default:
|
|
691
|
+
return report;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Generate summary report
|
|
697
|
+
*/
|
|
698
|
+
generateSummaryReport(report) {
|
|
699
|
+
const summary = report.summary;
|
|
700
|
+
|
|
701
|
+
let text = '# Alert System Summary Report\n\n';
|
|
702
|
+
text += `**Generated:** ${new Date().toLocaleString()}\n`;
|
|
703
|
+
text += `**Uptime:** ${Math.floor(summary.uptime / 1000 / 60)} minutes\n\n`;
|
|
704
|
+
|
|
705
|
+
text += `## Summary\n\n`;
|
|
706
|
+
text += `- **Total Alerts:** ${summary.totalAlerts}\n`;
|
|
707
|
+
text += `- **Active Alerts:** ${summary.activeAlerts}\n`;
|
|
708
|
+
text += `- **Resolved Alerts:** ${summary.resolvedAlerts}\n`;
|
|
709
|
+
text += `- **Escalated Alerts:** ${summary.escalatedAlerts}\n`;
|
|
710
|
+
text += `- **Resolution Rate:** ${summary.resolutionRate.toFixed(1)}%\n`;
|
|
711
|
+
text += `- **Avg Alerts/Hour:** ${summary.averageAlertsPerHour.toFixed(1)}\n\n`;
|
|
712
|
+
|
|
713
|
+
text += `## Alerts by Severity\n\n`;
|
|
714
|
+
for (const [severity, count] of Object.entries(summary.alertsBySeverity)) {
|
|
715
|
+
text += `- **${severity}:** ${count}\n`;
|
|
716
|
+
}
|
|
717
|
+
text += '\n';
|
|
718
|
+
|
|
719
|
+
text += `## Alerts by Category\n\n`;
|
|
720
|
+
for (const [category, count] of Object.entries(summary.alertsByCategory)) {
|
|
721
|
+
text += `- **${category}:** ${count}\n`;
|
|
722
|
+
}
|
|
723
|
+
text += '\n';
|
|
724
|
+
|
|
725
|
+
if (report.topAlertFiles.length > 0) {
|
|
726
|
+
text += `## Top Alert Files\n\n`;
|
|
727
|
+
for (const file of report.topAlertFiles) {
|
|
728
|
+
text += `- **${file.filePath}:** ${file.alertCount} alerts\n`;
|
|
729
|
+
}
|
|
730
|
+
text += '\n';
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return text;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Get top alert files
|
|
738
|
+
*/
|
|
739
|
+
getTopAlertFiles() {
|
|
740
|
+
const fileAlertCounts = {};
|
|
741
|
+
|
|
742
|
+
for (const alert of this.alertHistory) {
|
|
743
|
+
const filePath = alert.filePath;
|
|
744
|
+
fileAlertCounts[filePath] = (fileAlertCounts[filePath] || 0) + 1;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return Object.entries(fileAlertCounts)
|
|
748
|
+
.map(([filePath, alertCount]) => ({ filePath, alertCount }))
|
|
749
|
+
.sort((a, b) => b.alertCount - a.alertCount)
|
|
750
|
+
.slice(0, 10);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Get alert trends
|
|
755
|
+
*/
|
|
756
|
+
getAlertTrends() {
|
|
757
|
+
const now = Date.now();
|
|
758
|
+
const intervals = {
|
|
759
|
+
'1h': 60 * 60 * 1000,
|
|
760
|
+
'6h': 6 * 60 * 60 * 1000,
|
|
761
|
+
'24h': 24 * 60 * 60 * 1000
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const trends = {};
|
|
765
|
+
|
|
766
|
+
for (const [label, intervalMs] of Object.entries(intervals)) {
|
|
767
|
+
const cutoff = now - intervalMs;
|
|
768
|
+
const alerts = this.alertHistory.filter(alert =>
|
|
769
|
+
new Date(alert.metadata.timestamp).getTime() > cutoff
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
trends[label] = {
|
|
773
|
+
count: alerts.length,
|
|
774
|
+
rate: alerts.length / (intervalMs / 1000 / 60 / 60) // per hour
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return trends;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Clear all alerts
|
|
783
|
+
*/
|
|
784
|
+
clearAllAlerts() {
|
|
785
|
+
// Clear all timers
|
|
786
|
+
for (const timer of this.cooldownTimers.values()) {
|
|
787
|
+
clearTimeout(timer);
|
|
788
|
+
}
|
|
789
|
+
for (const timer of this.escalationTimers.values()) {
|
|
790
|
+
clearTimeout(timer);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// Clear data
|
|
794
|
+
this.alerts.clear();
|
|
795
|
+
this.cooldownTimers.clear();
|
|
796
|
+
this.escalationTimers.clear();
|
|
797
|
+
this.lastAlerts.clear();
|
|
798
|
+
|
|
799
|
+
// Reset stats
|
|
800
|
+
this.stats = {
|
|
801
|
+
totalAlerts: 0,
|
|
802
|
+
alertsBySeverity: { info: 0, warning: 0, error: 0, critical: 0 },
|
|
803
|
+
alertsByCategory: {},
|
|
804
|
+
escalatedAlerts: 0,
|
|
805
|
+
resolvedAlerts: 0,
|
|
806
|
+
startTime: new Date(),
|
|
807
|
+
lastAlertTime: null
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
this.emit('alertsCleared');
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Shutdown alert system
|
|
815
|
+
*/
|
|
816
|
+
shutdown() {
|
|
817
|
+
// Clear all timers
|
|
818
|
+
for (const timer of this.cooldownTimers.values()) {
|
|
819
|
+
clearTimeout(timer);
|
|
820
|
+
}
|
|
821
|
+
for (const timer of this.escalationTimers.values()) {
|
|
822
|
+
clearTimeout(timer);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
this.emit('shutdown');
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
module.exports = {
|
|
830
|
+
AlertSystem,
|
|
831
|
+
Alert,
|
|
832
|
+
ALERT_SEVERITY,
|
|
833
|
+
ALERT_CATEGORIES
|
|
834
|
+
};
|