ai-lens 0.8.72 → 0.8.73
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 +1 -1
- package/CHANGELOG.md +3 -0
- package/client/capture.js +27 -1
- package/package.json +1 -1
package/.commithash
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
45d094e
|
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.73 — 2026-05-29
|
|
6
|
+
- fix: spooling an event now retries the atomic `rename` in `~/.ai-lens/pending/` a few times with a short backoff before giving up. On Windows, antivirus/Defender or the search indexer transiently locks the temp file and the rename failed with EPERM, silently dropping that event (observed in prod via the 0.8.70 error-code instrumentation). POSIX is unaffected (its rename is atomic and never hits this).
|
|
7
|
+
|
|
5
8
|
## 0.8.72 — 2026-05-29
|
|
6
9
|
- fix: `status` no longer reports hooks as "outdated" when they already capture reliably. A hook is now considered current if it's GUI-safe — either the per-machine launcher (`run.sh`/`run.cmd`, including the transitional `sh -c` wrapper) OR a `capture.js` command with an absolute node path baked in (e.g. `/opt/homebrew/bin/node`). Only PATH-dependent forms (bare `node`, `/usr/bin/env node`) — which break for GUI-launched Cursor/Claude on macOS — stay flagged outdated so `init` rewrites them.
|
|
7
10
|
- fix: `ai-lens status --report` now prints the full status to the screen just like plain `ai-lens status`, in addition to sending the report to the server. Previously it ran silently. The only difference from plain status is that it POSTs the report instead of writing the local `~/ai-lens-status.txt` file.
|
package/client/capture.js
CHANGED
|
@@ -1182,6 +1182,32 @@ export function normalizeEvent(event) {
|
|
|
1182
1182
|
// Queue + Sender Spawn
|
|
1183
1183
|
// =============================================================================
|
|
1184
1184
|
|
|
1185
|
+
// Windows (Defender / other AV, search indexer, file-locks) transiently fails an
|
|
1186
|
+
// atomic rename with EPERM/EACCES/EBUSY while another process briefly holds the
|
|
1187
|
+
// .tmp or destination handle. The rename is our spool-write step, so a transient
|
|
1188
|
+
// failure would drop the event (observed in prod: queue-write-failed EPERM on
|
|
1189
|
+
// rename in ~/.ai-lens/pending). Retry a few times with a short synchronous
|
|
1190
|
+
// backoff before giving up. POSIX rename is atomic and never hits these, so this
|
|
1191
|
+
// is a no-op there. Non-transient errors (e.g. ENOENT) are re-thrown immediately.
|
|
1192
|
+
const RENAME_RETRY_CODES = new Set(['EPERM', 'EACCES', 'EBUSY']);
|
|
1193
|
+
export function renameSyncWithRetry(from, to, { attempts = 5, baseDelayMs = 20, renameFn = renameSync } = {}) {
|
|
1194
|
+
for (let i = 1; ; i++) {
|
|
1195
|
+
try {
|
|
1196
|
+
renameFn(from, to);
|
|
1197
|
+
return;
|
|
1198
|
+
} catch (err) {
|
|
1199
|
+
if (i >= attempts || !RENAME_RETRY_CODES.has(err?.code)) throw err;
|
|
1200
|
+
// Synchronous backoff — capture.js runs in a one-shot hook context, no event loop to yield to.
|
|
1201
|
+
try {
|
|
1202
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, baseDelayMs * i);
|
|
1203
|
+
} catch {
|
|
1204
|
+
const end = Date.now() + baseDelayMs * i;
|
|
1205
|
+
while (Date.now() < end) { /* spin fallback if Atomics unavailable */ }
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1185
1211
|
export function writeToSpool(unified) {
|
|
1186
1212
|
// Shallow copy before redaction — do not mutate the caller's object
|
|
1187
1213
|
const toWrite = { ...unified };
|
|
@@ -1198,7 +1224,7 @@ export function writeToSpool(unified) {
|
|
|
1198
1224
|
const tmpPath = dstPath + '.tmp.' + process.pid;
|
|
1199
1225
|
writeFileSync(tmpPath, JSON.stringify(toWrite));
|
|
1200
1226
|
try {
|
|
1201
|
-
|
|
1227
|
+
renameSyncWithRetry(tmpPath, dstPath);
|
|
1202
1228
|
} catch (err) {
|
|
1203
1229
|
// Clean up orphaned tmp file (e.g. antivirus/file-lock on Windows)
|
|
1204
1230
|
try { unlinkSync(tmpPath); } catch {}
|