claude-roi 0.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/src/server.js ADDED
@@ -0,0 +1,116 @@
1
+ import express from 'express';
2
+ import { readFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ export function createServer(payload) {
9
+ const app = express();
10
+
11
+ // Serve dashboard HTML
12
+ const dashboardHtml = readFileSync(path.join(__dirname, 'dashboard.html'), 'utf-8');
13
+
14
+ app.get('/', (req, res) => {
15
+ res.type('html').send(dashboardHtml);
16
+ });
17
+
18
+ // Full payload (single fetch for dashboard)
19
+ app.get('/api/all', (req, res) => {
20
+ res.json(payload);
21
+ });
22
+
23
+ // Hero stats + insights
24
+ app.get('/api/summary', (req, res) => {
25
+ res.json({
26
+ ...payload.meta,
27
+ ...payload.summary,
28
+ insights: payload.insights,
29
+ });
30
+ });
31
+
32
+ // Daily timeline data for charts
33
+ app.get('/api/timeline', (req, res) => {
34
+ res.json(payload.daily);
35
+ });
36
+
37
+ // All sessions with pagination
38
+ app.get('/api/sessions', (req, res) => {
39
+ const page = parseInt(req.query.page) || 1;
40
+ const limit = parseInt(req.query.limit) || 50;
41
+ const sortBy = req.query.sort || 'startTime';
42
+ const order = req.query.order === 'asc' ? 1 : -1;
43
+ const mainOnly = req.query.mainOnly === 'true';
44
+
45
+ let sessions = payload.sessions;
46
+
47
+ // Filter to main branch commits only if requested
48
+ if (mainOnly) {
49
+ sessions = sessions.map(s => {
50
+ const mainCommits = s.commits.filter(c => c.onMain);
51
+ return {
52
+ ...s,
53
+ commits: mainCommits,
54
+ commitCount: mainCommits.length,
55
+ linesAdded: mainCommits.reduce((sum, c) => sum + c.totalAdded, 0),
56
+ linesDeleted: mainCommits.reduce((sum, c) => sum + c.totalDeleted, 0),
57
+ };
58
+ });
59
+ }
60
+
61
+ // Sort
62
+ sessions = [...sessions].sort((a, b) => {
63
+ const av = a[sortBy] ?? 0;
64
+ const bv = b[sortBy] ?? 0;
65
+ if (typeof av === 'string') return order * av.localeCompare(bv);
66
+ return order * (av - bv);
67
+ });
68
+
69
+ const start = (page - 1) * limit;
70
+ res.json({
71
+ sessions: sessions.slice(start, start + limit),
72
+ total: sessions.length,
73
+ page,
74
+ limit,
75
+ });
76
+ });
77
+
78
+ // Model comparison data
79
+ app.get('/api/models', (req, res) => {
80
+ res.json(payload.modelBreakdown);
81
+ });
82
+
83
+ // Hour x day heatmap
84
+ app.get('/api/heatmap', (req, res) => {
85
+ res.json(payload.heatmap);
86
+ });
87
+
88
+ // Single session detail
89
+ app.get('/api/session/:id', (req, res) => {
90
+ const session = payload.sessions.find(s => s.sessionId === req.params.id);
91
+ if (!session) return res.status(404).json({ error: 'Session not found' });
92
+ res.json(session);
93
+ });
94
+
95
+ // Projects breakdown
96
+ app.get('/api/projects', (req, res) => {
97
+ res.json(payload.projects);
98
+ });
99
+
100
+ // Session buckets
101
+ app.get('/api/buckets', (req, res) => {
102
+ res.json(payload.sessionBuckets);
103
+ });
104
+
105
+ // Tool usage
106
+ app.get('/api/tools', (req, res) => {
107
+ res.json(payload.toolBreakdown);
108
+ });
109
+
110
+ // Line survival
111
+ app.get('/api/survival', (req, res) => {
112
+ res.json(payload.lineSurvival);
113
+ });
114
+
115
+ return app;
116
+ }