agentlytics 0.2.8 → 0.2.10
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 +60 -1
- package/deno.json +9 -0
- package/index.js +68 -8
- package/mod.ts +1020 -0
- package/package.json +7 -2
- package/relay-server.js +1 -1
- package/ui/package.json +1 -1
- package/ui/src/App.jsx +13 -8
- package/ui/src/hooks/useLive.jsx +15 -0
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<a href="#supported-editors"><img src="https://img.shields.io/badge/editors-16-818cf8" alt="editors"></a>
|
|
15
15
|
<a href="#license"><img src="https://img.shields.io/badge/license-MIT-green" alt="license"></a>
|
|
16
16
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%E2%89%A520.19%20%7C%20%E2%89%A522.12-brightgreen" alt="node"></a>
|
|
17
|
+
<a href="https://deno.land"><img src="https://img.shields.io/badge/deno-%E2%89%A52.0-000?logo=deno" alt="deno"></a>
|
|
17
18
|
</p>
|
|
18
19
|
|
|
19
20
|
<p align="center">
|
|
@@ -39,10 +40,61 @@ You switch between Cursor, Windsurf, Claude Code, VS Code Copilot, and more —
|
|
|
39
40
|
|
|
40
41
|
```bash
|
|
41
42
|
npx agentlytics
|
|
43
|
+
# or
|
|
44
|
+
pnpm dlx agentlytics
|
|
45
|
+
# or
|
|
46
|
+
yarn dlx agentlytics
|
|
47
|
+
# or
|
|
48
|
+
bunx agentlytics
|
|
42
49
|
```
|
|
43
50
|
|
|
44
51
|
Opens at **http://localhost:4637**. Requires Node.js ≥ 20.19 or ≥ 22.12, macOS. No data ever leaves your machine.
|
|
45
52
|
|
|
53
|
+
### Deno (Sandboxed)
|
|
54
|
+
|
|
55
|
+
Run a lightweight, zero-dependency analytics scan with Deno's permission sandbox — directly from a URL, no install needed:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
deno run --allow-read --allow-env https://raw.githubusercontent.com/f/agentlytics/master/mod.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Only `--allow-read` and `--allow-env` are required. No network access, no file writes, no code execution — just reads your local editor data and prints a summary.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
(● ●) [● ●] Agentlytics — Deno Sandboxed Edition
|
|
65
|
+
{● ●} <● ●> Lightweight CLI analytics for AI coding agents
|
|
66
|
+
|
|
67
|
+
✓ Claude Code 8 sessions
|
|
68
|
+
✓ VS Code 23 sessions
|
|
69
|
+
✓ VS Code Insiders 66 sessions
|
|
70
|
+
● Cursor detected
|
|
71
|
+
✓ Codex CLI 3 sessions
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
Summary
|
|
75
|
+
Sessions 109
|
|
76
|
+
Messages 459
|
|
77
|
+
Projects 18
|
|
78
|
+
Editors 7 of 15 checked
|
|
79
|
+
Date range 2025-04-02 → 2026-03-09
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Add `--json` for machine-readable output:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
deno run --allow-read --allow-env mod.ts --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If you've cloned the repo, you can also use Deno tasks for the full dashboard:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
deno task start # Full dashboard (all permissions)
|
|
92
|
+
deno task scan # Lightweight CLI scan
|
|
93
|
+
deno task scan:json # JSON output
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Node.js
|
|
97
|
+
|
|
46
98
|
```
|
|
47
99
|
$ npx agentlytics
|
|
48
100
|
|
|
@@ -68,6 +120,7 @@ To only build the cache without starting the server:
|
|
|
68
120
|
|
|
69
121
|
```bash
|
|
70
122
|
npx agentlytics --collect
|
|
123
|
+
# or: pnpm dlx agentlytics --collect
|
|
71
124
|
```
|
|
72
125
|
|
|
73
126
|
## Features
|
|
@@ -112,6 +165,7 @@ Relay enables multi-user context sharing across a team. One person starts a rela
|
|
|
112
165
|
|
|
113
166
|
```bash
|
|
114
167
|
npx agentlytics --relay
|
|
168
|
+
# or: pnpm dlx agentlytics --relay
|
|
115
169
|
```
|
|
116
170
|
|
|
117
171
|
Optionally protect with a password:
|
|
@@ -138,6 +192,7 @@ This starts a relay server on port `4638` and prints the join command and MCP en
|
|
|
138
192
|
```bash
|
|
139
193
|
cd /path/to/your-project
|
|
140
194
|
npx agentlytics --join <host:port>
|
|
195
|
+
# or: pnpm dlx agentlytics --join <host:port>
|
|
141
196
|
```
|
|
142
197
|
|
|
143
198
|
If the relay is password-protected:
|
|
@@ -186,7 +241,11 @@ Editor files/APIs → editors/*.js → cache.js (SQLite) → server.js (REST)
|
|
|
186
241
|
Relay: join clients → POST /relay/sync → relay.db (SQLite) → MCP server → AI clients
|
|
187
242
|
```
|
|
188
243
|
|
|
189
|
-
|
|
244
|
+
```
|
|
245
|
+
Deno: Editor files → mod.ts (zero deps) → stdout (CLI/JSON)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
All data is normalized into a local SQLite cache at `~/.agentlytics/cache.db`. The Express server exposes read-only REST endpoints consumed by the React frontend. Relay data is stored separately in `~/.agentlytics/relay.db`. The Deno sandboxed edition (`mod.ts`) bypasses SQLite entirely and reads editor files directly for a lightweight, permission-minimal CLI report.
|
|
190
249
|
|
|
191
250
|
## API
|
|
192
251
|
|
package/deno.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tasks": {
|
|
3
|
+
"start": "deno run --allow-read --allow-write --allow-net --allow-env --allow-ffi --allow-run index.js",
|
|
4
|
+
"collect": "deno run --allow-read --allow-write --allow-env --allow-ffi index.js --collect",
|
|
5
|
+
"scan": "deno run --allow-read --allow-env mod.ts",
|
|
6
|
+
"scan:json": "deno run --allow-read --allow-env mod.ts --json"
|
|
7
|
+
},
|
|
8
|
+
"nodeModulesDir": "auto"
|
|
9
|
+
}
|
package/index.js
CHANGED
|
@@ -6,11 +6,44 @@ const path = require('path');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const { execSync } = require('child_process');
|
|
8
8
|
|
|
9
|
+
// ── Node.js version check ─────────────────────────────────
|
|
10
|
+
const nodeVersion = process.versions.node;
|
|
11
|
+
const [nodeMajor, nodeMinor] = nodeVersion.split('.').map(Number);
|
|
12
|
+
const isNodeSupported =
|
|
13
|
+
(nodeMajor === 20 && nodeMinor >= 19) ||
|
|
14
|
+
(nodeMajor === 22 && nodeMinor >= 12) ||
|
|
15
|
+
nodeMajor === 23 || nodeMajor === 24 || nodeMajor >= 25;
|
|
16
|
+
|
|
17
|
+
if (!isNodeSupported) {
|
|
18
|
+
console.error('');
|
|
19
|
+
console.error(` \x1b[31m✗ Unsupported Node.js version: v${nodeVersion}\x1b[0m`);
|
|
20
|
+
console.error('');
|
|
21
|
+
console.error(` \x1b[1mAgentlytics requires Node.js 20.19+ or 22.12+\x1b[0m`);
|
|
22
|
+
console.error(` \x1b[2mYour current version: v${nodeVersion}\x1b[0m`);
|
|
23
|
+
console.error('');
|
|
24
|
+
console.error(` \x1b[2mTo upgrade, visit: https://nodejs.org\x1b[0m`);
|
|
25
|
+
console.error(` \x1b[2mOr use a version manager: nvm install 22\x1b[0m`);
|
|
26
|
+
console.error('');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
const HOME = os.homedir();
|
|
31
|
+
|
|
32
|
+
// ── Detect package manager ─────────────────────────────────
|
|
33
|
+
function detectPackageManager() {
|
|
34
|
+
const ua = process.env.npm_config_user_agent || '';
|
|
35
|
+
if (ua.startsWith('pnpm/')) return 'pnpm';
|
|
36
|
+
if (ua.startsWith('yarn/')) return 'yarn';
|
|
37
|
+
if (ua.startsWith('bun/')) return 'bun';
|
|
38
|
+
return 'npm';
|
|
39
|
+
}
|
|
40
|
+
const PM = detectPackageManager();
|
|
41
|
+
const PM_RUN = PM === 'npm' ? 'npx' : PM === 'pnpm' ? 'pnpm dlx' : PM === 'yarn' ? 'yarn dlx' : 'bunx';
|
|
10
42
|
const PORT = process.env.PORT || 4637;
|
|
11
43
|
const RELAY_PORT = process.env.RELAY_PORT || 4638;
|
|
12
44
|
const noCache = process.argv.includes('--no-cache');
|
|
13
45
|
const collectOnly = process.argv.includes('--collect');
|
|
46
|
+
const isUiDev = process.argv.includes('--ui-dev');
|
|
14
47
|
const isRelay = process.argv.includes('--relay');
|
|
15
48
|
const joinIndex = process.argv.indexOf('--join');
|
|
16
49
|
const isJoin = joinIndex !== -1;
|
|
@@ -47,7 +80,7 @@ if (isRelay) {
|
|
|
47
80
|
console.log('');
|
|
48
81
|
console.log(chalk.bold(' Share this command with your team:'));
|
|
49
82
|
console.log('');
|
|
50
|
-
console.log(chalk.cyan(`
|
|
83
|
+
console.log(chalk.cyan(` ${PM_RUN} agentlytics --join ${localIp}:${RELAY_PORT} --username <name>`));
|
|
51
84
|
console.log('');
|
|
52
85
|
console.log(chalk.bold(' MCP server endpoint (add to your AI client):'));
|
|
53
86
|
console.log('');
|
|
@@ -76,7 +109,7 @@ if (isJoin) {
|
|
|
76
109
|
let username = usernameIndex !== -1 ? process.argv[usernameIndex + 1] : null;
|
|
77
110
|
|
|
78
111
|
if (!relayAddress) {
|
|
79
|
-
console.error(chalk.red(
|
|
112
|
+
console.error(chalk.red(`\n ✗ Missing relay address. Usage: ${PM_RUN} agentlytics --join <host:port> --username <name>\n`));
|
|
80
113
|
process.exit(1);
|
|
81
114
|
}
|
|
82
115
|
|
|
@@ -135,15 +168,20 @@ console.log('');
|
|
|
135
168
|
const publicIndex = path.join(__dirname, 'public', 'index.html');
|
|
136
169
|
const uiDir = path.join(__dirname, 'ui');
|
|
137
170
|
|
|
138
|
-
if (!collectOnly && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
171
|
+
if (!collectOnly && !isUiDev && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
139
172
|
console.log(chalk.cyan(' ⟳ Building dashboard UI (first run)...'));
|
|
140
173
|
try {
|
|
141
174
|
const uiModules = path.join(uiDir, 'node_modules');
|
|
142
175
|
if (fs.existsSync(uiModules)) fs.rmSync(uiModules, { recursive: true, force: true });
|
|
143
176
|
console.log(chalk.dim(' Installing UI dependencies...'));
|
|
144
|
-
|
|
177
|
+
const installCmd = PM === 'npm' ? 'npm install --no-audit --no-fund'
|
|
178
|
+
: PM === 'pnpm' ? 'pnpm install --no-frozen-lockfile'
|
|
179
|
+
: PM === 'yarn' ? 'yarn install'
|
|
180
|
+
: 'bun install';
|
|
181
|
+
const buildCmd = `${PM} run build`;
|
|
182
|
+
execSync(installCmd, { cwd: uiDir, stdio: 'pipe' });
|
|
145
183
|
console.log(chalk.dim(' Compiling frontend...'));
|
|
146
|
-
execSync(
|
|
184
|
+
execSync(buildCmd, { cwd: uiDir, stdio: 'pipe' });
|
|
147
185
|
console.log(chalk.green(' ✓ UI built successfully'));
|
|
148
186
|
} catch (err) {
|
|
149
187
|
console.error(chalk.red(' ✗ UI build failed:'), err.message);
|
|
@@ -152,12 +190,34 @@ if (!collectOnly && !fs.existsSync(publicIndex) && fs.existsSync(uiDir)) {
|
|
|
152
190
|
console.log('');
|
|
153
191
|
}
|
|
154
192
|
|
|
155
|
-
if (!collectOnly && !fs.existsSync(publicIndex)) {
|
|
193
|
+
if (!collectOnly && !isUiDev && !fs.existsSync(publicIndex)) {
|
|
156
194
|
console.error(chalk.red(' ✗ No built UI found at public/index.html'));
|
|
157
|
-
console.error(chalk.dim(
|
|
195
|
+
console.error(chalk.dim(` Run: cd ui && ${PM} install && ${PM} run build`));
|
|
158
196
|
process.exit(1);
|
|
159
197
|
}
|
|
160
198
|
|
|
199
|
+
// ── Ensure better-sqlite3 native bindings exist ─────────────
|
|
200
|
+
try {
|
|
201
|
+
require('better-sqlite3');
|
|
202
|
+
} catch (e) {
|
|
203
|
+
if (e.message && e.message.includes('Could not locate the bindings file')) {
|
|
204
|
+
console.log(chalk.cyan(' ⟳ Rebuilding native SQLite module...'));
|
|
205
|
+
try {
|
|
206
|
+
const bsqlDir = path.dirname(require.resolve('better-sqlite3/package.json'));
|
|
207
|
+
execSync('npx --yes prebuild-install -r napi || npx --yes node-gyp rebuild --release', {
|
|
208
|
+
cwd: bsqlDir, stdio: 'pipe', timeout: 120000,
|
|
209
|
+
});
|
|
210
|
+
console.log(chalk.green(' ✓ Native module rebuilt'));
|
|
211
|
+
} catch (rebuildErr) {
|
|
212
|
+
console.error(chalk.red(' ✗ Failed to rebuild better-sqlite3:'), rebuildErr.message);
|
|
213
|
+
console.error(chalk.dim(' Try: npm install better-sqlite3 --build-from-source'));
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
throw e;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
161
221
|
const cache = require('./cache');
|
|
162
222
|
|
|
163
223
|
// Wipe cache if --no-cache flag is passed
|
|
@@ -365,12 +425,12 @@ const BOT_STYLES = [
|
|
|
365
425
|
}
|
|
366
426
|
|
|
367
427
|
app.listen(port, '0.0.0.0', () => {
|
|
428
|
+
if (isUiDev) return;
|
|
368
429
|
const url = `http://localhost:${port}`;
|
|
369
430
|
console.log(chalk.green(` ✓ Dashboard ready at ${chalk.bold.white(url)}`));
|
|
370
431
|
console.log('');
|
|
371
432
|
console.log(chalk.dim(' Press Ctrl+C to stop\n'));
|
|
372
433
|
|
|
373
|
-
// Auto-open browser
|
|
374
434
|
const open = require('open');
|
|
375
435
|
open(url).catch(() => {});
|
|
376
436
|
});
|