@gotza02/sequential-thinking 10000.1.2 → 10000.1.3
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 +39 -249
- package/dist/dashboard/server.d.ts +2 -45
- package/dist/dashboard/server.js +64 -250
- package/dist/index.js +4 -1
- package/dist/tools/sports/core/cache.d.ts +8 -19
- package/dist/tools/sports/core/cache.js +38 -95
- package/dist/tools/sports/core/constants.d.ts +4 -63
- package/dist/tools/sports/core/constants.js +11 -86
- package/dist/tools/sports/core/types.d.ts +1 -40
- package/package.json +1 -1
- package/dist/tools/sports/core/alert-manager.d.ts +0 -96
- package/dist/tools/sports/core/alert-manager.js +0 -319
- package/dist/tools/sports/core/circuit-breaker.d.ts +0 -40
- package/dist/tools/sports/core/circuit-breaker.js +0 -99
- package/dist/tools/sports/core/data-quality.d.ts +0 -36
- package/dist/tools/sports/core/data-quality.js +0 -243
- package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
- package/dist/tools/sports/core/historical-analyzer.js +0 -261
- package/dist/tools/sports/core/index.d.ts +0 -13
- package/dist/tools/sports/core/index.js +0 -16
- package/dist/tools/sports/core/ml-prediction.d.ts +0 -76
- package/dist/tools/sports/core/ml-prediction.js +0 -260
- package/dist/tools/sports/core/realtime-manager.d.ts +0 -51
- package/dist/tools/sports/core/realtime-manager.js +0 -222
- package/dist/tools/sports/core/retry.d.ts +0 -29
- package/dist/tools/sports/core/retry.js +0 -77
package/README.md
CHANGED
|
@@ -1,262 +1,52 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gotza02/sequential-thinking
|
|
2
2
|
|
|
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
|
-
|
|
6
|
+
## 🚀 Features
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
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
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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
|
-
|
|
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>;
|
package/dist/dashboard/server.js
CHANGED
|
@@ -1,253 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
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
|
}
|