pikakit 1.0.13 → 1.0.15
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/bin/cli.mjs +2 -2
- package/bin/install.mjs +26 -0
- package/bin/kit.js +1 -1
- package/lib/agent-cli/dashboard/index.html +845 -231
- package/lib/agent-cli/lib/dashboard-data.js +79 -242
- package/lib/agent-cli/lib/ui/dashboard-ui.js +20 -30
- package/lib/agent-cli/scripts/dashboard-server.js +324 -0
- package/package.json +2 -2
- package/lib/agent-cli/scripts/dashboard_server.js +0 -344
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Dashboard Server v7.0 - PikaKit Precision Learning Engine
|
|
4
|
+
*
|
|
5
|
+
* Modern ES Modules server with REST API endpoints.
|
|
6
|
+
* Serves real-time metrics from PikaKit learning system.
|
|
7
|
+
*
|
|
8
|
+
* @version 7.0.0
|
|
9
|
+
* @author PikaKit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import http from 'http';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// Colors for terminal
|
|
21
|
+
const c = {
|
|
22
|
+
reset: '\x1b[0m',
|
|
23
|
+
bold: '\x1b[1m',
|
|
24
|
+
cyan: '\x1b[36m',
|
|
25
|
+
green: '\x1b[32m',
|
|
26
|
+
yellow: '\x1b[33m',
|
|
27
|
+
gray: '\x1b[90m'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Find project root
|
|
31
|
+
function findProjectRoot() {
|
|
32
|
+
let dir = process.cwd();
|
|
33
|
+
while (dir !== path.dirname(dir)) {
|
|
34
|
+
if (fs.existsSync(path.join(dir, '.agent'))) return dir;
|
|
35
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
|
|
36
|
+
dir = path.dirname(dir);
|
|
37
|
+
}
|
|
38
|
+
return process.cwd();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const projectRoot = findProjectRoot();
|
|
42
|
+
const dashboardPath = path.join(__dirname, '..', 'dashboard');
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// DATA PROVIDERS
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
// Safe import helper
|
|
49
|
+
async function safeImport(modulePath) {
|
|
50
|
+
try {
|
|
51
|
+
return await import(modulePath);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Load data modules dynamically
|
|
58
|
+
let metricsCollector = null;
|
|
59
|
+
let causalityEngine = null;
|
|
60
|
+
let skillGenerator = null;
|
|
61
|
+
let abTesting = null;
|
|
62
|
+
let reinforcement = null;
|
|
63
|
+
|
|
64
|
+
async function loadModules() {
|
|
65
|
+
const libPath = path.join(__dirname, '..', 'lib');
|
|
66
|
+
metricsCollector = await safeImport(path.join(libPath, 'metrics-collector.js'));
|
|
67
|
+
causalityEngine = await safeImport(path.join(libPath, 'causality-engine.js'));
|
|
68
|
+
skillGenerator = await safeImport(path.join(libPath, 'skill-generator.js'));
|
|
69
|
+
abTesting = await safeImport(path.join(libPath, 'ab-testing.js'));
|
|
70
|
+
reinforcement = await safeImport(path.join(libPath, 'reinforcement-loop.js'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// API HANDLERS
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
const api = {
|
|
78
|
+
// Full dashboard data
|
|
79
|
+
'/api/dashboard': () => {
|
|
80
|
+
try {
|
|
81
|
+
const realKpis = metricsCollector?.getKPIs?.() || null;
|
|
82
|
+
const hasRealData = realKpis?.kpis && Object.keys(realKpis.kpis).length > 0;
|
|
83
|
+
|
|
84
|
+
const kpis = hasRealData ? realKpis.kpis : {};
|
|
85
|
+
|
|
86
|
+
const totalTasks = metricsCollector?.getMetricValue?.('total_tasks') || 0;
|
|
87
|
+
const patternsLearned = causalityEngine?.getPatternCount?.() || 0;
|
|
88
|
+
const skillsGenerated = skillGenerator?.getSkillCount?.() || 0;
|
|
89
|
+
|
|
90
|
+
// Determine if this is a new user (no data yet)
|
|
91
|
+
const isNewUser = totalTasks === 0 && patternsLearned === 0;
|
|
92
|
+
|
|
93
|
+
const summary = {
|
|
94
|
+
totalTasks,
|
|
95
|
+
patternsLearned,
|
|
96
|
+
skillsGenerated,
|
|
97
|
+
version: '7.0.0',
|
|
98
|
+
isNewUser
|
|
99
|
+
};
|
|
100
|
+
return { kpis: { kpis }, summary, version: '7.0.0', isNewUser };
|
|
101
|
+
} catch (e) {
|
|
102
|
+
return { kpis: { kpis: {} }, summary: { isNewUser: true }, error: e.message, version: '7.0.0', isNewUser: true };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
// KPIs only
|
|
107
|
+
'/api/kpis': () => {
|
|
108
|
+
try {
|
|
109
|
+
return metricsCollector?.getKPIs?.() || { kpis: {} };
|
|
110
|
+
} catch (e) {
|
|
111
|
+
return { kpis: {}, error: e.message };
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// Alerts
|
|
116
|
+
'/api/alerts': () => {
|
|
117
|
+
try {
|
|
118
|
+
const kpis = metricsCollector?.getKPIs?.()?.kpis || {};
|
|
119
|
+
const alerts = [];
|
|
120
|
+
|
|
121
|
+
// Generate alerts based on KPI thresholds
|
|
122
|
+
if (kpis.task_success_rate && parseFloat(kpis.task_success_rate.value) < 80) {
|
|
123
|
+
alerts.push({ id: 'low_success', severity: 'warning', message: 'Task success rate below 80%' });
|
|
124
|
+
}
|
|
125
|
+
if (kpis.error_repeat_rate && parseFloat(kpis.error_repeat_rate.value) > 10) {
|
|
126
|
+
alerts.push({ id: 'high_error', severity: 'warning', message: 'Error repeat rate above 10%' });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { alerts, count: alerts.length };
|
|
130
|
+
} catch (e) {
|
|
131
|
+
return { alerts: [], error: e.message };
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Skills
|
|
136
|
+
'/api/skills': () => {
|
|
137
|
+
try {
|
|
138
|
+
const skills = skillGenerator?.getAllSkills?.() || [];
|
|
139
|
+
return {
|
|
140
|
+
skills,
|
|
141
|
+
stats: { total: skills.length }
|
|
142
|
+
};
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return { skills: [], stats: { total: 0 }, error: e.message };
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// A/B Testing
|
|
149
|
+
'/api/ab-testing': () => {
|
|
150
|
+
try {
|
|
151
|
+
const active = abTesting?.getActiveTests?.() || [];
|
|
152
|
+
const completed = abTesting?.getCompletedTests?.() || [];
|
|
153
|
+
return {
|
|
154
|
+
active,
|
|
155
|
+
completed,
|
|
156
|
+
stats: { running: active.length, completed: completed.length }
|
|
157
|
+
};
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { active: [], completed: [], stats: { running: 0, completed: 0 }, error: e.message };
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Reinforcement Loop
|
|
164
|
+
'/api/reinforcement': () => {
|
|
165
|
+
try {
|
|
166
|
+
const stats = reinforcement?.getStats?.() || {};
|
|
167
|
+
return {
|
|
168
|
+
totalRewards: stats.rewards || 0,
|
|
169
|
+
totalPenalties: stats.penalties || 0,
|
|
170
|
+
averageConfidence: stats.avgConfidence || null
|
|
171
|
+
};
|
|
172
|
+
} catch (e) {
|
|
173
|
+
return { totalRewards: 0, totalPenalties: 0, averageConfidence: null };
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Patterns
|
|
178
|
+
'/api/patterns': () => {
|
|
179
|
+
try {
|
|
180
|
+
const patterns = causalityEngine?.loadCausalPatterns?.() || [];
|
|
181
|
+
return { total: patterns.length, patterns: patterns.slice(0, 20) };
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return { total: 0, patterns: [], error: e.message };
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Summary (legacy support)
|
|
188
|
+
'/api/summary': () => {
|
|
189
|
+
return { status: 'ok', version: '7.0.0', server: 'PikaKit Dashboard Server' };
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// ============================================================================
|
|
194
|
+
// MIME TYPES
|
|
195
|
+
// ============================================================================
|
|
196
|
+
|
|
197
|
+
const mimeTypes = {
|
|
198
|
+
'.html': 'text/html',
|
|
199
|
+
'.css': 'text/css',
|
|
200
|
+
'.js': 'application/javascript',
|
|
201
|
+
'.json': 'application/json',
|
|
202
|
+
'.png': 'image/png',
|
|
203
|
+
'.jpg': 'image/jpeg',
|
|
204
|
+
'.svg': 'image/svg+xml'
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// ============================================================================
|
|
208
|
+
// SERVER
|
|
209
|
+
// ============================================================================
|
|
210
|
+
|
|
211
|
+
function createServer(port) {
|
|
212
|
+
const server = http.createServer(async (req, res) => {
|
|
213
|
+
// CORS headers
|
|
214
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
215
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
216
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
217
|
+
|
|
218
|
+
if (req.method === 'OPTIONS') {
|
|
219
|
+
res.writeHead(204);
|
|
220
|
+
res.end();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
225
|
+
const pathname = url.pathname;
|
|
226
|
+
|
|
227
|
+
// API routes
|
|
228
|
+
if (api[pathname]) {
|
|
229
|
+
const data = api[pathname](url.searchParams);
|
|
230
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
231
|
+
res.end(JSON.stringify(data, null, 2));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Static files
|
|
236
|
+
let filePath = pathname === '/' ? 'index.html' : pathname.slice(1);
|
|
237
|
+
filePath = path.join(dashboardPath, filePath);
|
|
238
|
+
|
|
239
|
+
if (fs.existsSync(filePath)) {
|
|
240
|
+
const ext = path.extname(filePath);
|
|
241
|
+
const contentType = mimeTypes[ext] || 'application/octet-stream';
|
|
242
|
+
const content = fs.readFileSync(filePath);
|
|
243
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
244
|
+
res.end(content);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 404
|
|
249
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
250
|
+
res.end(JSON.stringify({ error: 'Not found', path: pathname }));
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return server;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function startServer(port = 3030) {
|
|
257
|
+
await loadModules();
|
|
258
|
+
|
|
259
|
+
const server = createServer(port);
|
|
260
|
+
|
|
261
|
+
server.listen(port, () => {
|
|
262
|
+
console.log(`
|
|
263
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
264
|
+
🧠 ${c.bold}PikaKit Dashboard Server v7.0${c.reset}
|
|
265
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
266
|
+
|
|
267
|
+
${c.green}→${c.reset} Dashboard: ${c.yellow}http://localhost:${port}${c.reset}
|
|
268
|
+
${c.green}→${c.reset} API: ${c.yellow}http://localhost:${port}/api/dashboard${c.reset}
|
|
269
|
+
|
|
270
|
+
${c.gray}Press Ctrl+C to stop${c.reset}
|
|
271
|
+
|
|
272
|
+
${c.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}
|
|
273
|
+
`);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
server.on('error', (e) => {
|
|
277
|
+
if (e.code === 'EADDRINUSE') {
|
|
278
|
+
console.log(`${c.yellow}Port ${port} in use, trying ${port + 1}...${c.reset}`);
|
|
279
|
+
startServer(port + 1);
|
|
280
|
+
} else {
|
|
281
|
+
console.error(`${c.red}Server error:${c.reset}`, e.message);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return server;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// CLI handling
|
|
289
|
+
const args = process.argv.slice(2);
|
|
290
|
+
|
|
291
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
292
|
+
console.log(`
|
|
293
|
+
${c.bold}PikaKit Dashboard Server v7.0${c.reset}
|
|
294
|
+
|
|
295
|
+
${c.bold}Usage:${c.reset}
|
|
296
|
+
node dashboard-server.js [options]
|
|
297
|
+
|
|
298
|
+
${c.bold}Options:${c.reset}
|
|
299
|
+
--port, -p <number> Port to run on (default: 3030)
|
|
300
|
+
--help, -h Show this help
|
|
301
|
+
|
|
302
|
+
${c.bold}API Endpoints:${c.reset}
|
|
303
|
+
GET /api/dashboard Full dashboard data
|
|
304
|
+
GET /api/kpis KPI metrics only
|
|
305
|
+
GET /api/alerts Active alerts
|
|
306
|
+
GET /api/skills Auto-generated skills
|
|
307
|
+
GET /api/ab-testing A/B test experiments
|
|
308
|
+
GET /api/reinforcement Reinforcement loop stats
|
|
309
|
+
GET /api/patterns Causal patterns
|
|
310
|
+
GET /api/summary Server status
|
|
311
|
+
|
|
312
|
+
${c.bold}Example:${c.reset}
|
|
313
|
+
node dashboard-server.js --port 3030
|
|
314
|
+
`);
|
|
315
|
+
} else {
|
|
316
|
+
let port = 3030;
|
|
317
|
+
const portIdx = args.findIndex(a => a === '--port' || a === '-p');
|
|
318
|
+
if (portIdx !== -1 && args[portIdx + 1]) {
|
|
319
|
+
port = parseInt(args[portIdx + 1], 10) || 3030;
|
|
320
|
+
}
|
|
321
|
+
startServer(port);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export { createServer, startServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pikakit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "Enterprise-grade Agent Skill Manager with Antigravity Skills support, Progressive Disclosure detection, and semantic routing validation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pikakit <pikakit@gmail.com>",
|
|
@@ -78,4 +78,4 @@
|
|
|
78
78
|
"prettier": "^3.2.5",
|
|
79
79
|
"vitest": "^4.0.18"
|
|
80
80
|
}
|
|
81
|
-
}
|
|
81
|
+
}
|
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Dashboard Server v6.0 - AutoLearn Precision Learning Engine
|
|
4
|
-
*
|
|
5
|
-
* Serves real-time metrics from the new v6.0 modules:
|
|
6
|
-
* - metrics-collector.js (18 KPIs)
|
|
7
|
-
* - dashboard-data.js (aggregation)
|
|
8
|
-
* - causality-engine.js (patterns)
|
|
9
|
-
* - reinforcement.js (loop stats)
|
|
10
|
-
* - ab-testing.js (experiment stats)
|
|
11
|
-
* - precision-skill-generator.js (skills)
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* node dashboard_server.js --start
|
|
15
|
-
* node dashboard_server.js --port 3030
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import fs from 'fs';
|
|
19
|
-
import path from 'path';
|
|
20
|
-
import http from 'http';
|
|
21
|
-
import { fileURLToPath } from 'url';
|
|
22
|
-
|
|
23
|
-
// Import v6.0 modules
|
|
24
|
-
import { getDashboardData, getSummary, getMetricHistory } from '../lib/metrics-collector.js';
|
|
25
|
-
import { getFullDashboardData, getKeyTrends, generateAlerts, getGaugeWidgets, getCounterWidgets } from '../lib/dashboard-data.js';
|
|
26
|
-
import { getReinforcementStats } from '../lib/reinforcement.js';
|
|
27
|
-
import { getABTestStats, getActiveTests } from '../lib/ab-testing.js';
|
|
28
|
-
import { getSkillStats, loadAutoSkills } from '../lib/precision-skill-generator.js';
|
|
29
|
-
import { loadCausalPatterns } from '../lib/causality-engine.js';
|
|
30
|
-
|
|
31
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
-
const __dirname = path.dirname(__filename);
|
|
33
|
-
|
|
34
|
-
// Colors
|
|
35
|
-
const c = {
|
|
36
|
-
reset: '\x1b[0m',
|
|
37
|
-
red: '\x1b[31m',
|
|
38
|
-
green: '\x1b[32m',
|
|
39
|
-
yellow: '\x1b[33m',
|
|
40
|
-
cyan: '\x1b[36m',
|
|
41
|
-
gray: '\x1b[90m',
|
|
42
|
-
bold: '\x1b[1m'
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Find project root
|
|
46
|
-
function findProjectRoot() {
|
|
47
|
-
let current = process.cwd();
|
|
48
|
-
while (current !== path.dirname(current)) {
|
|
49
|
-
if (fs.existsSync(path.join(current, '.agent'))) {
|
|
50
|
-
return current;
|
|
51
|
-
}
|
|
52
|
-
current = path.dirname(current);
|
|
53
|
-
}
|
|
54
|
-
return process.cwd();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const projectRoot = findProjectRoot();
|
|
58
|
-
const dashboardPath = path.join(__dirname, '..', 'dashboard');
|
|
59
|
-
|
|
60
|
-
// ============================================================================
|
|
61
|
-
// API v6.0 HANDLERS
|
|
62
|
-
// ============================================================================
|
|
63
|
-
|
|
64
|
-
const api = {
|
|
65
|
-
// Full dashboard data (all metrics aggregated)
|
|
66
|
-
'/api/dashboard': () => {
|
|
67
|
-
try {
|
|
68
|
-
return getFullDashboardData();
|
|
69
|
-
} catch (e) {
|
|
70
|
-
return { error: e.message, version: '6.0.0' };
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
// KPIs only
|
|
75
|
-
'/api/kpis': () => {
|
|
76
|
-
try {
|
|
77
|
-
return getDashboardData();
|
|
78
|
-
} catch (e) {
|
|
79
|
-
return { error: e.message };
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
// Summary stats
|
|
84
|
-
'/api/summary': () => {
|
|
85
|
-
try {
|
|
86
|
-
return getSummary();
|
|
87
|
-
} catch (e) {
|
|
88
|
-
return { error: e.message };
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
// Trends over time
|
|
93
|
-
'/api/trends': () => {
|
|
94
|
-
try {
|
|
95
|
-
return getKeyTrends();
|
|
96
|
-
} catch (e) {
|
|
97
|
-
return { error: e.message };
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
// Active alerts
|
|
102
|
-
'/api/alerts': () => {
|
|
103
|
-
try {
|
|
104
|
-
return { alerts: generateAlerts() };
|
|
105
|
-
} catch (e) {
|
|
106
|
-
return { alerts: [], error: e.message };
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
// Gauge widget data
|
|
111
|
-
'/api/gauges': () => {
|
|
112
|
-
try {
|
|
113
|
-
return { gauges: getGaugeWidgets() };
|
|
114
|
-
} catch (e) {
|
|
115
|
-
return { gauges: [], error: e.message };
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
// Counter widget data
|
|
120
|
-
'/api/counters': () => {
|
|
121
|
-
try {
|
|
122
|
-
return { counters: getCounterWidgets() };
|
|
123
|
-
} catch (e) {
|
|
124
|
-
return { counters: [], error: e.message };
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
// Reinforcement loop stats
|
|
129
|
-
'/api/reinforcement': () => {
|
|
130
|
-
try {
|
|
131
|
-
return getReinforcementStats();
|
|
132
|
-
} catch (e) {
|
|
133
|
-
return { error: e.message };
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
// A/B testing stats
|
|
138
|
-
'/api/ab-testing': () => {
|
|
139
|
-
try {
|
|
140
|
-
return {
|
|
141
|
-
stats: getABTestStats(),
|
|
142
|
-
active: getActiveTests()
|
|
143
|
-
};
|
|
144
|
-
} catch (e) {
|
|
145
|
-
return { error: e.message };
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
// Auto-generated skills
|
|
150
|
-
'/api/skills': () => {
|
|
151
|
-
try {
|
|
152
|
-
return {
|
|
153
|
-
stats: getSkillStats(),
|
|
154
|
-
skills: loadAutoSkills()
|
|
155
|
-
};
|
|
156
|
-
} catch (e) {
|
|
157
|
-
return { error: e.message };
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
// Causal patterns
|
|
162
|
-
'/api/patterns': () => {
|
|
163
|
-
try {
|
|
164
|
-
const patterns = loadCausalPatterns();
|
|
165
|
-
return {
|
|
166
|
-
total: patterns.length,
|
|
167
|
-
patterns: patterns.slice(0, 20) // Limit to 20 most recent
|
|
168
|
-
};
|
|
169
|
-
} catch (e) {
|
|
170
|
-
return { total: 0, patterns: [], error: e.message };
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
// Metric history (query param: ?metric=task_success_rate&limit=168)
|
|
175
|
-
'/api/history': (query) => {
|
|
176
|
-
try {
|
|
177
|
-
const metric = query.get('metric') || 'task_success_rate';
|
|
178
|
-
const limit = parseInt(query.get('limit') || '168', 10);
|
|
179
|
-
return {
|
|
180
|
-
metric,
|
|
181
|
-
history: getMetricHistory(metric, limit)
|
|
182
|
-
};
|
|
183
|
-
} catch (e) {
|
|
184
|
-
return { error: e.message };
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// ============================================================================
|
|
190
|
-
// LEGACY API COMPATIBILITY (v4.0 endpoints that still work)
|
|
191
|
-
// ============================================================================
|
|
192
|
-
|
|
193
|
-
const legacyApi = {
|
|
194
|
-
'/api/errors': () => ({
|
|
195
|
-
deprecation: 'Use /api/patterns instead',
|
|
196
|
-
redirect: '/api/patterns'
|
|
197
|
-
}),
|
|
198
|
-
'/api/corrections': () => ({
|
|
199
|
-
deprecation: 'Use /api/patterns instead',
|
|
200
|
-
redirect: '/api/patterns'
|
|
201
|
-
}),
|
|
202
|
-
'/api/lessons': () => {
|
|
203
|
-
const filePath = path.join(projectRoot, '.agent', 'knowledge', 'lessons-learned.json');
|
|
204
|
-
try {
|
|
205
|
-
if (fs.existsSync(filePath)) {
|
|
206
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
207
|
-
}
|
|
208
|
-
} catch { }
|
|
209
|
-
return { lessons: [] };
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// MIME types
|
|
214
|
-
const mimeTypes = {
|
|
215
|
-
'.html': 'text/html',
|
|
216
|
-
'.css': 'text/css',
|
|
217
|
-
'.js': 'application/javascript',
|
|
218
|
-
'.json': 'application/json',
|
|
219
|
-
'.png': 'image/png',
|
|
220
|
-
'.jpg': 'image/jpeg',
|
|
221
|
-
'.svg': 'image/svg+xml'
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// Create server
|
|
225
|
-
function createServer(port) {
|
|
226
|
-
const server = http.createServer((req, res) => {
|
|
227
|
-
const urlParts = req.url.split('?');
|
|
228
|
-
const url = urlParts[0];
|
|
229
|
-
const query = new URLSearchParams(urlParts[1] || '');
|
|
230
|
-
|
|
231
|
-
// CORS headers for local development
|
|
232
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
233
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
234
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
235
|
-
|
|
236
|
-
// Handle API requests
|
|
237
|
-
if (url.startsWith('/api/')) {
|
|
238
|
-
const handler = api[url] || legacyApi[url];
|
|
239
|
-
if (handler) {
|
|
240
|
-
res.setHeader('Content-Type', 'application/json');
|
|
241
|
-
res.writeHead(200);
|
|
242
|
-
res.end(JSON.stringify(handler(query)));
|
|
243
|
-
} else {
|
|
244
|
-
res.writeHead(404);
|
|
245
|
-
res.end(JSON.stringify({ error: 'Not found', availableEndpoints: Object.keys(api) }));
|
|
246
|
-
}
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Serve static files
|
|
251
|
-
let filePath = url === '/' ? '/index.html' : url;
|
|
252
|
-
filePath = path.join(dashboardPath, filePath);
|
|
253
|
-
|
|
254
|
-
if (fs.existsSync(filePath)) {
|
|
255
|
-
const ext = path.extname(filePath);
|
|
256
|
-
const mimeType = mimeTypes[ext] || 'text/plain';
|
|
257
|
-
|
|
258
|
-
res.setHeader('Content-Type', mimeType);
|
|
259
|
-
res.writeHead(200);
|
|
260
|
-
res.end(fs.readFileSync(filePath));
|
|
261
|
-
} else {
|
|
262
|
-
res.writeHead(404);
|
|
263
|
-
res.end('Not found');
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
return server;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function startServer(port = 3030) {
|
|
271
|
-
const server = createServer(port);
|
|
272
|
-
|
|
273
|
-
server.listen(port, () => {
|
|
274
|
-
console.log(`${c.cyan}╔════════════════════════════════════════════════════╗${c.reset}`);
|
|
275
|
-
console.log(`${c.cyan}║${c.reset} 🧠 AutoLearn v6.0 Dashboard Server ${c.cyan}║${c.reset}`);
|
|
276
|
-
console.log(`${c.cyan}║${c.reset} ${c.green}Precision Learning Engine${c.reset} ${c.cyan}║${c.reset}`);
|
|
277
|
-
console.log(`${c.cyan}╚════════════════════════════════════════════════════╝${c.reset}\n`);
|
|
278
|
-
console.log(`${c.green}✓ Server running at:${c.reset}`);
|
|
279
|
-
console.log(` ${c.bold}http://localhost:${port}${c.reset}\n`);
|
|
280
|
-
console.log(`${c.gray}API Endpoints (v6.0):${c.reset}`);
|
|
281
|
-
console.log(` GET /api/dashboard - Full dashboard data`);
|
|
282
|
-
console.log(` GET /api/kpis - 18 KPIs`);
|
|
283
|
-
console.log(` GET /api/summary - Summary stats`);
|
|
284
|
-
console.log(` GET /api/trends - Key trends`);
|
|
285
|
-
console.log(` GET /api/alerts - Active alerts`);
|
|
286
|
-
console.log(` GET /api/reinforcement - Reinforcement loop`);
|
|
287
|
-
console.log(` GET /api/ab-testing - A/B experiments`);
|
|
288
|
-
console.log(` GET /api/skills - Auto-generated skills`);
|
|
289
|
-
console.log(` GET /api/patterns - Causal patterns\n`);
|
|
290
|
-
console.log(`${c.yellow}Press Ctrl+C to stop${c.reset}`);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
server.on('error', (err) => {
|
|
294
|
-
if (err.code === 'EADDRINUSE') {
|
|
295
|
-
console.log(`${c.red}Error: Port ${port} is already in use${c.reset}`);
|
|
296
|
-
console.log(`${c.gray}Try: node dashboard_server.js --port ${port + 1}${c.reset}`);
|
|
297
|
-
} else {
|
|
298
|
-
console.error(`${c.red}Server error:${c.reset}`, err);
|
|
299
|
-
}
|
|
300
|
-
process.exit(1);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
return server;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Parse CLI args
|
|
307
|
-
const args = process.argv.slice(2);
|
|
308
|
-
|
|
309
|
-
if (args.includes('--start') || args.includes('-s') || args.length === 0 || args.includes('--port') || args.includes('-p')) {
|
|
310
|
-
let port = 3030;
|
|
311
|
-
const portIdx = args.findIndex(a => a === '--port' || a === '-p');
|
|
312
|
-
if (portIdx >= 0 && args[portIdx + 1]) {
|
|
313
|
-
port = parseInt(args[portIdx + 1], 10);
|
|
314
|
-
}
|
|
315
|
-
startServer(port);
|
|
316
|
-
} else if (args.includes('--help') || args.includes('-h')) {
|
|
317
|
-
console.log(`${c.cyan}AutoLearn v6.0 Dashboard Server${c.reset}
|
|
318
|
-
|
|
319
|
-
${c.bold}Usage:${c.reset}
|
|
320
|
-
node dashboard_server.js Start server (default port 3030)
|
|
321
|
-
node dashboard_server.js --port 8080 Start on custom port
|
|
322
|
-
node dashboard_server.js --help Show this help
|
|
323
|
-
|
|
324
|
-
${c.bold}API Endpoints (v6.0):${c.reset}
|
|
325
|
-
GET /api/dashboard - Full dashboard aggregation
|
|
326
|
-
GET /api/kpis - 18 KPIs for Dashboard
|
|
327
|
-
GET /api/summary - Summary statistics
|
|
328
|
-
GET /api/trends - Week-over-week trends
|
|
329
|
-
GET /api/alerts - Active alerts
|
|
330
|
-
GET /api/gauges - Gauge widget data
|
|
331
|
-
GET /api/counters - Counter widget data
|
|
332
|
-
GET /api/reinforcement - Reinforcement loop stats
|
|
333
|
-
GET /api/ab-testing - A/B testing experiments
|
|
334
|
-
GET /api/skills - Auto-generated skills
|
|
335
|
-
GET /api/patterns - Causal patterns
|
|
336
|
-
GET /api/history?metric=X - Metric history
|
|
337
|
-
|
|
338
|
-
${c.bold}Example:${c.reset}
|
|
339
|
-
node dashboard_server.js --port 3030
|
|
340
|
-
# Open http://localhost:3030 in browser
|
|
341
|
-
`);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export { createServer, startServer };
|