ai-lens 0.8.69 → 0.8.70

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
- fe949b0
1
+ 0912a07
package/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  History of changes to the `ai-lens` CLI package on npm. New entries go on top. Format: `## X.Y.Z — YYYY-MM-DD`, followed by user-facing bullets.
4
4
 
5
+ ## 0.8.70 — 2026-05-28
6
+ - diag: `sender-spawn-failed`, `codex-watcher-spawn-failed` and `queue-write-failed` entries in the capture log now record the OS `error.code` (e.g. EMFILE, EACCES). `ai-lens status` surfaces a per-category code breakdown so these failures can be diagnosed centrally instead of guessing from a bare count. The raw error message (which may contain local paths) stays on your machine — only the short code travels in the status report.
7
+
5
8
  ## 0.8.69 — 2026-05-27
6
9
  - feat: per-machine launcher (`~/.ai-lens/client/run.sh` / `run.cmd`) now also accepts an optional script path as its first argument. With no args it still execs the sibling `capture.js` (default install), but `~/.ai-lens/client/run.sh path/to/some/capture.js` execs that script with the launcher's resolved node — letting bootstrap-style workflows (e.g. meta-cursor) route through the launcher for proper node resolution while keeping `capture.js` under git in the workspace repo.
7
10
  - feat: new `--install-launcher` flag for `init`. Forces launcher installation even when `--no-hooks` is set, so the meta-cursor setup skill can wire up `~/.ai-lens/client/run.sh` without touching the static hook templates in the workspace repo.
package/cli/status.js CHANGED
@@ -725,8 +725,13 @@ function checkCaptureLog() {
725
725
  return { ok: false, summary: `error reading log: ${err.message}`, detail: `Error: ${err.message}` };
726
726
  }
727
727
 
728
- // Count entries by category (reason for drops, msg for errors)
728
+ // Count entries by category (reason for drops, msg for errors). For error
729
+ // entries that carry an `error.code` (spawn/queue failures), also tally the
730
+ // code distribution per category so it reaches the server via client_reports
731
+ // — the count alone can't tell EMFILE from EACCES from ENOENT. The raw
732
+ // error.message strings stay local (may contain paths); only the code travels.
729
733
  const counts = {};
734
+ const codesByCategory = {};
730
735
  let lastTs = null;
731
736
  let hasErrors = false;
732
737
  for (const line of lines) {
@@ -734,24 +739,38 @@ function checkCaptureLog() {
734
739
  const entry = JSON.parse(line);
735
740
  const category = entry.reason || entry.msg || 'unknown';
736
741
  counts[category] = (counts[category] || 0) + 1;
742
+ if (entry.code) {
743
+ (codesByCategory[category] ??= {});
744
+ codesByCategory[category][entry.code] = (codesByCategory[category][entry.code] || 0) + 1;
745
+ }
737
746
  lastTs = entry.ts;
738
747
  if (entry.msg) hasErrors = true;
739
748
  } catch { /* non-JSON line */ }
740
749
  }
741
750
 
742
751
  const total = lines.length;
743
- const breakdown = Object.entries(counts).map(([r, n]) => `${r}: ${n}`).join(', ');
752
+ const breakdown = Object.entries(counts).map(([r, n]) => {
753
+ const codes = codesByCategory[r];
754
+ if (!codes) return `${r}: ${n}`;
755
+ const codeStr = Object.entries(codes).map(([c, cn]) => `${c}: ${cn}`).join(', ');
756
+ return `${r}: ${n} [${codeStr}]`;
757
+ }).join(', ');
744
758
 
745
759
  let summary = `${total} entries`;
746
760
  if (breakdown) summary += ` (${breakdown})`;
747
761
  if (lastTs) summary += `, last ${relativeTime(lastTs)}`;
748
762
 
763
+ // Include a compact code-distribution block in detail too — survives even if
764
+ // the "last 10 entries" happen to be benign project_filter lines.
765
+ const codeLines = Object.entries(codesByCategory)
766
+ .map(([cat, codes]) => ` ${cat}: ${Object.entries(codes).map(([c, n]) => `${c}=${n}`).join(', ')}`);
767
+ const codeBlock = codeLines.length ? `\n\nError codes by category:\n${codeLines.join('\n')}` : '';
749
768
  const last10 = lines.slice(-10);
750
769
 
751
770
  return {
752
771
  ok: !hasErrors,
753
772
  summary,
754
- detail: `Log: ${CAPTURE_LOG_PATH}\nTotal: ${total}\n\nLast 10 entries:\n${last10.join('\n')}`,
773
+ detail: `Log: ${CAPTURE_LOG_PATH}\nTotal: ${total}${codeBlock}\n\nLast 10 entries:\n${last10.join('\n')}`,
755
774
  };
756
775
  }
757
776
 
package/client/capture.js CHANGED
@@ -1263,7 +1263,7 @@ async function main() {
1263
1263
  writeToSpool(ev);
1264
1264
  if (ev === primary) primaryWritten = true;
1265
1265
  } catch (err) {
1266
- captureLog({ msg: 'queue-write-failed', error: err.message, type: ev.type, session_id: ev.session_id });
1266
+ captureLog({ msg: 'queue-write-failed', error: err.message, code: err.code, type: ev.type, session_id: ev.session_id });
1267
1267
  // If the primary failed, propagate the failure so the hook exits non-zero
1268
1268
  // (and dedup is NOT committed). If a per-call event failed, log and keep
1269
1269
  // going — losing one TokenUsage row is better than dropping the whole
@@ -1312,14 +1312,14 @@ async function main() {
1312
1312
  try {
1313
1313
  trySpawnSender();
1314
1314
  } catch (err) {
1315
- captureLog({ msg: 'sender-spawn-failed', error: err.message });
1315
+ captureLog({ msg: 'sender-spawn-failed', error: err.message, code: err.code });
1316
1316
  // event is queued — sender will be spawned on next capture
1317
1317
  }
1318
1318
 
1319
1319
  try {
1320
1320
  trySpawnCodexWatcher({ replayExisting: shouldReplayCodexHistory(primary) });
1321
1321
  } catch (err) {
1322
- captureLog({ msg: 'codex-watcher-spawn-failed', error: err.message });
1322
+ captureLog({ msg: 'codex-watcher-spawn-failed', error: err.message, code: err.code });
1323
1323
  }
1324
1324
  }
1325
1325
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-lens",
3
- "version": "0.8.69",
3
+ "version": "0.8.70",
4
4
  "type": "module",
5
5
  "description": "Centralized session analytics for AI coding tools",
6
6
  "bin": {