@gotza02/sequential-thinking 10000.1.2 → 10000.1.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 (32) hide show
  1. package/README.md +39 -249
  2. package/dist/dashboard/server.d.ts +2 -45
  3. package/dist/dashboard/server.js +64 -250
  4. package/dist/index.js +4 -1
  5. package/dist/tools/sports/core/base.d.ts +31 -1
  6. package/dist/tools/sports/core/base.js +25 -0
  7. package/dist/tools/sports/core/cache.d.ts +8 -19
  8. package/dist/tools/sports/core/cache.js +38 -95
  9. package/dist/tools/sports/core/constants.d.ts +4 -63
  10. package/dist/tools/sports/core/constants.js +11 -86
  11. package/dist/tools/sports/core/types.d.ts +146 -34
  12. package/dist/tools/sports/providers/api.d.ts +12 -1
  13. package/dist/tools/sports/providers/api.js +170 -0
  14. package/dist/tools/sports/tools/match.d.ts +5 -0
  15. package/dist/tools/sports/tools/match.js +530 -5
  16. package/package.json +1 -1
  17. package/dist/tools/sports/core/alert-manager.d.ts +0 -96
  18. package/dist/tools/sports/core/alert-manager.js +0 -319
  19. package/dist/tools/sports/core/circuit-breaker.d.ts +0 -40
  20. package/dist/tools/sports/core/circuit-breaker.js +0 -99
  21. package/dist/tools/sports/core/data-quality.d.ts +0 -36
  22. package/dist/tools/sports/core/data-quality.js +0 -243
  23. package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
  24. package/dist/tools/sports/core/historical-analyzer.js +0 -261
  25. package/dist/tools/sports/core/index.d.ts +0 -13
  26. package/dist/tools/sports/core/index.js +0 -16
  27. package/dist/tools/sports/core/ml-prediction.d.ts +0 -76
  28. package/dist/tools/sports/core/ml-prediction.js +0 -260
  29. package/dist/tools/sports/core/realtime-manager.d.ts +0 -51
  30. package/dist/tools/sports/core/realtime-manager.js +0 -222
  31. package/dist/tools/sports/core/retry.d.ts +0 -29
  32. package/dist/tools/sports/core/retry.js +0 -77
package/README.md CHANGED
@@ -1,262 +1,52 @@
1
- # Football Analysis System - Fixed & Enhanced
1
+ # @gotza02/sequential-thinking
2
2
 
3
- ## การปรับปรุงที่ทำ (Priority 1-3)
3
+ **The Elite "Brain" for AI Agents.**
4
+ An advanced Model Context Protocol (MCP) server that equips AI with structured reasoning, web search capabilities, and safety protocols.
4
5
 
5
- ### Priority 1: Core Reliability
6
+ ## 🚀 Features
6
7
 
7
- #### 1.1 Circuit Breaker Pattern (`core/circuit-breaker.ts`)
8
- - ป้องกัน cascading failures เมื่อ API ล่ม
9
- - สถานะ: CLOSED HALF-OPEN → OPEN
10
- - Auto-reset หลังจาก timeout
8
+ ### 🧠 Sequential Thinking Engine
9
+ Forces the AI to think before acting using a strict block-based flow:
10
+ - **Analysis:** Understand the problem deeply.
11
+ - **Planning:** Formulate a step-by-step plan.
12
+ - **Execution:** Perform actions (with tool support).
13
+ - **Observation:** Analyze results (Mandatory step).
14
+ - **Reflection:** Critique the outcome and adjust.
11
15
 
12
- #### 1.2 Retry Mechanism (`core/retry.ts`)
13
- - Exponential backoff with jitter
14
- - Configurable retry policies
15
- - แยก retryable vs non-retryable errors
16
+ ### 🛡️ Coding Safety Net (New!)
17
+ - **Auto-Backup:** Automatically creates `.bak` backups before modifying any file via `deep_code_edit`.
18
+ - **Destructive Action Protection:** Prevents accidental data loss during code refactoring.
16
19
 
17
- #### 1.3 Enhanced Cache (`core/cache.ts`)
18
- - Stale-while-revalidate pattern
19
- - TTL ลดลงสำหรับ live data (15 วินาที)
20
- - Background refresh
20
+ ### Sports Intelligence (Upgraded)
21
+ - **Dual-Source Deep Dive:** Simultaneously scrapes news (BBC, Sky) and stats (Understat, FBref) for comprehensive coverage.
22
+ - **Smart xG Extraction:** "Smart Extractor" technology specifically for Understat to get accurate Expected Goals data.
23
+ - **Handicap Odds:** Dedicated search protocols for Asian Handicap and betting market analysis.
24
+ - **Fallback Protection:** Intelligent warning system when API keys are missing.
21
25
 
22
- ### Priority 2: Realtime Features
26
+ ### 🌐 Web Search Integration
27
+ - Built-in support for **Exa**, **Brave**, **Google Search**, and **DuckDuckGo** (New!).
28
+ - Allows the AI to "pause and research" during the thinking process.
23
29
 
24
- #### 2.1 Realtime Data Manager (`core/realtime-manager.ts`)
25
- - Polling ทุก 5-15 วินาที
26
- - Event-driven architecture
27
- - Score change detection
28
- - Odds change detection
30
+ ## 🛠️ Recent Updates (v10000.0.7+)
29
31
 
30
- #### 2.2 Alert Manager (`core/alert-manager.ts`)
31
- - Rule-based alert engine
32
- - Multiple notification channels (webhook, slack, discord, email)
33
- - Cooldown และ rate limiting
34
- - Alert history
32
+ ### 🔒 Security & Stability
33
+ - **Enhanced Shell Protection:** Blocks dangerous shell metacharacters and risky commands (e.g., `curl | sh`, `rm -rf`).
34
+ - **ReDoS Protection:** Validates regex patterns to prevent Denial of Service attacks.
35
+ - **Path Safety:** Strict path traversal checks to keep operations within the project root.
36
+ - **Resource Limits:** Enforces file size limits (10MB) for editing and parsing to prevent memory exhaustion.
35
37
 
36
- #### 2.3 Data Quality Validator (`core/data-quality.ts`)
37
- - Completeness, accuracy, freshness scoring
38
- - Issue detection
39
- - Improvement suggestions
38
+ ### Performance & Caching
39
+ - **Graph Caching:** Implemented intelligent caching for the Knowledge Graph (max 1000 files) to speed up analysis.
40
+ - **Rate Limiting:** Added request rate limiting for HTTP endpoints and search providers.
41
+ - **Auto-Cleanup:** Automatically manages backup files (keeps last 10) and prunes old thought history.
40
42
 
41
- ### Priority 3: Advanced Analytics
43
+ ### New Capabilities
44
+ - **DuckDuckGo Support:** Added a no-API-key search provider fallback.
45
+ - **Graceful Shutdown:** Ensures data (thoughts, notes) is saved correctly when the server stops.
46
+ - **Enhanced API:** Improved `/health` endpoint with system stats and added pagination to `/api/notes`.
42
47
 
43
- #### 3.1 ML Prediction Engine (`core/ml-prediction.ts`)
44
- - Weighted factor analysis
45
- - Probability calculation
46
- - Confidence scoring
47
- - Over/Under & BTTS predictions
48
-
49
- #### 3.2 Historical Analyzer (`core/historical-analyzer.ts`)
50
- - Pattern detection
51
- - Trend analysis
52
- - Betting pattern analysis
53
- - Referee bias analysis
54
-
55
- #### 3.3 Dashboard Server (`dashboard/server.ts`)
56
- - Real-time monitoring
57
- - Cache statistics
58
- - Circuit breaker status
59
- - Alert management
60
-
61
- ## การใช้งาน
62
-
63
- ### 1. เริ่มต้นระบบ
64
-
65
- ```typescript
66
- import { getRealtimeManager, getAlertManager, startDashboard } from './core/index.js';
67
-
68
- // Start realtime manager
69
- const realtime = getRealtimeManager();
70
- realtime.start();
71
-
72
- // Start alert manager
73
- const alerts = getAlertManager();
74
- alerts.start();
75
-
76
- // Start dashboard
77
- startDashboard(8080);
78
- ```
79
-
80
- ### 2. สมัครรับข้อมูลแบบ Realtime
81
-
82
- ```typescript
83
- import { getRealtimeManager } from './core/index.js';
84
-
85
- const realtime = getRealtimeManager();
86
-
87
- // Subscribe to match events
88
- const unsubscribe = realtime.subscribeToMatch('match-123', (event) => {
89
- console.log(`Event: ${event.type}`, event.data);
90
- });
91
-
92
- // Subscribe to odds changes
93
- realtime.subscribeToOdds('match-123', (event) => {
94
- console.log(`Odds changed: ${event.data.newOdds}`);
95
- });
96
- ```
97
-
98
- ### 3. ตั้งค่า Alerts
99
-
100
- ```typescript
101
- import { getAlertManager } from './core/index.js';
102
-
103
- const alerts = getAlertManager();
104
-
105
- // Add odds drop alert
106
- alerts.addRule({
107
- name: 'Arsenal Odds Drop',
108
- type: 'odds_drop',
109
- condition: {
110
- type: 'odds_drop',
111
- matchId: 'match-123',
112
- threshold: 2.0,
113
- percentage: 15,
114
- },
115
- channels: [
116
- { type: 'webhook', config: { url: 'https://your-webhook.com' } },
117
- { type: 'slack', config: { webhook: 'https://hooks.slack.com/...' } },
118
- ],
119
- cooldown: 300000, // 5 minutes
120
- enabled: true,
121
- });
122
-
123
- // Add goal alert
124
- alerts.addRule({
125
- name: 'Goal Alert',
126
- type: 'goal',
127
- condition: {
128
- type: 'event',
129
- eventTypes: ['goal'],
130
- },
131
- channels: [{ type: 'console', config: {} }],
132
- cooldown: 0,
133
- enabled: true,
134
- });
135
- ```
136
-
137
- ### 4. ใช้ Retry และ Circuit Breaker
138
-
139
- ```typescript
140
- import { withRetry, getCircuitBreaker } from './core/index.js';
141
-
142
- // Simple retry
143
- const data = await withRetry(
144
- async () => fetchFromAPI(),
145
- { maxAttempts: 5, initialDelay: 1000 },
146
- 'api-call'
147
- );
148
-
149
- // With circuit breaker
150
- const cb = getCircuitBreaker('api-football', { failureThreshold: 5 });
151
- const result = await cb.execute(() => fetchFromAPI());
152
- ```
153
-
154
- ### 5. ML Prediction
155
-
156
- ```typescript
157
- import { getPredictionEngine } from './core/index.js';
158
-
159
- const ml = getPredictionEngine();
160
-
161
- const prediction = await ml.predict({
162
- match: matchData,
163
- homeForm: { last5: ['W', 'W', 'D', 'L', 'W'], goalsFor: 10, goalsAgainst: 5, xG: 8.5, xGA: 6.2 },
164
- awayForm: { last5: ['L', 'D', 'W', 'L', 'L'], goalsFor: 6, goalsAgainst: 12, xG: 7.1, xGA: 10.5 },
165
- h2h: headToHeadData,
166
- homeStats: homeTeamStats,
167
- awayStats: awayTeamStats,
168
- injuries: { home: { keyPlayers: 1, total: 3 }, away: { keyPlayers: 0, total: 2 } },
169
- });
170
-
171
- console.log(`Home: ${prediction.homeWin * 100}%`);
172
- console.log(`Draw: ${prediction.draw * 100}%`);
173
- console.log(`Away: ${prediction.awayWin * 100}%`);
174
- console.log(`Confidence: ${prediction.confidence}%`);
175
- ```
176
-
177
- ### 6. Historical Analysis
178
-
179
- ```typescript
180
- import { getHistoricalAnalyzer } from './core/index.js';
181
-
182
- const analyzer = getHistoricalAnalyzer();
183
-
184
- // Analyze team performance
185
- const performance = analyzer.analyzeTeamPerformance('team-123');
186
-
187
- // Find patterns
188
- const patterns = analyzer.findBettingPatterns({
189
- league: 'Premier League',
190
- dateFrom: new Date('2024-01-01'),
191
- });
192
-
193
- // Analyze trends
194
- const trends = analyzer.analyzeTrends('team-123', 10);
195
- ```
196
-
197
- ### 7. Data Quality Check
198
-
199
- ```typescript
200
- import { getDataQualityValidator } from './core/index.js';
201
-
202
- const validator = getDataQualityValidator();
203
-
204
- const report = validator.validateMatchData(matchData);
205
-
206
- console.log(`Quality Score: ${report.overall}/100`);
207
- console.log(`Grade: ${validator.getGrade(report.overall)}`);
208
-
209
- if (report.issues.length > 0) {
210
- console.log('Issues:', report.issues);
211
- }
212
- ```
213
-
214
- ## Dashboard
215
-
216
- เปิด browser ไปที่ `http://localhost:8080` เพื่อดู:
217
- - System status
218
- - Cache statistics
219
- - Circuit breaker status
220
- - Realtime data
221
- - Alert system
222
-
223
- ## Environment Variables
48
+ ## 📦 Installation
224
49
 
225
50
  ```bash
226
- # API Keys
227
- API_FOOTBALL_KEY=your_key
228
- FOOTBALL_DATA_KEY=your_key
229
- ODDS_API_KEY=your_key
230
- SPORTRADAR_KEY=your_key
231
-
232
- # Cache
233
- SPORTS_CACHE_TTL=300000
234
- SPORTS_CACHE_PATH=.sports_cache.json
235
-
236
- # Dashboard
237
- DASHBOARD_PORT=8080
238
- ```
239
-
240
- ## ไฟล์ที่สร้างใหม่
241
-
242
- ```
243
- src/tools/sports/core/
244
- ├── circuit-breaker.ts # Circuit breaker pattern
245
- ├── retry.ts # Retry mechanism
246
- ├── cache.ts # Enhanced cache (modified)
247
- ├── realtime-manager.ts # Realtime data manager
248
- ├── alert-manager.ts # Alert system
249
- ├── data-quality.ts # Data quality validator
250
- ├── ml-prediction.ts # ML prediction engine
251
- ├── historical-analyzer.ts # Historical analysis
252
- ├── types.ts # Type definitions (updated)
253
- ├── constants.ts # Constants (updated)
254
- └── index.ts # Exports
255
-
256
- src/dashboard/
257
- └── server.ts # Dashboard server
258
- ```
259
-
260
- ## License
261
-
262
- MIT
51
+ npx -y @gotza02/sequential-thinking
52
+ ```
@@ -1,45 +1,2 @@
1
- /**
2
- * DASHBOARD SERVER
3
- */
4
- export interface DashboardStats {
5
- cache: {
6
- size: number;
7
- maxSize: number;
8
- hits: number;
9
- breakdown: Record<string, number>;
10
- };
11
- circuitBreakers: Array<{
12
- name: string;
13
- state: string;
14
- failures: number;
15
- }>;
16
- realtime: {
17
- isRunning: boolean;
18
- liveMatches: number;
19
- subscribers: number;
20
- };
21
- alerts: {
22
- totalRules: number;
23
- enabledRules: number;
24
- totalAlerts: number;
25
- };
26
- }
27
- export declare class DashboardServer {
28
- private server;
29
- private port;
30
- private updateInterval;
31
- constructor(port?: number);
32
- start(): void;
33
- stop(): void;
34
- private handleRequest;
35
- private serveDashboard;
36
- private serveStats;
37
- private clearCache;
38
- private resetCircuitBreakers;
39
- private serveAlertRules;
40
- private serveAlertHistory;
41
- private collectStats;
42
- private broadcastStats;
43
- }
44
- export declare function startDashboard(port?: number): DashboardServer;
45
- export declare function stopDashboard(): void;
1
+ import * as http from 'http';
2
+ export declare function startDashboard(port: number, historyPath: string): Promise<http.Server>;
@@ -1,253 +1,67 @@
1
- /**
2
- * DASHBOARD SERVER
3
- */
4
- import { createServer } from 'http';
5
- import { CacheService } from '../tools/sports/core/cache.js';
6
- import { getAllCircuitBreakerStatus } from '../tools/sports/core/circuit-breaker.js';
7
- import { getRealtimeManager } from '../tools/sports/core/realtime-manager.js';
8
- import { getAlertManager } from '../tools/sports/core/alert-manager.js';
9
- // Simple logger fallback
10
- const logger = {
11
- info: (...args) => console.error('[INFO]', ...args),
12
- warn: (...args) => console.warn('[WARN]', ...args),
13
- error: (...args) => console.error('[ERROR]', ...args),
14
- debug: (...args) => { }
15
- };
16
- export class DashboardServer {
17
- server = null;
18
- port;
19
- updateInterval = null;
20
- constructor(port = 8080) {
21
- this.port = port;
22
- }
23
- start() {
24
- if (this.server)
25
- return;
26
- this.server = createServer((req, res) => {
27
- this.handleRequest(req, res);
28
- });
29
- this.server.listen(this.port, () => {
30
- logger.info(`[Dashboard] Server running on http://localhost:${this.port}`);
1
+ import * as http from 'http';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ export function startDashboard(port, historyPath) {
8
+ return new Promise((resolve, reject) => {
9
+ const server = http.createServer((req, res) => {
10
+ // Handle CORS
11
+ res.setHeader('Access-Control-Allow-Origin', '*');
12
+ res.setHeader('Access-Control-Allow-Methods', 'GET');
13
+ if (req.url === '/') {
14
+ // Serve index.html
15
+ const indexPath = path.join(__dirname, 'index.html');
16
+ fs.readFile(indexPath, (err, data) => {
17
+ if (err) {
18
+ res.writeHead(500);
19
+ res.end('Error loading dashboard');
20
+ return;
21
+ }
22
+ res.writeHead(200, { 'Content-Type': 'text/html' });
23
+ res.end(data);
24
+ });
25
+ }
26
+ else if (req.url === '/api/history') {
27
+ // Serve thoughts_history.json
28
+ fs.readFile(historyPath, (err, data) => {
29
+ if (err) {
30
+ // Try .tmp if main file is locked/missing (rare fallback)
31
+ fs.readFile(historyPath + '.tmp', (err2, data2) => {
32
+ if (err2) {
33
+ res.writeHead(500);
34
+ res.end(JSON.stringify({ error: 'History not found' }));
35
+ return;
36
+ }
37
+ res.writeHead(200, { 'Content-Type': 'application/json' });
38
+ res.end(data2);
39
+ });
40
+ return;
41
+ }
42
+ res.writeHead(200, { 'Content-Type': 'application/json' });
43
+ res.end(data);
44
+ });
45
+ }
46
+ else {
47
+ res.writeHead(404);
48
+ res.end('Not found');
49
+ }
31
50
  });
32
- this.updateInterval = setInterval(() => {
33
- this.broadcastStats();
34
- }, 5000);
35
- }
36
- stop() {
37
- if (this.updateInterval) {
38
- clearInterval(this.updateInterval);
39
- this.updateInterval = null;
40
- }
41
- if (this.server) {
42
- this.server.close();
43
- this.server = null;
44
- logger.info('[Dashboard] Server stopped');
45
- }
46
- }
47
- handleRequest(req, res) {
48
- const url = new URL(req.url, `http://localhost:${this.port}`);
49
- res.setHeader('Access-Control-Allow-Origin', '*');
50
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
51
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
52
- if (req.method === 'OPTIONS') {
53
- res.writeHead(200);
54
- res.end();
55
- return;
56
- }
57
- if (url.pathname === '/' || url.pathname === '/index.html') {
58
- this.serveDashboard(res);
59
- }
60
- else if (url.pathname === '/api/stats') {
61
- this.serveStats(res);
62
- }
63
- else if (url.pathname === '/api/cache/clear') {
64
- this.clearCache(res);
65
- }
66
- else if (url.pathname === '/api/circuit-breakers/reset') {
67
- this.resetCircuitBreakers(res);
68
- }
69
- else if (url.pathname === '/api/alerts/rules') {
70
- this.serveAlertRules(res);
71
- }
72
- else if (url.pathname === '/api/alerts/history') {
73
- this.serveAlertHistory(res);
74
- }
75
- else {
76
- res.writeHead(404);
77
- res.end(JSON.stringify({ error: 'Not found' }));
78
- }
79
- }
80
- serveDashboard(res) {
81
- const html = `<!DOCTYPE html>
82
- <html lang="en">
83
- <head>
84
- <meta charset="UTF-8">
85
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
86
- <title>Football Analysis Dashboard</title>
87
- <style>
88
- * { margin: 0; padding: 0; box-sizing: border-box; }
89
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; padding: 20px; }
90
- .header { text-align: center; padding: 20px; margin-bottom: 30px; }
91
- .header h1 { font-size: 2.5em; background: linear-gradient(135deg, #3b82f6, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
92
- .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; max-width: 1400px; margin: 0 auto; }
93
- .card { background: #1e293b; border-radius: 12px; padding: 20px; border: 1px solid #334155; }
94
- .card h2 { font-size: 1.2em; margin-bottom: 15px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px; }
95
- .stat { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #334155; }
96
- .stat:last-child { border-bottom: none; }
97
- .stat-value { font-weight: bold; color: #3b82f6; }
98
- .status { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 0.85em; font-weight: 500; }
99
- .status-online { background: #22c55e; color: #064e3b; }
100
- .status-offline { background: #ef4444; color: #7f1d1d; }
101
- .cb-closed { color: #22c55e; }
102
- .cb-open { color: #ef4444; }
103
- button { background: #3b82f6; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 0.9em; margin-top: 10px; }
104
- button:hover { background: #2563eb; }
105
- .refresh-indicator { position: fixed; top: 20px; right: 20px; width: 10px; height: 10px; background: #22c55e; border-radius: 50%; animation: pulse 2s infinite; }
106
- @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
107
- </style>
108
- </head>
109
- <body>
110
- <div class="refresh-indicator"></div>
111
- <div class="header">
112
- <h1>Football Analysis Dashboard</h1>
113
- <p>Real-time System Monitoring</p>
114
- </div>
115
- <div class="grid">
116
- <div class="card">
117
- <h2>System Status</h2>
118
- <div class="stat"><span>Realtime Manager</span><span class="status status-online" id="realtime-status">Online</span></div>
119
- <div class="stat"><span>Alert Manager</span><span class="status status-online" id="alert-status">Online</span></div>
120
- <div class="stat"><span>Cache Service</span><span class="status status-online" id="cache-status">Online</span></div>
121
- <div class="stat"><span>Last Update</span><span id="last-update">-</span></div>
122
- </div>
123
- <div class="card">
124
- <h2>Cache Statistics</h2>
125
- <div class="stat"><span>Cache Size</span><span class="stat-value" id="cache-size">0 / 2000</span></div>
126
- <div class="stat"><span>Cache Hits</span><span class="stat-value" id="cache-hits">0</span></div>
127
- <button onclick="clearCache()">Clear Cache</button>
128
- </div>
129
- <div class="card">
130
- <h2>Circuit Breakers</h2>
131
- <div id="circuit-breakers"><div class="stat"><span>No circuit breakers active</span></div></div>
132
- <button onclick="resetCircuitBreakers()">Reset All</button>
133
- </div>
134
- <div class="card">
135
- <h2>Realtime Data</h2>
136
- <div class="stat"><span>Live Matches</span><span class="stat-value" id="live-matches">0</span></div>
137
- <div class="stat"><span>Active Subscribers</span><span class="stat-value" id="subscribers">0</span></div>
138
- </div>
139
- <div class="card">
140
- <h2>Alert System</h2>
141
- <div class="stat"><span>Total Rules</span><span class="stat-value" id="total-rules">0</span></div>
142
- <div class="stat"><span>Enabled Rules</span><span class="stat-value" id="enabled-rules">0</span></div>
143
- <div class="stat"><span>Total Alerts</span><span class="stat-value" id="total-alerts">0</span></div>
144
- </div>
145
- </div>
146
- <script>
147
- async function fetchStats() {
148
- try {
149
- const response = await fetch('/api/stats');
150
- const stats = await response.json();
151
- updateDashboard(stats);
152
- } catch (error) { console.error('Failed to fetch stats:', error); }
153
- }
154
- function updateDashboard(stats) {
155
- document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
156
- document.getElementById('cache-size').textContent = stats.cache.size + ' / ' + stats.cache.maxSize;
157
- document.getElementById('cache-hits').textContent = stats.cache.hits;
158
- const cbContainer = document.getElementById('circuit-breakers');
159
- if (stats.circuitBreakers.length > 0) {
160
- cbContainer.innerHTML = stats.circuitBreakers.map(cb => '<div class="stat"><span>' + cb.name + '</span><span class="cb-' + cb.state + '">' + cb.state.toUpperCase() + '</span></div>').join('');
51
+ server.on('error', (e) => {
52
+ if (e.code === 'EADDRINUSE') {
53
+ console.error(`[Dashboard] Port ${port} is busy. Trying ${port + 1}...`);
54
+ // Recursive retry
55
+ startDashboard(port + 1, historyPath).then(resolve).catch(reject);
161
56
  }
162
- document.getElementById('live-matches').textContent = stats.realtime.liveMatches;
163
- document.getElementById('subscribers').textContent = stats.realtime.subscribers;
164
- document.getElementById('total-rules').textContent = stats.alerts.totalRules;
165
- document.getElementById('enabled-rules').textContent = stats.alerts.enabledRules;
166
- document.getElementById('total-alerts').textContent = stats.alerts.totalAlerts;
167
- }
168
- async function clearCache() {
169
- try { await fetch('/api/cache/clear', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
170
- }
171
- async function resetCircuitBreakers() {
172
- try { await fetch('/api/circuit-breakers/reset', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
173
- }
174
- fetchStats();
175
- setInterval(fetchStats, 5000);
176
- </script>
177
- </body>
178
- </html>`;
179
- res.setHeader('Content-Type', 'text/html');
180
- res.writeHead(200);
181
- res.end(html);
182
- }
183
- serveStats(res) {
184
- const stats = this.collectStats();
185
- res.setHeader('Content-Type', 'application/json');
186
- res.writeHead(200);
187
- res.end(JSON.stringify(stats));
188
- }
189
- clearCache(res) {
190
- const cache = new CacheService();
191
- cache.clear();
192
- res.setHeader('Content-Type', 'application/json');
193
- res.writeHead(200);
194
- res.end(JSON.stringify({ success: true, message: 'Cache cleared' }));
195
- }
196
- resetCircuitBreakers(res) {
197
- res.setHeader('Content-Type', 'application/json');
198
- res.writeHead(200);
199
- res.end(JSON.stringify({ success: true, message: 'Circuit breakers will reset automatically' }));
200
- }
201
- serveAlertRules(res) {
202
- const alertManager = getAlertManager();
203
- const rules = alertManager.getRules();
204
- res.setHeader('Content-Type', 'application/json');
205
- res.writeHead(200);
206
- res.end(JSON.stringify(rules, null, 2));
207
- }
208
- serveAlertHistory(res) {
209
- const alertManager = getAlertManager();
210
- const history = alertManager.getAlertHistory(50);
211
- res.setHeader('Content-Type', 'application/json');
212
- res.writeHead(200);
213
- res.end(JSON.stringify(history, null, 2));
214
- }
215
- collectStats() {
216
- const cache = new CacheService();
217
- const cacheStats = cache.getStats();
218
- const realtimeManager = getRealtimeManager();
219
- const alertManager = getAlertManager();
220
- return {
221
- cache: {
222
- size: cacheStats.size,
223
- maxSize: cacheStats.maxSize,
224
- hits: cacheStats.hits,
225
- breakdown: cache.getBreakdown(),
226
- },
227
- circuitBreakers: getAllCircuitBreakerStatus(),
228
- realtime: {
229
- isRunning: true,
230
- liveMatches: realtimeManager.getLiveMatches().length,
231
- subscribers: realtimeManager.listenerCount('event'),
232
- },
233
- alerts: alertManager.getStats(),
234
- };
235
- }
236
- broadcastStats() {
237
- // WebSocket broadcast would go here
238
- }
239
- }
240
- let globalDashboardServer = null;
241
- export function startDashboard(port) {
242
- if (!globalDashboardServer) {
243
- globalDashboardServer = new DashboardServer(port);
244
- globalDashboardServer.start();
245
- }
246
- return globalDashboardServer;
247
- }
248
- export function stopDashboard() {
249
- if (globalDashboardServer) {
250
- globalDashboardServer.stop();
251
- globalDashboardServer = null;
252
- }
57
+ else {
58
+ console.error('[Dashboard] Server error:', e);
59
+ reject(e);
60
+ }
61
+ });
62
+ server.listen(port, () => {
63
+ console.error(`[Dashboard] 📊 Dashboard running at http://localhost:${port}`);
64
+ resolve(server);
65
+ });
66
+ });
253
67
  }