agentlytics 0.1.3 → 0.1.6
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 +420 -10
- package/editors/cursor.js +28 -6
- package/editors/vscode.js +6 -0
- package/index.js +78 -11
- package/package.json +2 -1
- package/server.js +27 -0
- package/ui/package-lock.json +60 -375
- package/ui/package.json +1 -1
- package/ui/src/App.jsx +22 -17
- package/ui/src/components/ActivityHeatmap.jsx +3 -3
- package/ui/src/components/AnimatedLogo.jsx +96 -0
- package/ui/src/components/ChatSidebar.jsx +7 -7
- package/ui/src/components/DateRangePicker.jsx +5 -5
- package/ui/src/components/EditorBreakdown.jsx +2 -2
- package/ui/src/components/EditorDot.jsx +1 -1
- package/ui/src/components/KpiCard.jsx +2 -2
- package/ui/src/components/LiveFeed.jsx +8 -8
- package/ui/src/components/LoginScreen.jsx +8 -6
- package/ui/src/components/MessageRenderer.jsx +5 -5
- package/ui/src/components/ModelBreakdown.jsx +3 -3
- package/ui/src/components/SectionTitle.jsx +1 -1
- package/ui/src/index.css +1 -1
- package/ui/src/lib/api.js +20 -0
- package/ui/src/lib/constants.js +8 -0
- package/ui/src/pages/ChatDetail.jsx +5 -2
- package/ui/src/pages/Compare.jsx +18 -18
- package/ui/src/pages/CostAnalysis.jsx +356 -0
- package/ui/src/pages/Dashboard.jsx +39 -21
- package/ui/src/pages/DeepAnalysis.jsx +38 -31
- package/ui/src/pages/ProjectDetail.jsx +23 -15
- package/ui/src/pages/Projects.jsx +14 -8
- package/ui/src/pages/RelayDashboard.jsx +29 -29
- package/ui/src/pages/RelaySessionDetail.jsx +1 -1
- package/ui/src/pages/RelayUserDetail.jsx +18 -18
- package/ui/src/pages/Sessions.jsx +24 -20
- package/ui/src/pages/SqlViewer.jsx +14 -14
package/index.js
CHANGED
|
@@ -123,9 +123,11 @@ function getLocalIp() {
|
|
|
123
123
|
return 'localhost';
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// ── ASCII banner ─────────────────────────────────────────
|
|
127
|
+
const c1 = chalk.hex('#818cf8'), c2 = chalk.hex('#f472b6'), c3 = chalk.hex('#34d399'), c4 = chalk.hex('#fbbf24');
|
|
126
128
|
console.log('');
|
|
127
|
-
console.log(chalk.bold('
|
|
128
|
-
console.log(chalk.dim('
|
|
129
|
+
console.log(` ${c1('(● ●)')} ${c2('[● ●]')} ${chalk.bold('Agentlytics')}`);
|
|
130
|
+
console.log(` ${c3('{● ●}')} ${c4('<● ●>')} ${chalk.dim('Unified analytics for your AI coding agents')}`);
|
|
129
131
|
if (collectOnly) console.log(chalk.cyan(' ⟳ Collect-only mode (no server)'));
|
|
130
132
|
console.log('');
|
|
131
133
|
|
|
@@ -164,6 +166,10 @@ if (noCache) {
|
|
|
164
166
|
const cacheDb = path.join(os.homedir(), '.agentlytics', 'cache.db');
|
|
165
167
|
if (fs.existsSync(cacheDb)) {
|
|
166
168
|
fs.unlinkSync(cacheDb);
|
|
169
|
+
// Remove WAL/SHM journal files to avoid SQLITE_IOERR_SHORT_READ
|
|
170
|
+
for (const suffix of ['-wal', '-shm']) {
|
|
171
|
+
if (fs.existsSync(cacheDb + suffix)) fs.unlinkSync(cacheDb + suffix);
|
|
172
|
+
}
|
|
167
173
|
console.log(chalk.yellow(' ⟳ Cache cleared (--no-cache)'));
|
|
168
174
|
}
|
|
169
175
|
}
|
|
@@ -204,18 +210,74 @@ const WINDSURF_VARIANTS = [
|
|
|
204
210
|
})();
|
|
205
211
|
|
|
206
212
|
// Initialize cache DB
|
|
207
|
-
console.log(chalk.dim(' Initializing cache database...'));
|
|
208
213
|
cache.initDb();
|
|
209
214
|
|
|
210
|
-
//
|
|
211
|
-
|
|
215
|
+
// ── Detect editors & collect sessions ───────────────────────
|
|
216
|
+
const { editors: editorModules } = require('./editors');
|
|
217
|
+
const EDITOR_DISPLAY = [
|
|
218
|
+
['cursor', 'Cursor'],
|
|
219
|
+
['windsurf', 'Windsurf'],
|
|
220
|
+
['windsurf-next', 'Windsurf Next'],
|
|
221
|
+
['antigravity', 'Antigravity'],
|
|
222
|
+
['claude-code', 'Claude Code'],
|
|
223
|
+
['vscode', 'VS Code'],
|
|
224
|
+
['vscode-insiders', 'VS Code Insiders'],
|
|
225
|
+
['zed', 'Zed'],
|
|
226
|
+
['opencode', 'OpenCode'],
|
|
227
|
+
['codex', 'Codex'],
|
|
228
|
+
['gemini-cli', 'Gemini CLI'],
|
|
229
|
+
['copilot-cli', 'Copilot CLI'],
|
|
230
|
+
['cursor-agent', 'Cursor Agent'],
|
|
231
|
+
['commandcode', 'Command Code'],
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
console.log(chalk.dim(' Looking for AI coding agents...'));
|
|
235
|
+
const allChats = [];
|
|
236
|
+
for (const editor of editorModules) {
|
|
237
|
+
try {
|
|
238
|
+
const chats = editor.getChats();
|
|
239
|
+
allChats.push(...chats);
|
|
240
|
+
} catch { /* skip broken adapters */ }
|
|
241
|
+
}
|
|
242
|
+
allChats.sort((a, b) => (b.lastUpdatedAt || b.createdAt || 0) - (a.lastUpdatedAt || a.createdAt || 0));
|
|
243
|
+
|
|
244
|
+
// Count per source
|
|
245
|
+
const bySource = {};
|
|
246
|
+
for (const chat of allChats) bySource[chat.source] = (bySource[chat.source] || 0) + 1;
|
|
247
|
+
|
|
248
|
+
for (const [src, label] of EDITOR_DISPLAY) {
|
|
249
|
+
const count = bySource[src] || 0;
|
|
250
|
+
if (count > 0) {
|
|
251
|
+
console.log(` ${chalk.green('✓')} ${chalk.bold(label.padEnd(18))} ${chalk.dim(`${count} session${count === 1 ? '' : 's'}`)}`);
|
|
252
|
+
} else {
|
|
253
|
+
console.log(` ${chalk.dim('–')} ${chalk.dim(label.padEnd(18) + '–')}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
console.log('');
|
|
257
|
+
|
|
258
|
+
// ── Analyze sessions with robot animation ──────────────────
|
|
259
|
+
const logUpdate = require('log-update');
|
|
260
|
+
const BOT_STYLES = [
|
|
261
|
+
{ l: '(', r: ')', color: '#818cf8' },
|
|
262
|
+
{ l: '[', r: ']', color: '#f472b6' },
|
|
263
|
+
{ l: '{', r: '}', color: '#34d399' },
|
|
264
|
+
{ l: '<', r: '>', color: '#fbbf24' },
|
|
265
|
+
];
|
|
266
|
+
let tick = 0;
|
|
267
|
+
|
|
212
268
|
const startTime = Date.now();
|
|
213
|
-
const result = cache.scanAll((
|
|
214
|
-
|
|
215
|
-
|
|
269
|
+
const result = cache.scanAll((p) => {
|
|
270
|
+
tick++;
|
|
271
|
+
if (tick % 5 !== 0) return;
|
|
272
|
+
const frame = Math.floor(tick / 40);
|
|
273
|
+
const b = BOT_STYLES[frame % 4];
|
|
274
|
+
const dots = '.'.repeat((Math.floor(tick / 10) % 3) + 1).padEnd(3);
|
|
275
|
+
logUpdate(` ${chalk.hex(b.color)(`${b.l}● ●${b.r}`)} ${chalk.dim(`Analyzing${dots} ${p.scanned}/${p.total}`)}`);
|
|
276
|
+
}, { chats: allChats });
|
|
216
277
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
217
|
-
|
|
218
|
-
|
|
278
|
+
const allFaces = BOT_STYLES.map(b => chalk.hex(b.color)(`${b.l}● ●${b.r}`)).join(' ');
|
|
279
|
+
logUpdate(` ${allFaces} ${chalk.green(`✓ ${result.analyzed} analyzed, ${result.skipped} cached (${elapsed}s)`)}`);
|
|
280
|
+
logUpdate.done();
|
|
219
281
|
console.log('');
|
|
220
282
|
|
|
221
283
|
// In collect-only mode, exit after cache is built
|
|
@@ -231,7 +293,12 @@ const app = require('./server');
|
|
|
231
293
|
app.listen(PORT, () => {
|
|
232
294
|
const url = `http://localhost:${PORT}`;
|
|
233
295
|
console.log(chalk.green(` ✓ Dashboard ready at ${chalk.bold.white(url)}`));
|
|
234
|
-
console.log(
|
|
296
|
+
console.log('');
|
|
297
|
+
console.log(chalk.dim(' 💡 Share sessions with your team:'));
|
|
298
|
+
console.log(chalk.dim(` npx agentlytics --relay Start a relay server`));
|
|
299
|
+
console.log(chalk.dim(` npx agentlytics --join <host:port> --username Join a relay server`));
|
|
300
|
+
console.log('');
|
|
301
|
+
console.log(chalk.dim(' Press Ctrl+C to stop\n'));
|
|
235
302
|
|
|
236
303
|
// Auto-open browser
|
|
237
304
|
const open = require('open');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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": {
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"commander": "^14.0.3",
|
|
56
56
|
"express": "^4.22.1",
|
|
57
57
|
"inquirer": "^13.3.0",
|
|
58
|
+
"log-update": "^4.0.0",
|
|
58
59
|
"open": "^8.4.2"
|
|
59
60
|
}
|
|
60
61
|
}
|
package/server.js
CHANGED
|
@@ -66,6 +66,7 @@ app.get('/api/chats', (req, res) => {
|
|
|
66
66
|
encrypted: !!c.encrypted,
|
|
67
67
|
bubbleCount: c.bubble_count,
|
|
68
68
|
topModel: c.top_model || null,
|
|
69
|
+
cost: c.cost || 0,
|
|
69
70
|
})),
|
|
70
71
|
});
|
|
71
72
|
} catch (err) {
|
|
@@ -159,6 +160,32 @@ app.get('/api/dashboard-stats', (req, res) => {
|
|
|
159
160
|
}
|
|
160
161
|
});
|
|
161
162
|
|
|
163
|
+
app.get('/api/cost-analytics', (req, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const opts = {
|
|
166
|
+
editor: req.query.editor || null,
|
|
167
|
+
...parseDateOpts(req.query),
|
|
168
|
+
};
|
|
169
|
+
res.json(cache.getCostAnalytics(opts));
|
|
170
|
+
} catch (err) {
|
|
171
|
+
res.status(500).json({ error: err.message });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
app.get('/api/costs', (req, res) => {
|
|
176
|
+
try {
|
|
177
|
+
const opts = {
|
|
178
|
+
editor: req.query.editor || null,
|
|
179
|
+
folder: req.query.folder || null,
|
|
180
|
+
chatId: req.query.chatId || null,
|
|
181
|
+
...parseDateOpts(req.query),
|
|
182
|
+
};
|
|
183
|
+
res.json(cache.getCostBreakdown(opts));
|
|
184
|
+
} catch (err) {
|
|
185
|
+
res.status(500).json({ error: err.message });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
162
189
|
app.get('/api/tool-calls', (req, res) => {
|
|
163
190
|
try {
|
|
164
191
|
const name = req.query.name;
|