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 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
- 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`.
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(` npx agentlytics --join ${localIp}:${RELAY_PORT} --username <name>`));
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('\n ✗ Missing relay address. Usage: npx agentlytics --join <host:port> --username <name>\n'));
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
- execSync('npm install --no-audit --no-fund', { cwd: uiDir, stdio: 'pipe' });
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('npm run build', { cwd: uiDir, stdio: 'pipe' });
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(' Run: cd ui && npm install && npm run build'));
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
  });