@gotza02/sequential-thinking 10000.0.8 → 10000.1.0
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 +249 -39
- package/dist/dashboard/server.d.ts +79 -2
- package/dist/dashboard/server.js +466 -61
- package/dist/index.js +1 -4
- package/dist/tools/sports/core/alert-manager.d.ts +145 -0
- package/dist/tools/sports/core/alert-manager.js +380 -0
- package/dist/tools/sports/core/cache.d.ts +19 -8
- package/dist/tools/sports/core/cache.js +95 -38
- package/dist/tools/sports/core/circuit-breaker.d.ts +40 -0
- package/dist/tools/sports/core/circuit-breaker.js +99 -0
- package/dist/tools/sports/core/constants.d.ts +63 -4
- package/dist/tools/sports/core/constants.js +86 -11
- package/dist/tools/sports/core/data-quality.d.ts +80 -0
- package/dist/tools/sports/core/data-quality.js +460 -0
- package/dist/tools/sports/core/historical-analyzer.d.ts +108 -0
- package/dist/tools/sports/core/historical-analyzer.js +461 -0
- package/dist/tools/sports/core/index.d.ts +13 -0
- package/dist/tools/sports/core/index.js +16 -0
- package/dist/tools/sports/core/ml-prediction.d.ts +134 -0
- package/dist/tools/sports/core/ml-prediction.js +402 -0
- package/dist/tools/sports/core/realtime-manager.d.ts +102 -0
- package/dist/tools/sports/core/realtime-manager.js +331 -0
- package/dist/tools/sports/core/retry.d.ts +29 -0
- package/dist/tools/sports/core/retry.js +77 -0
- package/dist/tools/sports/core/types.d.ts +40 -1
- package/package.json +1 -1
package/dist/dashboard/server.js
CHANGED
|
@@ -1,67 +1,472 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* DASHBOARD SERVER
|
|
3
|
+
* Real-time monitoring dashboard for the football analysis system
|
|
4
|
+
*/
|
|
5
|
+
import { createServer } from 'http';
|
|
6
|
+
import { dirname } from 'path';
|
|
4
7
|
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
8
|
+
import { logger } from '../utils.js';
|
|
9
|
+
import { getGlobalCache } from '../tools/sports/core/cache.js';
|
|
10
|
+
import { getAllCircuitBreakerStatus } from '../tools/sports/core/circuit-breaker.js';
|
|
11
|
+
import { getRealtimeManager } from '../tools/sports/core/realtime-manager.js';
|
|
12
|
+
import { getAlertManager } from '../tools/sports/core/alert-manager.js';
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
export class DashboardServer {
|
|
15
|
+
server = null;
|
|
16
|
+
port;
|
|
17
|
+
updateInterval = null;
|
|
18
|
+
constructor(port = 8080) {
|
|
19
|
+
this.port = port;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start the dashboard server
|
|
23
|
+
*/
|
|
24
|
+
start() {
|
|
25
|
+
if (this.server)
|
|
26
|
+
return;
|
|
27
|
+
this.server = createServer((req, res) => {
|
|
28
|
+
this.handleRequest(req, res);
|
|
29
|
+
});
|
|
30
|
+
this.server.listen(this.port, () => {
|
|
31
|
+
logger.info(`[Dashboard] Server running on http://localhost:${this.port}`);
|
|
32
|
+
});
|
|
33
|
+
// Start stats update interval
|
|
34
|
+
this.updateInterval = setInterval(() => {
|
|
35
|
+
this.broadcastStats();
|
|
36
|
+
}, 5000);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Stop the dashboard server
|
|
40
|
+
*/
|
|
41
|
+
stop() {
|
|
42
|
+
if (this.updateInterval) {
|
|
43
|
+
clearInterval(this.updateInterval);
|
|
44
|
+
this.updateInterval = null;
|
|
45
|
+
}
|
|
46
|
+
if (this.server) {
|
|
47
|
+
this.server.close();
|
|
48
|
+
this.server = null;
|
|
49
|
+
logger.info('[Dashboard] Server stopped');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Handle HTTP requests
|
|
54
|
+
*/
|
|
55
|
+
handleRequest(req, res) {
|
|
56
|
+
const url = new URL(req.url, `http://localhost:${this.port}`);
|
|
57
|
+
// Enable CORS
|
|
58
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
59
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
60
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
61
|
+
if (req.method === 'OPTIONS') {
|
|
62
|
+
res.writeHead(200);
|
|
63
|
+
res.end();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Route handling
|
|
67
|
+
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
68
|
+
this.serveDashboard(res);
|
|
69
|
+
}
|
|
70
|
+
else if (url.pathname === '/api/stats') {
|
|
71
|
+
this.serveStats(res);
|
|
72
|
+
}
|
|
73
|
+
else if (url.pathname === '/api/cache/clear') {
|
|
74
|
+
this.clearCache(res);
|
|
75
|
+
}
|
|
76
|
+
else if (url.pathname === '/api/circuit-breakers/reset') {
|
|
77
|
+
this.resetCircuitBreakers(res);
|
|
78
|
+
}
|
|
79
|
+
else if (url.pathname === '/api/alerts/rules') {
|
|
80
|
+
this.serveAlertRules(res);
|
|
81
|
+
}
|
|
82
|
+
else if (url.pathname === '/api/alerts/history') {
|
|
83
|
+
this.serveAlertHistory(res);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
res.writeHead(404);
|
|
87
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Serve the dashboard HTML
|
|
92
|
+
*/
|
|
93
|
+
serveDashboard(res) {
|
|
94
|
+
const html = `<!DOCTYPE html>
|
|
95
|
+
<html lang="en">
|
|
96
|
+
<head>
|
|
97
|
+
<meta charset="UTF-8">
|
|
98
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
99
|
+
<title>Football Analysis Dashboard</title>
|
|
100
|
+
<style>
|
|
101
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
102
|
+
body {
|
|
103
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
104
|
+
background: #0f172a;
|
|
105
|
+
color: #e2e8f0;
|
|
106
|
+
padding: 20px;
|
|
107
|
+
}
|
|
108
|
+
.header {
|
|
109
|
+
text-align: center;
|
|
110
|
+
padding: 20px;
|
|
111
|
+
margin-bottom: 30px;
|
|
112
|
+
}
|
|
113
|
+
.header h1 {
|
|
114
|
+
font-size: 2.5em;
|
|
115
|
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
|
116
|
+
-webkit-background-clip: text;
|
|
117
|
+
-webkit-text-fill-color: transparent;
|
|
118
|
+
}
|
|
119
|
+
.grid {
|
|
120
|
+
display: grid;
|
|
121
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
122
|
+
gap: 20px;
|
|
123
|
+
max-width: 1400px;
|
|
124
|
+
margin: 0 auto;
|
|
125
|
+
}
|
|
126
|
+
.card {
|
|
127
|
+
background: #1e293b;
|
|
128
|
+
border-radius: 12px;
|
|
129
|
+
padding: 20px;
|
|
130
|
+
border: 1px solid #334155;
|
|
131
|
+
}
|
|
132
|
+
.card h2 {
|
|
133
|
+
font-size: 1.2em;
|
|
134
|
+
margin-bottom: 15px;
|
|
135
|
+
color: #94a3b8;
|
|
136
|
+
text-transform: uppercase;
|
|
137
|
+
letter-spacing: 1px;
|
|
138
|
+
}
|
|
139
|
+
.stat {
|
|
140
|
+
display: flex;
|
|
141
|
+
justify-content: space-between;
|
|
142
|
+
padding: 10px 0;
|
|
143
|
+
border-bottom: 1px solid #334155;
|
|
144
|
+
}
|
|
145
|
+
.stat:last-child { border-bottom: none; }
|
|
146
|
+
.stat-value {
|
|
147
|
+
font-weight: bold;
|
|
148
|
+
color: #3b82f6;
|
|
149
|
+
}
|
|
150
|
+
.status {
|
|
151
|
+
display: inline-block;
|
|
152
|
+
padding: 4px 12px;
|
|
153
|
+
border-radius: 20px;
|
|
154
|
+
font-size: 0.85em;
|
|
155
|
+
font-weight: 500;
|
|
156
|
+
}
|
|
157
|
+
.status-online { background: #22c55e; color: #064e3b; }
|
|
158
|
+
.status-offline { background: #ef4444; color: #7f1d1d; }
|
|
159
|
+
.status-warning { background: #f59e0b; color: #78350f; }
|
|
160
|
+
.cb-closed { color: #22c55e; }
|
|
161
|
+
.cb-open { color: #ef4444; }
|
|
162
|
+
.cb-half { color: #f59e0b; }
|
|
163
|
+
button {
|
|
164
|
+
background: #3b82f6;
|
|
165
|
+
color: white;
|
|
166
|
+
border: none;
|
|
167
|
+
padding: 10px 20px;
|
|
168
|
+
border-radius: 6px;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
font-size: 0.9em;
|
|
171
|
+
margin-top: 10px;
|
|
172
|
+
}
|
|
173
|
+
button:hover { background: #2563eb; }
|
|
174
|
+
.refresh-indicator {
|
|
175
|
+
position: fixed;
|
|
176
|
+
top: 20px;
|
|
177
|
+
right: 20px;
|
|
178
|
+
width: 10px;
|
|
179
|
+
height: 10px;
|
|
180
|
+
background: #22c55e;
|
|
181
|
+
border-radius: 50%;
|
|
182
|
+
animation: pulse 2s infinite;
|
|
183
|
+
}
|
|
184
|
+
@keyframes pulse {
|
|
185
|
+
0%, 100% { opacity: 1; }
|
|
186
|
+
50% { opacity: 0.5; }
|
|
187
|
+
}
|
|
188
|
+
.alerts-list {
|
|
189
|
+
max-height: 200px;
|
|
190
|
+
overflow-y: auto;
|
|
191
|
+
}
|
|
192
|
+
.alert-item {
|
|
193
|
+
padding: 8px;
|
|
194
|
+
margin: 5px 0;
|
|
195
|
+
background: #334155;
|
|
196
|
+
border-radius: 6px;
|
|
197
|
+
font-size: 0.85em;
|
|
198
|
+
}
|
|
199
|
+
.alert-critical { border-left: 3px solid #ef4444; }
|
|
200
|
+
.alert-warning { border-left: 3px solid #f59e0b; }
|
|
201
|
+
.alert-info { border-left: 3px solid #3b82f6; }
|
|
202
|
+
</style>
|
|
203
|
+
</head>
|
|
204
|
+
<body>
|
|
205
|
+
<div class="refresh-indicator"></div>
|
|
206
|
+
|
|
207
|
+
<div class="header">
|
|
208
|
+
<h1>⚽ Football Analysis Dashboard</h1>
|
|
209
|
+
<p>Real-time System Monitoring</p>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div class="grid">
|
|
213
|
+
<div class="card">
|
|
214
|
+
<h2>📊 System Status</h2>
|
|
215
|
+
<div class="stat">
|
|
216
|
+
<span>Realtime Manager</span>
|
|
217
|
+
<span class="status status-online" id="realtime-status">Online</span>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="stat">
|
|
220
|
+
<span>Alert Manager</span>
|
|
221
|
+
<span class="status status-online" id="alert-status">Online</span>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="stat">
|
|
224
|
+
<span>Cache Service</span>
|
|
225
|
+
<span class="status status-online" id="cache-status">Online</span>
|
|
226
|
+
</div>
|
|
227
|
+
<div class="stat">
|
|
228
|
+
<span>Last Update</span>
|
|
229
|
+
<span id="last-update">-</span>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<div class="card">
|
|
234
|
+
<h2>💾 Cache Statistics</h2>
|
|
235
|
+
<div class="stat">
|
|
236
|
+
<span>Cache Size</span>
|
|
237
|
+
<span class="stat-value" id="cache-size">0 / 2000</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div class="stat">
|
|
240
|
+
<span>Cache Hits</span>
|
|
241
|
+
<span class="stat-value" id="cache-hits">0</span>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="stat">
|
|
244
|
+
<span>Hit Rate</span>
|
|
245
|
+
<span class="stat-value" id="hit-rate">0%</span>
|
|
246
|
+
</div>
|
|
247
|
+
<button onclick="clearCache()">Clear Cache</button>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<div class="card">
|
|
251
|
+
<h2>🔒 Circuit Breakers</h2>
|
|
252
|
+
<div id="circuit-breakers">
|
|
253
|
+
<div class="stat">
|
|
254
|
+
<span>No circuit breakers active</span>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
<button onclick="resetCircuitBreakers()">Reset All</button>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<div class="card">
|
|
261
|
+
<h2>⚡ Realtime Data</h2>
|
|
262
|
+
<div class="stat">
|
|
263
|
+
<span>Live Matches</span>
|
|
264
|
+
<span class="stat-value" id="live-matches">0</span>
|
|
265
|
+
</div>
|
|
266
|
+
<div class="stat">
|
|
267
|
+
<span>Active Subscribers</span>
|
|
268
|
+
<span class="stat-value" id="subscribers">0</span>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="stat">
|
|
271
|
+
<span>Events/Minute</span>
|
|
272
|
+
<span class="stat-value" id="events-rate">0</span>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div class="card">
|
|
277
|
+
<h2>🚨 Alert System</h2>
|
|
278
|
+
<div class="stat">
|
|
279
|
+
<span>Total Rules</span>
|
|
280
|
+
<span class="stat-value" id="total-rules">0</span>
|
|
281
|
+
</div>
|
|
282
|
+
<div class="stat">
|
|
283
|
+
<span>Enabled Rules</span>
|
|
284
|
+
<span class="stat-value" id="enabled-rules">0</span>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="stat">
|
|
287
|
+
<span>Total Alerts</span>
|
|
288
|
+
<span class="stat-value" id="total-alerts">0</span>
|
|
289
|
+
</div>
|
|
290
|
+
<button onclick="viewAlertRules()">View Rules</button>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div class="card">
|
|
294
|
+
<h2>🔔 Recent Alerts</h2>
|
|
295
|
+
<div class="alerts-list" id="alerts-list">
|
|
296
|
+
<div class="alert-item">No recent alerts</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<script>
|
|
302
|
+
let stats = {};
|
|
303
|
+
|
|
304
|
+
async function fetchStats() {
|
|
305
|
+
try {
|
|
306
|
+
const response = await fetch('/api/stats');
|
|
307
|
+
stats = await response.json();
|
|
308
|
+
updateDashboard();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error('Failed to fetch stats:', error);
|
|
45
311
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function updateDashboard() {
|
|
315
|
+
document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
|
|
316
|
+
|
|
317
|
+
// Cache stats
|
|
318
|
+
document.getElementById('cache-size').textContent =
|
|
319
|
+
\`\${stats.cache.size} / \${stats.cache.maxSize}\`;
|
|
320
|
+
document.getElementById('cache-hits').textContent = stats.cache.hits;
|
|
321
|
+
|
|
322
|
+
// Circuit breakers
|
|
323
|
+
const cbContainer = document.getElementById('circuit-breakers');
|
|
324
|
+
if (stats.circuitBreakers.length > 0) {
|
|
325
|
+
cbContainer.innerHTML = stats.circuitBreakers.map(cb => \`
|
|
326
|
+
<div class="stat">
|
|
327
|
+
<span>\${cb.name}</span>
|
|
328
|
+
<span class="cb-\${cb.state}">\${cb.state.toUpperCase()}</span>
|
|
329
|
+
</div>
|
|
330
|
+
\`).join('');
|
|
49
331
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
332
|
+
|
|
333
|
+
// Realtime
|
|
334
|
+
document.getElementById('live-matches').textContent = stats.realtime.liveMatches;
|
|
335
|
+
document.getElementById('subscribers').textContent = stats.realtime.subscribers;
|
|
336
|
+
|
|
337
|
+
// Alerts
|
|
338
|
+
document.getElementById('total-rules').textContent = stats.alerts.totalRules;
|
|
339
|
+
document.getElementById('enabled-rules').textContent = stats.alerts.enabledRules;
|
|
340
|
+
document.getElementById('total-alerts').textContent = stats.alerts.totalAlerts;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function clearCache() {
|
|
344
|
+
try {
|
|
345
|
+
await fetch('/api/cache/clear', { method: 'POST' });
|
|
346
|
+
alert('Cache cleared');
|
|
347
|
+
fetchStats();
|
|
348
|
+
} catch (error) {
|
|
349
|
+
alert('Failed to clear cache');
|
|
56
350
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function resetCircuitBreakers() {
|
|
354
|
+
try {
|
|
355
|
+
await fetch('/api/circuit-breakers/reset', { method: 'POST' });
|
|
356
|
+
alert('Circuit breakers reset');
|
|
357
|
+
fetchStats();
|
|
358
|
+
} catch (error) {
|
|
359
|
+
alert('Failed to reset circuit breakers');
|
|
60
360
|
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function viewAlertRules() {
|
|
364
|
+
window.open('/api/alerts/rules', '_blank');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Fetch stats every 5 seconds
|
|
368
|
+
fetchStats();
|
|
369
|
+
setInterval(fetchStats, 5000);
|
|
370
|
+
</script>
|
|
371
|
+
</body>
|
|
372
|
+
</html>`;
|
|
373
|
+
res.setHeader('Content-Type', 'text/html');
|
|
374
|
+
res.writeHead(200);
|
|
375
|
+
res.end(html);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Serve stats JSON
|
|
379
|
+
*/
|
|
380
|
+
serveStats(res) {
|
|
381
|
+
const stats = this.collectStats();
|
|
382
|
+
res.setHeader('Content-Type', 'application/json');
|
|
383
|
+
res.writeHead(200);
|
|
384
|
+
res.end(JSON.stringify(stats));
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Clear cache
|
|
388
|
+
*/
|
|
389
|
+
clearCache(res) {
|
|
390
|
+
const cache = getGlobalCache();
|
|
391
|
+
cache.clear();
|
|
392
|
+
res.setHeader('Content-Type', 'application/json');
|
|
393
|
+
res.writeHead(200);
|
|
394
|
+
res.end(JSON.stringify({ success: true, message: 'Cache cleared' }));
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Reset circuit breakers
|
|
398
|
+
*/
|
|
399
|
+
resetCircuitBreakers(res) {
|
|
400
|
+
// Circuit breakers auto-reset based on timeout
|
|
401
|
+
// This endpoint just returns success
|
|
402
|
+
res.setHeader('Content-Type', 'application/json');
|
|
403
|
+
res.writeHead(200);
|
|
404
|
+
res.end(JSON.stringify({ success: true, message: 'Circuit breakers will reset automatically' }));
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Serve alert rules
|
|
408
|
+
*/
|
|
409
|
+
serveAlertRules(res) {
|
|
410
|
+
const alertManager = getAlertManager();
|
|
411
|
+
const rules = alertManager.getRules();
|
|
412
|
+
res.setHeader('Content-Type', 'application/json');
|
|
413
|
+
res.writeHead(200);
|
|
414
|
+
res.end(JSON.stringify(rules, null, 2));
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Serve alert history
|
|
418
|
+
*/
|
|
419
|
+
serveAlertHistory(res) {
|
|
420
|
+
const alertManager = getAlertManager();
|
|
421
|
+
const history = alertManager.getAlertHistory(50);
|
|
422
|
+
res.setHeader('Content-Type', 'application/json');
|
|
423
|
+
res.writeHead(200);
|
|
424
|
+
res.end(JSON.stringify(history, null, 2));
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Collect all stats
|
|
428
|
+
*/
|
|
429
|
+
collectStats() {
|
|
430
|
+
const cache = getGlobalCache();
|
|
431
|
+
const cacheStats = cache.getStats();
|
|
432
|
+
const realtimeManager = getRealtimeManager();
|
|
433
|
+
const alertManager = getAlertManager();
|
|
434
|
+
return {
|
|
435
|
+
cache: {
|
|
436
|
+
size: cacheStats.size,
|
|
437
|
+
maxSize: cacheStats.maxSize,
|
|
438
|
+
hits: cacheStats.hits,
|
|
439
|
+
breakdown: cache.getBreakdown(),
|
|
440
|
+
},
|
|
441
|
+
circuitBreakers: getAllCircuitBreakerStatus(),
|
|
442
|
+
realtime: {
|
|
443
|
+
isRunning: true, // Would check actual state
|
|
444
|
+
liveMatches: realtimeManager.getLiveMatches().length,
|
|
445
|
+
subscribers: realtimeManager.listenerCount('event'),
|
|
446
|
+
},
|
|
447
|
+
alerts: alertManager.getStats(),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Broadcast stats to all connected clients (for WebSocket upgrade)
|
|
452
|
+
*/
|
|
453
|
+
broadcastStats() {
|
|
454
|
+
// In a full implementation, this would push to WebSocket clients
|
|
455
|
+
// For now, clients poll via HTTP
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Singleton instance
|
|
459
|
+
let globalDashboardServer = null;
|
|
460
|
+
export function startDashboard(port) {
|
|
461
|
+
if (!globalDashboardServer) {
|
|
462
|
+
globalDashboardServer = new DashboardServer(port);
|
|
463
|
+
globalDashboardServer.start();
|
|
464
|
+
}
|
|
465
|
+
return globalDashboardServer;
|
|
466
|
+
}
|
|
467
|
+
export function stopDashboard() {
|
|
468
|
+
if (globalDashboardServer) {
|
|
469
|
+
globalDashboardServer.stop();
|
|
470
|
+
globalDashboardServer = null;
|
|
471
|
+
}
|
|
67
472
|
}
|
package/dist/index.js
CHANGED
|
@@ -29,10 +29,7 @@ const server = new McpServer({
|
|
|
29
29
|
});
|
|
30
30
|
const thinkingServer = new SequentialThinkingServer(process.env.THOUGHTS_STORAGE_PATH || 'thoughts_history.json', parseInt(process.env.THOUGHT_DELAY_MS || '0', 10));
|
|
31
31
|
// Start Dashboard
|
|
32
|
-
|
|
33
|
-
startDashboard(3001, historyPath).catch(err => {
|
|
34
|
-
console.error("[Dashboard] Failed to start:", err);
|
|
35
|
-
});
|
|
32
|
+
startDashboard(3001);
|
|
36
33
|
const knowledgeGraph = new ProjectKnowledgeGraph();
|
|
37
34
|
const memoryGraph = new KnowledgeGraphManager(process.env.MEMORY_GRAPH_PATH || 'knowledge_graph.json');
|
|
38
35
|
const notesManager = new NotesManager(process.env.NOTES_STORAGE_PATH || 'project_notes.json');
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ALERT MANAGER
|
|
3
|
+
* Real-time alert system with rule engine and notifications
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'events';
|
|
6
|
+
import type { LiveEvent } from './realtime-manager.js';
|
|
7
|
+
export interface AlertRule {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
type: AlertType;
|
|
11
|
+
condition: AlertCondition;
|
|
12
|
+
channels: NotificationChannel[];
|
|
13
|
+
cooldown: number;
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
lastTriggered?: number;
|
|
17
|
+
triggerCount?: number;
|
|
18
|
+
}
|
|
19
|
+
export type AlertType = 'odds_drop' | 'odds_value' | 'goal' | 'red_card' | 'lineup_change' | 'match_start' | 'match_end' | 'custom';
|
|
20
|
+
export type AlertCondition = OddsDropCondition | OddsValueCondition | EventCondition | CompositeCondition;
|
|
21
|
+
export interface OddsDropCondition {
|
|
22
|
+
type: 'odds_drop';
|
|
23
|
+
matchId?: string;
|
|
24
|
+
team?: string;
|
|
25
|
+
threshold: number;
|
|
26
|
+
percentage: number;
|
|
27
|
+
}
|
|
28
|
+
export interface OddsValueCondition {
|
|
29
|
+
type: 'odds_value';
|
|
30
|
+
matchId?: string;
|
|
31
|
+
minValue: number;
|
|
32
|
+
maxOdds?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface EventCondition {
|
|
35
|
+
type: 'event';
|
|
36
|
+
matchId?: string;
|
|
37
|
+
eventTypes: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface CompositeCondition {
|
|
40
|
+
type: 'composite';
|
|
41
|
+
conditions: AlertCondition[];
|
|
42
|
+
operator: 'AND' | 'OR';
|
|
43
|
+
}
|
|
44
|
+
export interface NotificationChannel {
|
|
45
|
+
type: 'webhook' | 'email' | 'slack' | 'discord' | 'console';
|
|
46
|
+
config: Record<string, any>;
|
|
47
|
+
}
|
|
48
|
+
export interface AlertMessage {
|
|
49
|
+
ruleId: string;
|
|
50
|
+
ruleName: string;
|
|
51
|
+
type: AlertType;
|
|
52
|
+
severity: 'info' | 'warning' | 'critical';
|
|
53
|
+
title: string;
|
|
54
|
+
body: string;
|
|
55
|
+
data: any;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
export declare class AlertManager extends EventEmitter {
|
|
59
|
+
private rules;
|
|
60
|
+
private checkInterval?;
|
|
61
|
+
private alertHistory;
|
|
62
|
+
private maxHistorySize;
|
|
63
|
+
constructor();
|
|
64
|
+
/**
|
|
65
|
+
* Start the alert manager
|
|
66
|
+
*/
|
|
67
|
+
start(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Stop the alert manager
|
|
70
|
+
*/
|
|
71
|
+
stop(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Add a new alert rule
|
|
74
|
+
*/
|
|
75
|
+
addRule(rule: Omit<AlertRule, 'id' | 'createdAt' | 'triggerCount'>): AlertRule;
|
|
76
|
+
/**
|
|
77
|
+
* Remove an alert rule
|
|
78
|
+
*/
|
|
79
|
+
removeRule(ruleId: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Get all rules
|
|
82
|
+
*/
|
|
83
|
+
getRules(): AlertRule[];
|
|
84
|
+
/**
|
|
85
|
+
* Get a specific rule
|
|
86
|
+
*/
|
|
87
|
+
getRule(ruleId: string): AlertRule | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Enable/disable a rule
|
|
90
|
+
*/
|
|
91
|
+
toggleRule(ruleId: string, enabled: boolean): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Process a live event and check for alerts
|
|
94
|
+
*/
|
|
95
|
+
processEvent(event: LiveEvent): void;
|
|
96
|
+
/**
|
|
97
|
+
* Evaluate an alert condition against an event
|
|
98
|
+
*/
|
|
99
|
+
private evaluateCondition;
|
|
100
|
+
private evaluateOddsDrop;
|
|
101
|
+
private evaluateOddsValue;
|
|
102
|
+
private evaluateEventCondition;
|
|
103
|
+
private evaluateCompositeCondition;
|
|
104
|
+
/**
|
|
105
|
+
* Trigger an alert
|
|
106
|
+
*/
|
|
107
|
+
private triggerAlert;
|
|
108
|
+
/**
|
|
109
|
+
* Create an alert message
|
|
110
|
+
*/
|
|
111
|
+
private createAlertMessage;
|
|
112
|
+
private determineSeverity;
|
|
113
|
+
private formatAlertTitle;
|
|
114
|
+
private formatAlertBody;
|
|
115
|
+
/**
|
|
116
|
+
* Send notification to a channel
|
|
117
|
+
*/
|
|
118
|
+
private sendNotification;
|
|
119
|
+
private sendWebhook;
|
|
120
|
+
private sendSlack;
|
|
121
|
+
private sendDiscord;
|
|
122
|
+
private sendEmail;
|
|
123
|
+
/**
|
|
124
|
+
* Check scheduled alerts (for time-based alerts)
|
|
125
|
+
*/
|
|
126
|
+
private checkScheduledAlerts;
|
|
127
|
+
/**
|
|
128
|
+
* Get alert history
|
|
129
|
+
*/
|
|
130
|
+
getAlertHistory(limit?: number): AlertMessage[];
|
|
131
|
+
/**
|
|
132
|
+
* Clear alert history
|
|
133
|
+
*/
|
|
134
|
+
clearHistory(): void;
|
|
135
|
+
/**
|
|
136
|
+
* Get statistics
|
|
137
|
+
*/
|
|
138
|
+
getStats(): {
|
|
139
|
+
totalRules: number;
|
|
140
|
+
enabledRules: number;
|
|
141
|
+
totalAlerts: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export declare function getAlertManager(): AlertManager;
|
|
145
|
+
export declare function resetAlertManager(): void;
|