agentlytics 0.1.5 → 0.1.7
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 +1 -1
- package/cache.js +440 -9
- package/editors/cursor.js +28 -6
- package/editors/vscode.js +6 -0
- package/package.json +1 -1
- package/server.js +84 -4
- package/ui/package-lock.json +60 -375
- package/ui/package.json +1 -1
- package/ui/src/App.jsx +14 -1
- package/ui/src/lib/api.js +39 -0
- package/ui/src/lib/constants.js +8 -0
- package/ui/src/pages/ChatDetail.jsx +5 -2
- package/ui/src/pages/CostAnalysis.jsx +356 -0
- package/ui/src/pages/Dashboard.jsx +29 -8
- package/ui/src/pages/DeepAnalysis.jsx +11 -4
- package/ui/src/pages/ProjectDetail.jsx +12 -4
- package/ui/src/pages/Projects.jsx +9 -3
- package/ui/src/pages/Sessions.jsx +5 -1
- package/ui/src/pages/Settings.jsx +142 -0
package/editors/vscode.js
CHANGED
|
@@ -118,6 +118,7 @@ function parseSessionFile(filePath) {
|
|
|
118
118
|
title: state.customTitle || null,
|
|
119
119
|
requests: state.requests || [],
|
|
120
120
|
format: 'jsonl',
|
|
121
|
+
selectedModel: state.inputState?.selectedModel?.metadata?.id || null,
|
|
121
122
|
};
|
|
122
123
|
} else if (ext === '.json') {
|
|
123
124
|
let data;
|
|
@@ -128,6 +129,7 @@ function parseSessionFile(filePath) {
|
|
|
128
129
|
title: data.customTitle || null,
|
|
129
130
|
requests: data.requests || [],
|
|
130
131
|
format: 'json',
|
|
132
|
+
selectedModel: data.inputState?.selectedModel?.metadata?.id || null,
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
return null;
|
|
@@ -204,6 +206,7 @@ function collectSessions(dir, folder, source, chats) {
|
|
|
204
206
|
encrypted: false,
|
|
205
207
|
bubbleCount: meta.requestCount || 0,
|
|
206
208
|
_filePath: filePath,
|
|
209
|
+
_modelPref: meta.selectedModel || null,
|
|
207
210
|
});
|
|
208
211
|
} catch { /* skip */ }
|
|
209
212
|
}
|
|
@@ -221,6 +224,7 @@ function peekMeta(filePath) {
|
|
|
221
224
|
createdAt: data.creationDate || data.lastMessageDate,
|
|
222
225
|
requestCount: data.requests?.length || 0,
|
|
223
226
|
firstUserText: firstText.substring(0, 120) || null,
|
|
227
|
+
selectedModel: data.inputState?.selectedModel?.metadata?.id || null,
|
|
224
228
|
};
|
|
225
229
|
} catch { return {}; }
|
|
226
230
|
}
|
|
@@ -251,6 +255,7 @@ function peekMeta(filePath) {
|
|
|
251
255
|
createdAt: state.creationDate,
|
|
252
256
|
requestCount: null,
|
|
253
257
|
firstUserText: null,
|
|
258
|
+
selectedModel: state.inputState?.selectedModel?.metadata?.id || null,
|
|
254
259
|
};
|
|
255
260
|
} catch { return {}; }
|
|
256
261
|
}
|
|
@@ -301,6 +306,7 @@ function getMessages(chat) {
|
|
|
301
306
|
const meta = req.result?.metadata;
|
|
302
307
|
messages.push({
|
|
303
308
|
role: 'assistant', content: responseText,
|
|
309
|
+
_model: parsed.selectedModel || null,
|
|
304
310
|
_inputTokens: meta?.promptTokens, _outputTokens: meta?.outputTokens,
|
|
305
311
|
_toolCalls,
|
|
306
312
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Comprehensive analytics dashboard for AI coding agents — Cursor, Windsurf, Claude Code, VS Code Copilot, Zed, Antigravity, OpenCode, Command Code",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/server.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
3
5
|
const cache = require('./cache');
|
|
4
6
|
const { generateShareSvg } = require('./share-image');
|
|
5
7
|
|
|
@@ -7,6 +9,26 @@ const app = express();
|
|
|
7
9
|
app.use(express.json());
|
|
8
10
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
9
11
|
|
|
12
|
+
// ============================================================
|
|
13
|
+
// Config: ~/.agentlytics/config.json
|
|
14
|
+
// ============================================================
|
|
15
|
+
|
|
16
|
+
const CONFIG_PATH = path.join(os.homedir(), '.agentlytics', 'config.json');
|
|
17
|
+
|
|
18
|
+
function readConfig() {
|
|
19
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); } catch { return {}; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function writeConfig(config) {
|
|
23
|
+
const dir = path.dirname(CONFIG_PATH);
|
|
24
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getHiddenFolders() {
|
|
29
|
+
return readConfig().hiddenProjects || [];
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
// ============================================================
|
|
11
33
|
// API endpoints — all reads from SQLite cache
|
|
12
34
|
// ============================================================
|
|
@@ -25,7 +47,7 @@ app.get('/api/mode', (req, res) => {
|
|
|
25
47
|
|
|
26
48
|
app.get('/api/overview', (req, res) => {
|
|
27
49
|
try {
|
|
28
|
-
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query) };
|
|
50
|
+
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query), hiddenFolders: getHiddenFolders() };
|
|
29
51
|
res.json(cache.getCachedOverview(opts));
|
|
30
52
|
} catch (err) {
|
|
31
53
|
res.status(500).json({ error: err.message });
|
|
@@ -34,7 +56,7 @@ app.get('/api/overview', (req, res) => {
|
|
|
34
56
|
|
|
35
57
|
app.get('/api/daily-activity', (req, res) => {
|
|
36
58
|
try {
|
|
37
|
-
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query) };
|
|
59
|
+
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query), hiddenFolders: getHiddenFolders() };
|
|
38
60
|
res.json(cache.getCachedDailyActivity(opts));
|
|
39
61
|
} catch (err) {
|
|
40
62
|
res.status(500).json({ error: err.message });
|
|
@@ -50,6 +72,7 @@ app.get('/api/chats', (req, res) => {
|
|
|
50
72
|
limit: req.query.limit ? parseInt(req.query.limit) : 200,
|
|
51
73
|
offset: req.query.offset ? parseInt(req.query.offset) : 0,
|
|
52
74
|
...parseDateOpts(req.query),
|
|
75
|
+
hiddenFolders: getHiddenFolders(),
|
|
53
76
|
};
|
|
54
77
|
const total = cache.countCachedChats(opts);
|
|
55
78
|
const rows = cache.getCachedChats(opts);
|
|
@@ -66,6 +89,7 @@ app.get('/api/chats', (req, res) => {
|
|
|
66
89
|
encrypted: !!c.encrypted,
|
|
67
90
|
bubbleCount: c.bubble_count,
|
|
68
91
|
topModel: c.top_model || null,
|
|
92
|
+
cost: c.cost || 0,
|
|
69
93
|
})),
|
|
70
94
|
});
|
|
71
95
|
} catch (err) {
|
|
@@ -130,7 +154,7 @@ app.get('/api/chats/:id/markdown', (req, res) => {
|
|
|
130
154
|
|
|
131
155
|
app.get('/api/projects', (req, res) => {
|
|
132
156
|
try {
|
|
133
|
-
res.json(cache.getCachedProjects(parseDateOpts(req.query)));
|
|
157
|
+
res.json(cache.getCachedProjects({ ...parseDateOpts(req.query), hiddenFolders: getHiddenFolders() }));
|
|
134
158
|
} catch (err) {
|
|
135
159
|
res.status(500).json({ error: err.message });
|
|
136
160
|
}
|
|
@@ -143,6 +167,7 @@ app.get('/api/deep-analytics', (req, res) => {
|
|
|
143
167
|
folder: req.query.folder || null,
|
|
144
168
|
limit: Math.min(parseInt(req.query.limit) || 500, 5000),
|
|
145
169
|
...parseDateOpts(req.query),
|
|
170
|
+
hiddenFolders: getHiddenFolders(),
|
|
146
171
|
};
|
|
147
172
|
res.json(cache.getCachedDeepAnalytics(opts));
|
|
148
173
|
} catch (err) {
|
|
@@ -152,13 +177,41 @@ app.get('/api/deep-analytics', (req, res) => {
|
|
|
152
177
|
|
|
153
178
|
app.get('/api/dashboard-stats', (req, res) => {
|
|
154
179
|
try {
|
|
155
|
-
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query) };
|
|
180
|
+
const opts = { editor: req.query.editor || null, ...parseDateOpts(req.query), hiddenFolders: getHiddenFolders() };
|
|
156
181
|
res.json(cache.getCachedDashboardStats(opts));
|
|
157
182
|
} catch (err) {
|
|
158
183
|
res.status(500).json({ error: err.message });
|
|
159
184
|
}
|
|
160
185
|
});
|
|
161
186
|
|
|
187
|
+
app.get('/api/cost-analytics', (req, res) => {
|
|
188
|
+
try {
|
|
189
|
+
const opts = {
|
|
190
|
+
editor: req.query.editor || null,
|
|
191
|
+
...parseDateOpts(req.query),
|
|
192
|
+
hiddenFolders: getHiddenFolders(),
|
|
193
|
+
};
|
|
194
|
+
res.json(cache.getCostAnalytics(opts));
|
|
195
|
+
} catch (err) {
|
|
196
|
+
res.status(500).json({ error: err.message });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
app.get('/api/costs', (req, res) => {
|
|
201
|
+
try {
|
|
202
|
+
const opts = {
|
|
203
|
+
editor: req.query.editor || null,
|
|
204
|
+
folder: req.query.folder || null,
|
|
205
|
+
chatId: req.query.chatId || null,
|
|
206
|
+
...parseDateOpts(req.query),
|
|
207
|
+
hiddenFolders: getHiddenFolders(),
|
|
208
|
+
};
|
|
209
|
+
res.json(cache.getCostBreakdown(opts));
|
|
210
|
+
} catch (err) {
|
|
211
|
+
res.status(500).json({ error: err.message });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
162
215
|
app.get('/api/tool-calls', (req, res) => {
|
|
163
216
|
try {
|
|
164
217
|
const name = req.query.name;
|
|
@@ -239,6 +292,33 @@ app.get('/api/refetch', async (req, res) => {
|
|
|
239
292
|
res.end();
|
|
240
293
|
});
|
|
241
294
|
|
|
295
|
+
// ============================================================
|
|
296
|
+
// Config endpoints
|
|
297
|
+
// ============================================================
|
|
298
|
+
|
|
299
|
+
app.get('/api/config', (req, res) => {
|
|
300
|
+
res.json(readConfig());
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
app.put('/api/config', (req, res) => {
|
|
304
|
+
try {
|
|
305
|
+
const config = readConfig();
|
|
306
|
+
Object.assign(config, req.body);
|
|
307
|
+
writeConfig(config);
|
|
308
|
+
res.json(config);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
res.status(500).json({ error: err.message });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
app.get('/api/all-projects', (req, res) => {
|
|
315
|
+
try {
|
|
316
|
+
res.json(cache.getCachedProjects({ ...parseDateOpts(req.query), includeHidden: true }));
|
|
317
|
+
} catch (err) {
|
|
318
|
+
res.status(500).json({ error: err.message });
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
242
322
|
// SPA fallback
|
|
243
323
|
app.get('*', (req, res) => {
|
|
244
324
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|