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.
Files changed (66) hide show
  1. package/dist/LICENSE +21 -0
  2. package/dist/README.md +185 -0
  3. package/dist/cli.js +2 -0
  4. package/dist/packages/scanner/ai-confidence-scorer.js +1 -0
  5. package/dist/packages/scanner/alert-manager.js +1 -0
  6. package/dist/packages/scanner/baseline-manager.js +1 -0
  7. package/dist/packages/scanner/code-scanner.js +1 -0
  8. package/dist/packages/scanner/codebase-passport.js +1 -0
  9. package/dist/packages/scanner/copy-paste-detector.js +1 -0
  10. package/dist/packages/scanner/dependency-graph.js +1 -0
  11. package/dist/packages/scanner/drift-detector.js +1 -0
  12. package/dist/packages/scanner/executive-report.js +1 -0
  13. package/dist/packages/scanner/file-collector.js +1 -0
  14. package/dist/packages/scanner/file-watcher.js +1 -0
  15. package/dist/packages/scanner/ghost-code-detector.js +1 -0
  16. package/dist/packages/scanner/hallucination-detector.js +1 -0
  17. package/dist/packages/scanner/health-checker.js +1 -0
  18. package/dist/packages/scanner/html-report.js +1 -0
  19. package/dist/packages/scanner/index.js +1 -0
  20. package/dist/packages/scanner/license-manager.js +1 -0
  21. package/dist/packages/scanner/master-health-checker.js +1 -0
  22. package/dist/packages/scanner/monitor-notifier.js +1 -0
  23. package/dist/packages/scanner/monitor-service.js +1 -0
  24. package/dist/packages/scanner/monitor-store.js +1 -0
  25. package/dist/packages/scanner/pre-commit-gate.js +1 -0
  26. package/dist/packages/scanner/scan-diff.js +1 -0
  27. package/dist/packages/scanner/scan-runner.js +1 -0
  28. package/dist/packages/scanner/secret-scanner.js +1 -0
  29. package/dist/packages/scanner/sentinel-core.js +1 -0
  30. package/dist/packages/scanner/sentinel-knowledge.js +1 -0
  31. package/dist/packages/scanner/tech-debt-analyzer.js +1 -0
  32. package/dist/packages/scanner/tech-debt-cost.js +1 -0
  33. package/dist/packages/scanner/widget-generator.js +1 -0
  34. package/package.json +12 -7
  35. package/cli.js +0 -2627
  36. package/packages/scanner/ai-confidence-scorer.js +0 -260
  37. package/packages/scanner/alert-manager.js +0 -398
  38. package/packages/scanner/baseline-manager.js +0 -109
  39. package/packages/scanner/code-scanner.js +0 -758
  40. package/packages/scanner/codebase-passport.js +0 -299
  41. package/packages/scanner/copy-paste-detector.js +0 -276
  42. package/packages/scanner/dependency-graph.js +0 -541
  43. package/packages/scanner/drift-detector.js +0 -423
  44. package/packages/scanner/executive-report.js +0 -774
  45. package/packages/scanner/file-collector.js +0 -64
  46. package/packages/scanner/file-watcher.js +0 -323
  47. package/packages/scanner/ghost-code-detector.js +0 -301
  48. package/packages/scanner/hallucination-detector.js +0 -822
  49. package/packages/scanner/health-checker.js +0 -586
  50. package/packages/scanner/html-report.js +0 -634
  51. package/packages/scanner/index.js +0 -100
  52. package/packages/scanner/license-manager.js +0 -331
  53. package/packages/scanner/master-health-checker.js +0 -513
  54. package/packages/scanner/monitor-notifier.js +0 -64
  55. package/packages/scanner/monitor-service.js +0 -103
  56. package/packages/scanner/monitor-store.js +0 -117
  57. package/packages/scanner/pre-commit-gate.js +0 -216
  58. package/packages/scanner/scan-diff.js +0 -50
  59. package/packages/scanner/scan-runner.js +0 -172
  60. package/packages/scanner/secret-scanner.js +0 -612
  61. package/packages/scanner/secret-scanner.test.js +0 -103
  62. package/packages/scanner/sentinel-core.js +0 -616
  63. package/packages/scanner/sentinel-knowledge.js +0 -322
  64. package/packages/scanner/tech-debt-analyzer.js +0 -583
  65. package/packages/scanner/tech-debt-cost.js +0 -194
  66. 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 };