thuban 0.3.3 → 0.3.4
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/dist/LICENSE +21 -0
- package/dist/README.md +185 -0
- package/dist/cli.js +2 -0
- package/dist/packages/scanner/ai-confidence-scorer.js +1 -0
- package/dist/packages/scanner/alert-manager.js +1 -0
- package/dist/packages/scanner/baseline-manager.js +1 -0
- package/dist/packages/scanner/code-scanner.js +1 -0
- package/dist/packages/scanner/codebase-passport.js +1 -0
- package/dist/packages/scanner/copy-paste-detector.js +1 -0
- package/dist/packages/scanner/dependency-graph.js +1 -0
- package/dist/packages/scanner/drift-detector.js +1 -0
- package/dist/packages/scanner/executive-report.js +1 -0
- package/dist/packages/scanner/file-collector.js +1 -0
- package/dist/packages/scanner/file-watcher.js +1 -0
- package/dist/packages/scanner/ghost-code-detector.js +1 -0
- package/dist/packages/scanner/hallucination-detector.js +1 -0
- package/dist/packages/scanner/health-checker.js +1 -0
- package/dist/packages/scanner/html-report.js +1 -0
- package/dist/packages/scanner/index.js +1 -0
- package/dist/packages/scanner/license-manager.js +1 -0
- package/dist/packages/scanner/master-health-checker.js +1 -0
- package/dist/packages/scanner/monitor-notifier.js +1 -0
- package/dist/packages/scanner/monitor-service.js +1 -0
- package/dist/packages/scanner/monitor-store.js +1 -0
- package/dist/packages/scanner/pre-commit-gate.js +1 -0
- package/dist/packages/scanner/scan-diff.js +1 -0
- package/dist/packages/scanner/scan-runner.js +1 -0
- package/dist/packages/scanner/secret-scanner.js +1 -0
- package/dist/packages/scanner/sentinel-core.js +1 -0
- package/dist/packages/scanner/sentinel-knowledge.js +1 -0
- package/dist/packages/scanner/tech-debt-analyzer.js +1 -0
- package/dist/packages/scanner/tech-debt-cost.js +1 -0
- package/dist/packages/scanner/widget-generator.js +1 -0
- package/package.json +12 -7
- package/cli.js +0 -2627
- package/packages/scanner/ai-confidence-scorer.js +0 -260
- package/packages/scanner/alert-manager.js +0 -398
- package/packages/scanner/baseline-manager.js +0 -109
- package/packages/scanner/code-scanner.js +0 -758
- package/packages/scanner/codebase-passport.js +0 -299
- package/packages/scanner/copy-paste-detector.js +0 -276
- package/packages/scanner/dependency-graph.js +0 -541
- package/packages/scanner/drift-detector.js +0 -423
- package/packages/scanner/executive-report.js +0 -774
- package/packages/scanner/file-collector.js +0 -64
- package/packages/scanner/file-watcher.js +0 -323
- package/packages/scanner/ghost-code-detector.js +0 -301
- package/packages/scanner/hallucination-detector.js +0 -822
- package/packages/scanner/health-checker.js +0 -586
- package/packages/scanner/html-report.js +0 -634
- package/packages/scanner/index.js +0 -100
- package/packages/scanner/license-manager.js +0 -331
- package/packages/scanner/master-health-checker.js +0 -513
- package/packages/scanner/monitor-notifier.js +0 -64
- package/packages/scanner/monitor-service.js +0 -103
- package/packages/scanner/monitor-store.js +0 -117
- package/packages/scanner/pre-commit-gate.js +0 -216
- package/packages/scanner/scan-diff.js +0 -50
- package/packages/scanner/scan-runner.js +0 -172
- package/packages/scanner/secret-scanner.js +0 -612
- package/packages/scanner/secret-scanner.test.js +0 -103
- package/packages/scanner/sentinel-core.js +0 -616
- package/packages/scanner/sentinel-knowledge.js +0 -322
- package/packages/scanner/tech-debt-analyzer.js +0 -583
- package/packages/scanner/tech-debt-cost.js +0 -194
- package/packages/scanner/widget-generator.js +0 -415
|
@@ -1,616 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sentinel Core
|
|
3
|
-
*
|
|
4
|
-
* @purpose Event-driven SentinelCore component
|
|
5
|
-
* @module sentinel
|
|
6
|
-
* @layer application
|
|
7
|
-
* @imports-from fs, path, events, sentinel/file-watcher.js, sentinel/code-scanner.js, sentinel/drift-detector.js, sentinel/health-checker.js, sentinel/alert-manager.js
|
|
8
|
-
* @exports-to sentinel
|
|
9
|
-
* @side-effects Process termination, Console output, Event emission
|
|
10
|
-
* @critical-for sentinel
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Sentinel Core - Codebase Monitoring Orchestrator
|
|
15
|
-
*
|
|
16
|
-
* Continuous monitoring system that catches problems before they ship.
|
|
17
|
-
* Coordinates file watching, code scanning, drift detection, and alerting.
|
|
18
|
-
*
|
|
19
|
-
* Part of Orion OS - will be spun out as Thuban SaaS
|
|
20
|
-
*
|
|
21
|
-
* Schedule:
|
|
22
|
-
* - On commit: Security scan, contract validation, dependency update
|
|
23
|
-
* - Every 6 hours: Full AST scan vs widget comparison
|
|
24
|
-
* - Nightly: Complete rescan, CVE database check, architectural drift
|
|
25
|
-
* - Weekly: Deep quality audit, trend report, technical debt assessment
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
const fs = require('fs');
|
|
29
|
-
const path = require('path');
|
|
30
|
-
const EventEmitter = require('events');
|
|
31
|
-
|
|
32
|
-
// Sentinel components (will be required when available)
|
|
33
|
-
let FileWatcher, CodeScanner, DriftDetector, HealthChecker, AlertManager, SecretScanner;
|
|
34
|
-
|
|
35
|
-
class SentinelCore extends EventEmitter {
|
|
36
|
-
constructor(options = {}) {
|
|
37
|
-
super();
|
|
38
|
-
|
|
39
|
-
this.config = {
|
|
40
|
-
rootPath: options.rootPath || process.cwd(),
|
|
41
|
-
watchPatterns: options.watchPatterns || ['**/*.js', '**/*.ts', '**/*.json'],
|
|
42
|
-
ignorePatterns: options.ignorePatterns || ['node_modules/**', '.git/**', 'dist/**', '.orion/snapshots/**'],
|
|
43
|
-
scanIntervalHours: options.scanIntervalHours || 6,
|
|
44
|
-
dailyReportHour: options.dailyReportHour || 8, // 8 AM
|
|
45
|
-
weeklyReportDay: options.weeklyReportDay || 1, // Monday
|
|
46
|
-
enableRealtime: options.enableRealtime !== false,
|
|
47
|
-
enableScheduled: options.enableScheduled !== false,
|
|
48
|
-
alertThreshold: options.alertThreshold || 'warning', // 'info', 'warning', 'error', 'critical'
|
|
49
|
-
...options
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
this.state = {
|
|
53
|
-
isRunning: false,
|
|
54
|
-
lastFullScan: null,
|
|
55
|
-
lastHealthCheck: null,
|
|
56
|
-
lastDriftCheck: null,
|
|
57
|
-
filesWatched: 0,
|
|
58
|
-
issuesFound: [],
|
|
59
|
-
metrics: {
|
|
60
|
-
totalScans: 0,
|
|
61
|
-
issuesDetected: 0,
|
|
62
|
-
issuesResolved: 0,
|
|
63
|
-
averageScanTime: 0
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
this.components = {
|
|
68
|
-
fileWatcher: null,
|
|
69
|
-
codeScanner: null,
|
|
70
|
-
driftDetector: null,
|
|
71
|
-
healthChecker: null,
|
|
72
|
-
alertManager: null
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
this.scheduledJobs = [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Initialize all Sentinel components
|
|
80
|
-
*/
|
|
81
|
-
async initialize() {
|
|
82
|
-
console.log('[SENTINEL] Initializing Sentinel Core...');
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
// Load components
|
|
86
|
-
FileWatcher = require('./file-watcher.js');
|
|
87
|
-
CodeScanner = require('./code-scanner.js');
|
|
88
|
-
DriftDetector = require('./drift-detector.js');
|
|
89
|
-
HealthChecker = require('./health-checker.js');
|
|
90
|
-
AlertManager = require('./alert-manager.js');
|
|
91
|
-
SecretScanner = require('./secret-scanner.js');
|
|
92
|
-
|
|
93
|
-
// Initialize components
|
|
94
|
-
this.components.fileWatcher = new FileWatcher(this.config);
|
|
95
|
-
this.components.codeScanner = new CodeScanner(this.config);
|
|
96
|
-
this.components.driftDetector = new DriftDetector(this.config);
|
|
97
|
-
this.components.healthChecker = new HealthChecker(this.config);
|
|
98
|
-
this.components.alertManager = new AlertManager(this.config);
|
|
99
|
-
this.components.secretScanner = new SecretScanner(this.config);
|
|
100
|
-
|
|
101
|
-
// Wire up event handlers
|
|
102
|
-
this._setupEventHandlers();
|
|
103
|
-
|
|
104
|
-
console.log('[SENTINEL] All components initialized');
|
|
105
|
-
return { success: true };
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error('[SENTINEL] Initialization failed:', error.message);
|
|
108
|
-
return { success: false, error: error.message };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Start Sentinel monitoring
|
|
114
|
-
*/
|
|
115
|
-
async start() {
|
|
116
|
-
if (this.state.isRunning) {
|
|
117
|
-
console.log('[SENTINEL] Already running');
|
|
118
|
-
return { success: true, message: 'Already running' };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
console.log('[SENTINEL] Starting Sentinel monitoring...');
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
// Start file watcher for real-time monitoring
|
|
125
|
-
if (this.config.enableRealtime && this.components.fileWatcher) {
|
|
126
|
-
await this.components.fileWatcher.start();
|
|
127
|
-
this.state.filesWatched = this.components.fileWatcher.getWatchedCount();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Set up scheduled jobs
|
|
131
|
-
if (this.config.enableScheduled) {
|
|
132
|
-
this._setupScheduledJobs();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Run initial health check
|
|
136
|
-
await this.runHealthCheck();
|
|
137
|
-
|
|
138
|
-
// Run initial scan
|
|
139
|
-
const initialScan = await this.runFullScan();
|
|
140
|
-
|
|
141
|
-
this.state.isRunning = true;
|
|
142
|
-
this.emit('started', { timestamp: new Date().toISOString() });
|
|
143
|
-
|
|
144
|
-
console.log('[SENTINEL] Monitoring active');
|
|
145
|
-
console.log(`[SENTINEL] Watching ${this.state.filesWatched} files`);
|
|
146
|
-
console.log(`[SENTINEL] Initial scan found ${initialScan.issues?.length || 0} issues`);
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
success: true,
|
|
150
|
-
filesWatched: this.state.filesWatched,
|
|
151
|
-
initialIssues: initialScan.issues?.length || 0
|
|
152
|
-
};
|
|
153
|
-
} catch (error) {
|
|
154
|
-
console.error('[SENTINEL] Start failed:', error.message);
|
|
155
|
-
return { success: false, error: error.message };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Stop Sentinel monitoring
|
|
161
|
-
*/
|
|
162
|
-
async stop() {
|
|
163
|
-
if (!this.state.isRunning) {
|
|
164
|
-
return { success: true, message: 'Not running' };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
console.log('[SENTINEL] Stopping Sentinel monitoring...');
|
|
168
|
-
|
|
169
|
-
// Stop file watcher
|
|
170
|
-
if (this.components.fileWatcher) {
|
|
171
|
-
await this.components.fileWatcher.stop();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Clear scheduled jobs
|
|
175
|
-
this.scheduledJobs.forEach(job => clearInterval(job));
|
|
176
|
-
this.scheduledJobs = [];
|
|
177
|
-
|
|
178
|
-
this.state.isRunning = false;
|
|
179
|
-
this.emit('stopped', { timestamp: new Date().toISOString() });
|
|
180
|
-
|
|
181
|
-
console.log('[SENTINEL] Monitoring stopped');
|
|
182
|
-
return { success: true };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Run a full codebase scan
|
|
187
|
-
*/
|
|
188
|
-
async runFullScan(options = {}) {
|
|
189
|
-
const startTime = Date.now();
|
|
190
|
-
console.log('[SENTINEL] Starting full codebase scan...');
|
|
191
|
-
|
|
192
|
-
const scanResult = {
|
|
193
|
-
timestamp: new Date().toISOString(),
|
|
194
|
-
duration: 0,
|
|
195
|
-
filesScanned: 0,
|
|
196
|
-
issues: [],
|
|
197
|
-
summary: {}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
if (!this.components.codeScanner) {
|
|
202
|
-
throw new Error('Code scanner not initialized');
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Run code scanner
|
|
206
|
-
const codeResults = await this.components.codeScanner.scanAll();
|
|
207
|
-
scanResult.filesScanned = codeResults.filesScanned || 0;
|
|
208
|
-
scanResult.issues.push(...(codeResults.issues || []));
|
|
209
|
-
|
|
210
|
-
if (this.components.secretScanner) {
|
|
211
|
-
const secretResults = await this.components.secretScanner.scanAll();
|
|
212
|
-
scanResult.filesScanned += secretResults.filesScanned || 0;
|
|
213
|
-
scanResult.issues.push(...(secretResults.issues || []));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Run drift detection
|
|
217
|
-
if (this.components.driftDetector) {
|
|
218
|
-
const driftResults = await this.components.driftDetector.detectAll();
|
|
219
|
-
scanResult.issues.push(...(driftResults.issues || []));
|
|
220
|
-
this.state.lastDriftCheck = new Date().toISOString();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Calculate duration and update metrics
|
|
224
|
-
scanResult.duration = Date.now() - startTime;
|
|
225
|
-
this.state.lastFullScan = scanResult.timestamp;
|
|
226
|
-
this.state.metrics.totalScans++;
|
|
227
|
-
this.state.metrics.issuesDetected += scanResult.issues.length;
|
|
228
|
-
|
|
229
|
-
// Update running average
|
|
230
|
-
this.state.metrics.averageScanTime =
|
|
231
|
-
(this.state.metrics.averageScanTime * (this.state.metrics.totalScans - 1) + scanResult.duration)
|
|
232
|
-
/ this.state.metrics.totalScans;
|
|
233
|
-
|
|
234
|
-
// Generate summary
|
|
235
|
-
scanResult.summary = this._generateScanSummary(scanResult.issues);
|
|
236
|
-
|
|
237
|
-
// Store issues
|
|
238
|
-
this.state.issuesFound = scanResult.issues;
|
|
239
|
-
|
|
240
|
-
// Send alerts for critical issues
|
|
241
|
-
await this._processIssues(scanResult.issues);
|
|
242
|
-
|
|
243
|
-
console.log(`[SENTINEL] Scan complete: ${scanResult.filesScanned} files, ${scanResult.issues.length} issues, ${scanResult.duration}ms`);
|
|
244
|
-
this.emit('scanComplete', scanResult);
|
|
245
|
-
|
|
246
|
-
return scanResult;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error('[SENTINEL] Full scan failed:', error.message);
|
|
249
|
-
scanResult.error = error.message;
|
|
250
|
-
return scanResult;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Run a quick scan on specific files
|
|
256
|
-
*/
|
|
257
|
-
async runQuickScan(filePaths) {
|
|
258
|
-
if (!this.components.codeScanner) {
|
|
259
|
-
return { success: false, error: 'Code scanner not initialized' };
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
console.log(`[SENTINEL] Quick scan on ${filePaths.length} files...`);
|
|
263
|
-
const results = await this.components.codeScanner.scanFiles(filePaths);
|
|
264
|
-
|
|
265
|
-
// Process any issues found
|
|
266
|
-
if (results.issues && results.issues.length > 0) {
|
|
267
|
-
await this._processIssues(results.issues);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return results;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Run health check on all Orion systems
|
|
275
|
-
*/
|
|
276
|
-
async runHealthCheck() {
|
|
277
|
-
console.log('[SENTINEL] Running health check...');
|
|
278
|
-
|
|
279
|
-
if (!this.components.healthChecker) {
|
|
280
|
-
return { success: false, error: 'Health checker not initialized' };
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const healthResult = await this.components.healthChecker.checkAll();
|
|
284
|
-
this.state.lastHealthCheck = new Date().toISOString();
|
|
285
|
-
|
|
286
|
-
// Alert on unhealthy systems
|
|
287
|
-
if (healthResult.unhealthySystems && healthResult.unhealthySystems.length > 0) {
|
|
288
|
-
await this.components.alertManager?.sendAlert({
|
|
289
|
-
level: 'warning',
|
|
290
|
-
title: 'System Health Issues Detected',
|
|
291
|
-
message: `${healthResult.unhealthySystems.length} systems are unhealthy`,
|
|
292
|
-
details: healthResult.unhealthySystems
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
this.emit('healthCheck', healthResult);
|
|
297
|
-
return healthResult;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Generate daily health digest
|
|
302
|
-
*/
|
|
303
|
-
async generateDailyDigest() {
|
|
304
|
-
console.log('[SENTINEL] Generating daily digest...');
|
|
305
|
-
|
|
306
|
-
const digest = {
|
|
307
|
-
date: new Date().toISOString().split('T')[0],
|
|
308
|
-
timestamp: new Date().toISOString(),
|
|
309
|
-
summary: {
|
|
310
|
-
issuesFound: this.state.issuesFound.length,
|
|
311
|
-
criticalIssues: this.state.issuesFound.filter(i => i.severity === 'critical').length,
|
|
312
|
-
warningIssues: this.state.issuesFound.filter(i => i.severity === 'warning').length,
|
|
313
|
-
filesWatched: this.state.filesWatched,
|
|
314
|
-
lastScan: this.state.lastFullScan,
|
|
315
|
-
lastHealthCheck: this.state.lastHealthCheck
|
|
316
|
-
},
|
|
317
|
-
topIssues: this.state.issuesFound
|
|
318
|
-
.sort((a, b) => this._severityScore(b.severity) - this._severityScore(a.severity))
|
|
319
|
-
.slice(0, 10),
|
|
320
|
-
metrics: this.state.metrics,
|
|
321
|
-
recommendations: this._generateRecommendations()
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
// Send digest via alert manager
|
|
325
|
-
if (this.components.alertManager) {
|
|
326
|
-
await this.components.alertManager.sendDigest(digest);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
this.emit('dailyDigest', digest);
|
|
330
|
-
return digest;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Generate weekly report
|
|
335
|
-
*/
|
|
336
|
-
async generateWeeklyReport() {
|
|
337
|
-
console.log('[SENTINEL] Generating weekly report...');
|
|
338
|
-
|
|
339
|
-
const report = {
|
|
340
|
-
weekOf: new Date().toISOString().split('T')[0],
|
|
341
|
-
timestamp: new Date().toISOString(),
|
|
342
|
-
summary: this.state.metrics,
|
|
343
|
-
issuesTrend: this._calculateTrend(),
|
|
344
|
-
codeQuality: await this._assessCodeQuality(),
|
|
345
|
-
technicalDebt: await this._assessTechnicalDebt(),
|
|
346
|
-
recommendations: this._generateRecommendations(),
|
|
347
|
-
fullIssueList: this.state.issuesFound
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
// Send report via alert manager
|
|
351
|
-
if (this.components.alertManager) {
|
|
352
|
-
await this.components.alertManager.sendWeeklyReport(report);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
this.emit('weeklyReport', report);
|
|
356
|
-
return report;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Get current Sentinel status
|
|
361
|
-
*/
|
|
362
|
-
getStatus() {
|
|
363
|
-
return {
|
|
364
|
-
isRunning: this.state.isRunning,
|
|
365
|
-
config: this.config,
|
|
366
|
-
state: {
|
|
367
|
-
...this.state,
|
|
368
|
-
componentsLoaded: {
|
|
369
|
-
fileWatcher: !!this.components.fileWatcher,
|
|
370
|
-
codeScanner: !!this.components.codeScanner,
|
|
371
|
-
driftDetector: !!this.components.driftDetector,
|
|
372
|
-
healthChecker: !!this.components.healthChecker,
|
|
373
|
-
alertManager: !!this.components.alertManager
|
|
374
|
-
}
|
|
375
|
-
},
|
|
376
|
-
uptime: this.state.isRunning ? Date.now() - new Date(this.state.lastFullScan).getTime() : 0
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Get all current issues
|
|
382
|
-
*/
|
|
383
|
-
getIssues(filter = {}) {
|
|
384
|
-
let issues = [...this.state.issuesFound];
|
|
385
|
-
|
|
386
|
-
if (filter.severity) {
|
|
387
|
-
issues = issues.filter(i => i.severity === filter.severity);
|
|
388
|
-
}
|
|
389
|
-
if (filter.category) {
|
|
390
|
-
issues = issues.filter(i => i.category === filter.category);
|
|
391
|
-
}
|
|
392
|
-
if (filter.file) {
|
|
393
|
-
issues = issues.filter(i => i.file && i.file.includes(filter.file));
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return issues;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Private methods
|
|
400
|
-
|
|
401
|
-
_setupEventHandlers() {
|
|
402
|
-
// File watcher events
|
|
403
|
-
if (this.components.fileWatcher) {
|
|
404
|
-
this.components.fileWatcher.on('change', async (event) => {
|
|
405
|
-
console.log(`[SENTINEL] File changed: ${event.filePath}`);
|
|
406
|
-
await this.runQuickScan([event.filePath]);
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
this.components.fileWatcher.on('error', (error) => {
|
|
410
|
-
console.error('[SENTINEL] File watcher error:', error);
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Code scanner events
|
|
415
|
-
if (this.components.codeScanner) {
|
|
416
|
-
this.components.codeScanner.on('issueFound', async (issue) => {
|
|
417
|
-
await this._processIssues([issue]);
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
_setupScheduledJobs() {
|
|
423
|
-
// Full scan every N hours
|
|
424
|
-
const scanInterval = this.config.scanIntervalHours * 60 * 60 * 1000;
|
|
425
|
-
this.scheduledJobs.push(
|
|
426
|
-
setInterval(() => this.runFullScan(), scanInterval)
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
// Health check every hour
|
|
430
|
-
this.scheduledJobs.push(
|
|
431
|
-
setInterval(() => this.runHealthCheck(), 60 * 60 * 1000)
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
// Daily digest (check every hour if it's time)
|
|
435
|
-
this.scheduledJobs.push(
|
|
436
|
-
setInterval(() => {
|
|
437
|
-
const now = new Date();
|
|
438
|
-
if (now.getHours() === this.config.dailyReportHour && now.getMinutes() < 5) {
|
|
439
|
-
this.generateDailyDigest();
|
|
440
|
-
}
|
|
441
|
-
}, 60 * 60 * 1000)
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
// Weekly report (check daily if it's the right day)
|
|
445
|
-
this.scheduledJobs.push(
|
|
446
|
-
setInterval(() => {
|
|
447
|
-
const now = new Date();
|
|
448
|
-
if (now.getDay() === this.config.weeklyReportDay &&
|
|
449
|
-
now.getHours() === this.config.dailyReportHour &&
|
|
450
|
-
now.getMinutes() < 5) {
|
|
451
|
-
this.generateWeeklyReport();
|
|
452
|
-
}
|
|
453
|
-
}, 60 * 60 * 1000)
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
console.log(`[SENTINEL] Scheduled jobs: scan every ${this.config.scanIntervalHours}h, daily digest at ${this.config.dailyReportHour}:00`);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
async _processIssues(issues) {
|
|
460
|
-
for (const issue of issues) {
|
|
461
|
-
// Check if issue meets alert threshold
|
|
462
|
-
if (this._meetsThreshold(issue.severity)) {
|
|
463
|
-
if (this.components.alertManager) {
|
|
464
|
-
await this.components.alertManager.sendAlert({
|
|
465
|
-
level: issue.severity,
|
|
466
|
-
title: `[${issue.category}] ${issue.title || issue.message}`,
|
|
467
|
-
message: issue.message,
|
|
468
|
-
file: issue.file,
|
|
469
|
-
line: issue.line,
|
|
470
|
-
details: issue
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
_meetsThreshold(severity) {
|
|
478
|
-
const levels = ['info', 'warning', 'error', 'critical'];
|
|
479
|
-
const severityIndex = levels.indexOf(severity);
|
|
480
|
-
const thresholdIndex = levels.indexOf(this.config.alertThreshold);
|
|
481
|
-
return severityIndex >= thresholdIndex;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
_severityScore(severity) {
|
|
485
|
-
const scores = { info: 1, warning: 2, error: 3, critical: 4 };
|
|
486
|
-
return scores[severity] || 0;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
_generateScanSummary(issues) {
|
|
490
|
-
return {
|
|
491
|
-
total: issues.length,
|
|
492
|
-
bySeverity: {
|
|
493
|
-
critical: issues.filter(i => i.severity === 'critical').length,
|
|
494
|
-
error: issues.filter(i => i.severity === 'error').length,
|
|
495
|
-
warning: issues.filter(i => i.severity === 'warning').length,
|
|
496
|
-
info: issues.filter(i => i.severity === 'info').length
|
|
497
|
-
},
|
|
498
|
-
byCategory: issues.reduce((acc, i) => {
|
|
499
|
-
acc[i.category] = (acc[i.category] || 0) + 1;
|
|
500
|
-
return acc;
|
|
501
|
-
}, {})
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
_generateRecommendations() {
|
|
506
|
-
const recommendations = [];
|
|
507
|
-
const issues = this.state.issuesFound;
|
|
508
|
-
|
|
509
|
-
// Check for security issues
|
|
510
|
-
const securityIssues = issues.filter(i => i.category === 'security');
|
|
511
|
-
if (securityIssues.length > 0) {
|
|
512
|
-
recommendations.push({
|
|
513
|
-
priority: 'high',
|
|
514
|
-
category: 'security',
|
|
515
|
-
message: `Address ${securityIssues.length} security issues before deploying`,
|
|
516
|
-
issues: securityIssues.slice(0, 5)
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// Check for quality issues
|
|
521
|
-
const qualityIssues = issues.filter(i => i.category === 'quality');
|
|
522
|
-
if (qualityIssues.length > 10) {
|
|
523
|
-
recommendations.push({
|
|
524
|
-
priority: 'medium',
|
|
525
|
-
category: 'quality',
|
|
526
|
-
message: `Consider refactoring - ${qualityIssues.length} quality issues detected`,
|
|
527
|
-
issues: qualityIssues.slice(0, 5)
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Check for drift
|
|
532
|
-
const driftIssues = issues.filter(i => i.category === 'drift');
|
|
533
|
-
if (driftIssues.length > 0) {
|
|
534
|
-
recommendations.push({
|
|
535
|
-
priority: 'medium',
|
|
536
|
-
category: 'drift',
|
|
537
|
-
message: `${driftIssues.length} files have drifted from their declared contracts`,
|
|
538
|
-
issues: driftIssues.slice(0, 5)
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
return recommendations;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
_calculateTrend() {
|
|
546
|
-
// Placeholder - would track historical data
|
|
547
|
-
return {
|
|
548
|
-
direction: 'stable',
|
|
549
|
-
change: 0,
|
|
550
|
-
note: 'Trend analysis requires historical data'
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
async _assessCodeQuality() {
|
|
555
|
-
// Placeholder - would calculate code quality score
|
|
556
|
-
return {
|
|
557
|
-
score: 75,
|
|
558
|
-
grade: 'B',
|
|
559
|
-
factors: {
|
|
560
|
-
complexity: 70,
|
|
561
|
-
maintainability: 80,
|
|
562
|
-
documentation: 65,
|
|
563
|
-
testCoverage: 40
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
async _assessTechnicalDebt() {
|
|
569
|
-
// Placeholder - would calculate technical debt
|
|
570
|
-
return {
|
|
571
|
-
estimatedHours: 40,
|
|
572
|
-
categories: {
|
|
573
|
-
refactoring: 20,
|
|
574
|
-
documentation: 10,
|
|
575
|
-
testing: 10
|
|
576
|
-
},
|
|
577
|
-
priorityItems: []
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// Factory function for creating Sentinel instance
|
|
583
|
-
function createSentinel(options = {}) {
|
|
584
|
-
return new SentinelCore(options);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Main execution if run directly
|
|
588
|
-
async function main() {
|
|
589
|
-
const sentinel = createSentinel({
|
|
590
|
-
rootPath: path.resolve(__dirname, '..'),
|
|
591
|
-
enableRealtime: true,
|
|
592
|
-
enableScheduled: false // Disable scheduled for testing
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
const initResult = await sentinel.initialize();
|
|
596
|
-
if (!initResult.success) {
|
|
597
|
-
console.error('Failed to initialize Sentinel:', initResult.error);
|
|
598
|
-
process.exit(1);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const startResult = await sentinel.start();
|
|
602
|
-
console.log('Sentinel status:', sentinel.getStatus());
|
|
603
|
-
|
|
604
|
-
// Keep running
|
|
605
|
-
process.on('SIGINT', async () => {
|
|
606
|
-
console.log('\nShutting down Sentinel...');
|
|
607
|
-
await sentinel.stop();
|
|
608
|
-
process.exit(0);
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (require.main === module) {
|
|
613
|
-
main().catch(console.error);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
module.exports = { SentinelCore, createSentinel };
|