ai-lens 0.8.44 → 0.8.46

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
- 7043044
1
+ 8f21797
package/cli/hooks.js CHANGED
@@ -115,9 +115,16 @@ export function captureCommand(useTilde = false, rawPath = false, customPath = n
115
115
  ? '~/.ai-lens/client/capture.js'
116
116
  : CAPTURE_PATH.replace(/\\/g, '/');
117
117
  const pathPart = (customPath != null || rawPath) ? capturePath : shellEscape(capturePath);
118
- return nodePath === '/usr/bin/env node'
119
- ? `/usr/bin/env node ${pathPart}`
120
- : `${shellEscape(nodePath.replace(/\\/g, '/'))} ${pathPart}`;
118
+ if (nodePath === '/usr/bin/env node') return `/usr/bin/env node ${pathPart}`;
119
+ // Claude Code on Windows executes hooks via cmd.exe. A quoted executable path
120
+ // at the start of the line (e.g. "C:/Program Files/node.exe" args) is not
121
+ // recognised as a command by cmd.exe. Use bare `node` for Claude Code hooks
122
+ // (rawPath=true) when the path contains spaces; Cursor hooks use PowerShell
123
+ // where quoting works fine.
124
+ if (process.platform === 'win32' && rawPath && nodePath.includes(' ')) {
125
+ return `node ${pathPart}`;
126
+ }
127
+ return `${shellEscape(nodePath.replace(/\\/g, '/'))} ${pathPart}`;
121
128
  }
122
129
 
123
130
  // Cursor on Windows executes hooks via PowerShell, which treats a quoted path like
package/cli/status.js CHANGED
@@ -323,11 +323,12 @@ function checkSystem() {
323
323
  const arch = osArch() || 'unknown';
324
324
  const nodeVersion = process.version || 'unknown';
325
325
  const platform = process.platform === 'darwin' ? 'macOS' : process.platform;
326
+ const cwd = process.cwd();
326
327
  const summary = `${platform} ${osVersion} ${arch}, Node ${nodeVersion}`;
327
328
  return {
328
329
  ok: true,
329
330
  summary,
330
- detail: `Platform: ${platform}\nOS version: ${osVersion}\nArchitecture: ${arch}\nNode: ${nodeVersion}`,
331
+ detail: `Platform: ${platform}\nOS version: ${osVersion}\nArchitecture: ${arch}\nNode: ${nodeVersion}\nCWD: ${cwd}`,
331
332
  };
332
333
  }
333
334
 
package/client/redact.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Secret redaction utility (client-side copy).
3
+ * ⚠️ KEEP IN SYNC with server/utils/redact.js — see test/server/redact.test.js sync test.
3
4
  * Replaces tokens, passwords, API keys and other secrets with [REDACTED:TYPE] tags.
4
5
  * Standalone — no imports, safe to run on developer machines.
5
6
  */
@@ -15,15 +16,63 @@ const PATTERNS = [
15
16
  // GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_)
16
17
  { type: 'GITHUB_TOKEN', re: /gh[pousr]_[A-Za-z0-9_]{36,}/g },
17
18
 
19
+ // GitLab tokens (glpat-, gldt-, glrt-, glcbt-, glsoat-)
20
+ {
21
+ type: 'GITLAB_TOKEN',
22
+ re: /gl(?:pat|dt|rt|cbt|soat)-[A-Za-z0-9_-]{20,}/g,
23
+ replacer: (m) => {
24
+ const prefix = m.slice(0, m.indexOf('-'));
25
+ const typeMap = { glpat: 'GITLAB_PAT', gldt: 'GITLAB_DEPLOY_TOKEN', glrt: 'GITLAB_RUNNER_TOKEN', glcbt: 'GITLAB_CLUSTER_TOKEN', glsoat: 'GITLAB_OAUTH_TOKEN' };
26
+ return `[REDACTED:${typeMap[prefix] || 'GITLAB_TOKEN'}]`;
27
+ },
28
+ },
29
+
18
30
  // Anthropic API key
19
31
  { type: 'ANTHROPIC_KEY', re: /sk-ant-[A-Za-z0-9_-]{20,}/g },
20
32
 
21
33
  // OpenAI project key
22
34
  { type: 'OPENAI_KEY', re: /sk-proj-[A-Za-z0-9_-]{20,}/g },
23
35
 
36
+ // Google API key (AIzaSy...)
37
+ { type: 'GOOGLE_API_KEY', re: /AIza[A-Za-z0-9_-]{35}/g },
38
+
39
+ // Google OAuth access token (ya29....)
40
+ { type: 'GOOGLE_OAUTH', re: /ya29\.[A-Za-z0-9_-]{50,}/g },
41
+
42
+ // GCP service account email (project-id@project-id.iam.gserviceaccount.com)
43
+ { type: 'GCP_SERVICE_ACCOUNT', re: /[a-z0-9-]+@[a-z0-9-]+\.iam\.gserviceaccount\.com/g },
44
+
45
+ // x-api-key header value
46
+ {
47
+ type: 'API_KEY_HEADER',
48
+ re: /(["']?x-api-key["']?\s*[:=]\s*["']?)[^\s"',)}\]]+/gi,
49
+ replacer: (m, prefix) => `${prefix}[REDACTED:API_KEY_HEADER]`,
50
+ },
51
+
24
52
  // Generic sk- key (must come after sk-ant- and sk-proj-)
25
53
  { type: 'API_KEY', re: /sk-[A-Za-z0-9_-]{32,}/g },
26
54
 
55
+ // Stripe secret key (live only — test keys intentionally not redacted)
56
+ { type: 'STRIPE_SECRET', re: /sk_live_[A-Za-z0-9]{24,}/g },
57
+
58
+ // Stripe restricted key
59
+ { type: 'STRIPE_RESTRICTED', re: /rk_live_[A-Za-z0-9]{24,}/g },
60
+
61
+ // npm token
62
+ { type: 'NPM_TOKEN', re: /npm_[A-Za-z0-9]{36,}/g },
63
+
64
+ // PyPI token
65
+ { type: 'PYPI_TOKEN', re: /pypi-[A-Za-z0-9_-]{16,}/g },
66
+
67
+ // SendGrid API key (SG.{22}.{43})
68
+ { type: 'SENDGRID_KEY', re: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g },
69
+
70
+ // Twilio API key (SK + 32 lowercase hex chars)
71
+ { type: 'TWILIO_KEY', re: /SK[a-f0-9]{32}/g },
72
+
73
+ // Telegram bot token (8-10 digit bot ID : 35-char secret)
74
+ { type: 'TELEGRAM_BOT_TOKEN', re: /\b\d{8,10}:[A-Za-z0-9_-]{35}\b/g },
75
+
27
76
  // Slack tokens (xox{b,p,a,o,s}-)
28
77
  { type: 'SLACK_TOKEN', re: /xo[xabposr]{1,2}-[A-Za-z0-9-]{10,}/g },
29
78
 
@@ -36,22 +85,46 @@ const PATTERNS = [
36
85
  // PEM private keys — full block or truncated (just the header + base64 content)
37
86
  { type: 'PRIVATE_KEY', re: /-----BEGIN[A-Z ]*PRIVATE KEY-----[A-Za-z0-9+/=\s\\.]*(?:-----END[A-Z ]*PRIVATE KEY-----)?/g },
38
87
 
88
+ // Meilisearch master/API key (mc_ + 32+ hex chars)
89
+ { type: 'MEILISEARCH_KEY', re: /\bmc_[0-9a-f]{32,}\b/g },
90
+
39
91
  // Connection string password (://user:password@host) — redacts password only
40
92
  { type: 'CONNECTION_STRING', re: /:\/\/([^:@\s]+):([^@\s]{3,})@/g, replacer: (m, user, _pw) => `://${user}:[REDACTED:CONNECTION_STRING]@` },
41
93
 
42
- // Environment variables: UPPER_CASE_VAR with secret keyword = value
43
- // Catches AI_LENS_AUTH_TOKEN=..., PGPASSWORD=..., AWS_SECRET_ACCESS_KEY=..., etc.
94
+ // MySQL/mysqldump CLI -p<password> syntax (mysql -u user -pSECRET dbname)
95
+ { type: 'MYSQL_PASSWORD', re: /(\bmysql\w*\b[^\n]*\s-p)(\S{8,})/g, replacer: (m, prefix, _pw) => `${prefix}[REDACTED:MYSQL_PASSWORD]` },
96
+
97
+ // Environment variables with PASSWORD/PASSWD keyword — lower 6-char minimum
98
+ // Catches BASIC_AUTH_PASSWORD=236716, db_password=secret, etc.
99
+ {
100
+ type: 'ENV_VAR',
101
+ re: /([A-Za-z_]{0,50}(?:PASSWORD|PASSWD)[A-Za-z_]{0,50}\s*=\s*["']?)([^\s"';\\\`|>{}\[\]]{6,})/gi,
102
+ replacer: (m, prefix, _val) => `${prefix}[REDACTED:ENV_VAR]`,
103
+ },
104
+
105
+ // Environment variables with other secret keywords — standard 8-char minimum
106
+ // Catches aws_secret_access_key=..., AI_LENS_AUTH_TOKEN=..., RUSTAT_KEY=..., etc.
107
+ // Case-insensitive to catch lowercase config files. _KEY added for API key vars.
44
108
  // Skips template refs like ${VAR} since value excludes { } chars.
45
109
  {
46
110
  type: 'ENV_VAR',
47
- re: /([A-Z_]*(?:SECRET|TOKEN|PASSWORD|PASSWD|API_KEY|APIKEY)[A-Z_]*\s*=\s*["']?)([^\s"';\\\`|>{}\[\]]{8,})/g,
111
+ re: /([A-Za-z_]{0,50}(?:SECRET|TOKEN|API_KEY|APIKEY|_KEY)[A-Za-z_]{0,50}\s*=\s*["']?)([^\s"';\\\`|>{}\[\]]{8,})/gi,
48
112
  replacer: (m, prefix, _val) => `${prefix}[REDACTED:ENV_VAR]`,
49
113
  },
50
114
 
51
115
  // Key-value pairs: password=..., token: ..., etc.
116
+ // Uses \w* prefix/suffix to match compound names like aws_secret_access_key
117
+ {
118
+ type: 'KEY_VALUE',
119
+ re: /(\b\w{0,30}(?:password|passwd|token|secret|api_key|apikey|api_secret|access_token|auth_token|private_key|client_secret|credential|authorization|session_key|encryption_key|signing_key|refresh_token|id_token)\w{0,30}["']?\s*[:=]\s*["']?)([^\s"',;\[\]{}]{8,})/gi,
120
+ replacer: (m, prefix, _val) => `${prefix}[REDACTED:KEY_VALUE]`,
121
+ },
122
+
123
+ // Russian key-value pairs: пароль=..., секрет: ..., токен: ..., ключ: ...
124
+ // Separate pattern because \b does not work with Cyrillic (\w is ASCII-only)
52
125
  {
53
126
  type: 'KEY_VALUE',
54
- re: /(\b["']?(?:password|passwd|token|secret|api_key|apikey|api_secret|access_token|auth_token|private_key|client_secret)["']?\s*[:=]\s*["']?)([^\s"',;\[\]{}]{8,})/gi,
127
+ re: /((?:^|[\s"',:={}([\-])(?:пароль|парол[а-я]*|секрет[а-я]*|токен[а-я]*|ключ[а-я]*)["']?\s*[:=\-—–]?\s*["']?)([^\s"',;\[\]{}]{6,})/gi,
55
128
  replacer: (m, prefix, _val) => `${prefix}[REDACTED:KEY_VALUE]`,
56
129
  },
57
130
  ];
package/client/sender.js CHANGED
@@ -60,7 +60,7 @@ export function rotateLog(logPath = LOG_PATH, maxAgeDays = LOG_MAX_AGE_DAYS) {
60
60
  }
61
61
 
62
62
  export const MAX_QUEUE_SIZE = 10_000;
63
- export const MAX_CHUNK_BYTES = 4 * 1024 * 1024; // 4 MB per POST (Express limit is 50 MB)
63
+ export const MAX_CHUNK_BYTES = 50 * 1024; // 50 KB per POST small chunks avoid ECONNRESET on corporate proxies/TLS inspection
64
64
  export const LOCK_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
65
65
 
66
66
  // =============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.44",
3
+ "version": "0.8.46",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {