ai-lens 0.8.28 → 0.8.30

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/.commithash CHANGED
@@ -1 +1 @@
1
- 2061356
1
+ 6a194f8
package/cli/hooks.js CHANGED
@@ -142,6 +142,13 @@ export function installClientFiles() {
142
142
  join(CLIENT_INSTALL_DIR, 'package.json'),
143
143
  '{"type":"module"}\n',
144
144
  );
145
+
146
+ // Write version.json so installed capture.js can identify itself
147
+ const { version, commit } = getVersionInfo();
148
+ writeFileSync(
149
+ join(CLIENT_INSTALL_DIR, 'version.json'),
150
+ JSON.stringify({ version, commit }) + '\n',
151
+ );
145
152
  }
146
153
 
147
154
  /**
@@ -212,15 +219,15 @@ export const TOOL_CONFIGS = [
212
219
  // ---------------------------------------------------------------------------
213
220
 
214
221
  export function isAiLensHook(entry) {
215
- // Normalize to forward slashes for cross-platform path matching: a hook installed on
216
- // Windows with backslash paths must still be detected when CAPTURE_PATH uses forward slashes.
217
- const normalizedCapture = CAPTURE_PATH.replace(/\\/g, '/');
222
+ // Match by canonical suffix so we also catch Windows 8.3 short-name paths
223
+ // (e.g. C:/Users/08A4~1/.ai-lens/...) that differ from homedir().
224
+ const SUFFIX = '.ai-lens/client/capture.js';
225
+ const matches = cmd => cmd.replace(/\\/g, '/').includes(SUFFIX);
218
226
  // Flat format (Cursor): { command: "..." }
219
- const cmd = (entry?.command || '').replace(/\\/g, '/');
220
- if (cmd.includes(normalizedCapture)) return true;
227
+ if (matches(entry?.command || '')) return true;
221
228
  // Nested format (Claude Code): { matcher, hooks: [{ command: "..." }] }
222
229
  if (Array.isArray(entry?.hooks)) {
223
- return entry.hooks.some(h => (h?.command || '').replace(/\\/g, '/').includes(normalizedCapture));
230
+ return entry.hooks.some(h => matches(h?.command || ''));
224
231
  }
225
232
  return false;
226
233
  }
package/cli/status.js CHANGED
@@ -313,11 +313,29 @@ function checkClientFiles() {
313
313
  const results = files.map(f => ({ name: f, exists: existsSync(join(dir, f)) }));
314
314
  const found = results.filter(r => r.exists).length;
315
315
  const ok = found === files.length;
316
- const detail = results.map(r => ` ${r.exists ? '\u2713' : '\u2717'} ${r.name}`).join('\n');
316
+ const fileDetail = results.map(r => ` ${r.exists ? '\u2713' : '\u2717'} ${r.name}`).join('\n');
317
+
318
+ const { version: cliVersion, commit: cliCommit } = getVersionInfo();
319
+ let versionDetail = '';
320
+ let outdated = false;
321
+ try {
322
+ const versionJson = JSON.parse(readFileSync(join(dir, 'version.json'), 'utf-8'));
323
+ const clientVersion = versionJson.version || 'unknown';
324
+ const clientCommit = versionJson.commit || 'unknown';
325
+ outdated = clientVersion !== cliVersion || clientCommit !== cliCommit;
326
+ versionDetail = `\n Client version: ${clientVersion} (${clientCommit})`;
327
+ if (outdated) {
328
+ versionDetail += `\n CLI version: ${cliVersion} (${cliCommit})`;
329
+ versionDetail += `\n ! Client is outdated — run: npx -y ai-lens init`;
330
+ }
331
+ } catch {
332
+ versionDetail = `\n Client version: unknown (version.json not found — run: npx -y ai-lens init)`;
333
+ }
334
+
317
335
  return {
318
- ok,
336
+ ok: ok && !outdated,
319
337
  summary: ok ? `installed (${found}/${files.length})` : `missing (${found}/${files.length})`,
320
- detail: `Client files in ${dir}:\n${detail}`,
338
+ detail: `Client files in ${dir}:\n${fileDetail}${versionDetail}`,
321
339
  };
322
340
  }
323
341
 
package/client/config.js CHANGED
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
4
4
  import { execSync } from 'node:child_process';
5
5
 
6
6
  export const DATA_DIR = process.env.AI_LENS_DATA_DIR || join(homedir(), '.ai-lens');
7
+ const CLIENT_INSTALL_DIR = join(DATA_DIR, 'client');
7
8
  export const CONFIG_PATH = join(DATA_DIR, 'config.json');
8
9
  // Spool directories (v1 storage format)
9
10
  export const PENDING_DIR = join(DATA_DIR, 'pending');
@@ -91,6 +92,14 @@ export function getAuthToken() {
91
92
  return process.env.AI_LENS_AUTH_TOKEN || loadConfig().authToken || null;
92
93
  }
93
94
 
95
+ export function getClientVersion() {
96
+ try {
97
+ return JSON.parse(readFileSync(join(CLIENT_INSTALL_DIR, 'version.json'), 'utf-8'));
98
+ } catch {
99
+ return { version: 'unknown', commit: 'unknown' };
100
+ }
101
+ }
102
+
94
103
  export function getGitIdentity(cwd) {
95
104
  let email = null;
96
105
  let name = null;
package/client/sender.js CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  SENDING_PATH,
30
30
  getServerUrl,
31
31
  getAuthToken,
32
+ getClientVersion,
32
33
  DEFAULT_SERVER_URL,
33
34
  log,
34
35
  } from './config.js';
@@ -557,9 +558,11 @@ function postEvents(serverUrl, events, identity, authToken) {
557
558
  const isHttps = url.protocol === 'https:';
558
559
  const requestFn = isHttps ? httpsRequest : httpRequest;
559
560
 
561
+ const { version: clientVersion, commit: clientCommit } = getClientVersion();
560
562
  const headers = {
561
- 'Content-Type': 'application/json',
562
- 'Content-Length': Buffer.byteLength(body),
563
+ 'Content-Type': 'application/json',
564
+ 'Content-Length': Buffer.byteLength(body),
565
+ 'X-Client-Version': `${clientVersion}+${clientCommit}`,
563
566
  };
564
567
  if (identity.email) headers['X-Developer-Git-Email'] = identity.email;
565
568
  if (identity.name) headers['X-Developer-Name'] = encodeURIComponent(identity.name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.28",
3
+ "version": "0.8.30",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {